diff options
304 files changed, 9391 insertions, 3510 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 2e0c95a5fe..95f5c03a37 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -297,12 +297,23 @@ on late-init write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size" # allow creating event triggers - chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger + chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger + + # allow enabling rss_stat_throttled + chmod 0666 /sys/kernel/tracing/events/synthetic/rss_stat_throttled/enable + chmod 0666 /sys/kernel/debug/tracing/events/synthetic/rss_stat_throttled/enable on late-init && property:ro.boot.fastboot.boottrace=enabled setprop debug.atrace.tags.enableflags 802922 setprop persist.traced.enable 0 + write /sys/kernel/tracing/events/binder/binder_transaction/enable 1 + write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 1 + write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 1 + write /sys/kernel/tracing/events/binder/binder_set_priority/enable 1 + write /sys/kernel/tracing/events/binder/binder_lock/enable 1 + write /sys/kernel/tracing/events/binder/binder_locked/enable 1 + write /sys/kernel/tracing/events/binder/binder_unlock/enable 1 write /sys/kernel/debug/tracing/tracing_on 1 write /sys/kernel/tracing/tracing_on 1 @@ -394,6 +405,103 @@ on post-fs-data && property:persist.mm_events.enabled=true chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace +# Handle hyp tracing instance +on late-init && property:ro.boot.hypervisor.vm.supported=1 + +# Hypervisor tracing instance doesn't support changing trace_clock + chmod 0440 /sys/kernel/debug/tracing/hyp/trace_clock + chmod 0440 /sys/kernel/tracing/hyp/trace_clock + + chmod 0660 /sys/kernel/debug/tracing/hyp/buffer_size_kb + chmod 0660 /sys/kernel/tracing/hyp/buffer_size_kb + + chmod 0660 /sys/kernel/debug/tracing/hyp/tracing_on + chmod 0660 /sys/kernel/tracing/hyp/tracing_on + +# Tracing disabled by default + write /sys/kernel/debug/tracing/hyp/tracing_on 0 + write /sys/kernel/tracing/hyp/tracing_on 0 + +# Read and truncate the hyp trace. + chmod 0660 /sys/kernel/debug/tracing/hyp/trace + chmod 0660 /sys/kernel/tracing/hyp/trace + +# Read and truncate the per-CPU kernel trace. +# Cannot use wildcards in .rc files. Update this if there is a phone with +# TODO(b/249050813, ioffe): introduce per-cpu wildcard + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu0/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu0/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu1/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu1/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu2/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu2/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu3/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu3/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu4/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu4/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu5/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu5/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu6/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu6/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu7/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu7/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu8/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu8/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu9/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu9/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu10/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu10/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu11/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu11/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu12/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu12/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu13/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu13/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu14/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu14/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu15/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu15/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu16/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu16/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu17/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu17/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu18/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu18/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu19/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu19/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu20/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu20/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu21/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu21/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu22/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu22/trace + chmod 0660 /sys/kernel/debug/tracing/hyp/per_cpu/cpu23/trace + chmod 0660 /sys/kernel/tracing/hyp/per_cpu/cpu23/trace + + chmod 0440 /sys/kernel/debug/tracing/hyp/events/header_page + chmod 0440 /sys/kernel/tracing/hyp/events/header_page + +# Hyp events start here + +# hyp_enter event + chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/enable + chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/enable +# TODO(b/249050813): should this be handled in kernel? + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/format + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/format + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_enter/id + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_enter/id + +# hyp_exit event + chmod 0660 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/enable + chmod 0660 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/enable +# TODO(b/249050813): should this be handled in kernel? + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/format + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/format + chmod 0440 /sys/kernel/debug/tracing/hyp/events/hyp/hyp_exit/id + chmod 0440 /sys/kernel/tracing/hyp/events/hyp/hyp_exit/id + + on property:persist.debug.atrace.boottrace=1 start boottrace @@ -405,6 +513,13 @@ service boottrace /system/bin/atrace --async_start -f /data/misc/boottrace/categ 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/tracing/events/binder/binder_transaction/enable 0 + write /sys/kernel/tracing/events/binder/binder_transaction_received/enable 0 + write /sys/kernel/tracing/events/binder/binder_transaction_alloc_buf/enable 0 + write /sys/kernel/tracing/events/binder/binder_set_priority/enable 0 + write /sys/kernel/tracing/events/binder/binder_lock/enable 0 + write /sys/kernel/tracing/events/binder/binder_locked/enable 0 + write /sys/kernel/tracing/events/binder/binder_unlock/enable 0 write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 diff --git a/cmds/dumpstate/OWNERS b/cmds/dumpstate/OWNERS index 5f56531754..ab81ecf4f4 100644 --- a/cmds/dumpstate/OWNERS +++ b/cmds/dumpstate/OWNERS @@ -3,3 +3,4 @@ set noparent gavincorkery@google.com nandana@google.com jsharkey@android.com +smoreland@google.com
\ No newline at end of file diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index a48313a6fa..23cdd10a25 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -2062,6 +2062,8 @@ static void DumpstateTelephonyOnly(const std::string& calling_package) { SEC_TO_MSEC(10)); RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"telecom"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); if (include_sensitive_info) { // Contains raw IP addresses, omit from reports on user builds. RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 34ea7597b4..794750ff81 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -442,6 +442,16 @@ static unique_fd open_current_profile(uid_t uid, userid_t user, const std::strin static unique_fd open_reference_profile(uid_t uid, const std::string& package_name, const std::string& location, bool read_write, bool is_secondary_dex) { std::string profile = create_reference_profile_path(package_name, location, is_secondary_dex); + if (read_write && GetBoolProperty("dalvik.vm.useartservice", false)) { + // ART Service doesn't use flock and instead assumes profile files are + // immutable, so ensure we don't open a file for writing when it's + // active. + // TODO(b/251921228): Normally installd isn't called at all in that + // case, but OTA is still an exception that uses the legacy code. + LOG(ERROR) << "Opening ref profile " << profile + << " for writing is unsafe when ART Service is enabled."; + return invalid_unique_fd(); + } return open_profile( uid, profile, @@ -450,14 +460,13 @@ static unique_fd open_reference_profile(uid_t uid, const std::string& package_na } static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name, - const std::string& location, bool read_write, bool is_secondary_dex) { + const std::string& location, + bool is_secondary_dex) { std::string profile_path = create_reference_profile_path(package_name, location, is_secondary_dex); - unique_fd ufd = open_profile( - uid, - profile_path, - read_write ? (O_CREAT | O_RDWR) : O_RDONLY, - S_IRUSR | S_IWUSR | S_IRGRP); // so that ART can also read it when apps run. + unique_fd ufd = open_profile(uid, profile_path, O_RDONLY, + S_IRUSR | S_IWUSR | + S_IRGRP); // so that ART can also read it when apps run. return UniqueFile(ufd.release(), profile_path, [](const std::string& path) { clear_profile(path); @@ -1104,8 +1113,7 @@ UniqueFile maybe_open_reference_profile(const std::string& pkgname, location = profile_name; } } - return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false, - is_secondary_dex); + return open_reference_profile_as_unique_file(uid, pkgname, location, is_secondary_dex); } // Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to @@ -1909,10 +1917,11 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins // Open the reference profile if needed. UniqueFile reference_profile = maybe_open_reference_profile( pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex); - - if (reference_profile.fd() == -1) { - // We don't create an app image without reference profile since there is no speedup from - // loading it in that case and instead will be a small overhead. + struct stat sbuf; + if (reference_profile.fd() == -1 || + (fstat(reference_profile.fd(), &sbuf) != -1 && sbuf.st_size == 0)) { + // We don't create an app image with empty or non existing reference profile since there + // is no speedup from loading it in that case and instead will be a small overhead. generate_app_image = false; } diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 6a3120c1b7..bf2c0d1023 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -308,7 +308,7 @@ private: // This is different from the normal installd. We only do the base // directory, the rest will be created on demand when each app is compiled. if (access(GetOtaDirectoryPrefix().c_str(), R_OK) < 0) { - LOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); + PLOG(ERROR) << "Could not access " << GetOtaDirectoryPrefix(); return false; } @@ -460,7 +460,7 @@ private: // this tool will wipe the OTA artifact cache and try again (for robustness after // a failed OTA with remaining cache artifacts). if (access(apk_path, F_OK) != 0) { - LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path; + PLOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path; return true; } diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c62734a925..1b7acabf70 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -45,6 +45,10 @@ using android::base::StringPrintf; namespace android { namespace installd { +// We don't know the filesystem types of the partitions in the update package, +// so just try the possibilities one by one. +static constexpr std::array kTryMountFsTypes = {"ext4", "erofs"}; + static void CloseDescriptor(int fd) { if (fd >= 0) { int result = close(fd); @@ -82,6 +86,27 @@ static void DeactivateApexPackages() { } } +static bool TryMountWithFstypes(const char* block_device, const char* target) { + for (int i = 0; i < kTryMountFsTypes.size(); ++i) { + const char* fstype = kTryMountFsTypes[i]; + int mount_result = mount(block_device, target, fstype, MS_RDONLY, /* data */ nullptr); + if (mount_result == 0) { + return true; + } + if (errno == EINVAL && i < kTryMountFsTypes.size() - 1) { + // Only try the next fstype if mounting failed due to the current one + // being invalid. + LOG(WARNING) << "Failed to mount " << block_device << " on " << target << " with " + << fstype << " - trying " << kTryMountFsTypes[i + 1]; + } else { + PLOG(ERROR) << "Failed to mount " << block_device << " on " << target << " with " + << fstype; + return false; + } + } + __builtin_unreachable(); +} + static void TryExtraMount(const char* name, const char* slot, const char* target) { std::string partition_name = StringPrintf("%s%s", name, slot); @@ -91,12 +116,7 @@ static void TryExtraMount(const char* name, const char* slot, const char* target if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) { std::string path; if (dm.GetDmDevicePathByName(partition_name, &path)) { - int mount_result = mount(path.c_str(), - target, - "ext4", - MS_RDONLY, - /* data */ nullptr); - if (mount_result == 0) { + if (TryMountWithFstypes(path.c_str(), target)) { return; } } @@ -105,12 +125,7 @@ static void TryExtraMount(const char* name, const char* slot, const char* target // Fall back and attempt a direct mount. std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str()); - int mount_result = mount(block_device.c_str(), - target, - "ext4", - MS_RDONLY, - /* data */ nullptr); - UNUSED(mount_result); + (void)TryMountWithFstypes(block_device.c_str(), target); } // Entry for otapreopt_chroot. Expected parameters are: diff --git a/cmds/installd/otapreopt_script.sh b/cmds/installd/otapreopt_script.sh index f950276090..db5c34edc2 100644 --- a/cmds/installd/otapreopt_script.sh +++ b/cmds/installd/otapreopt_script.sh @@ -60,6 +60,11 @@ print -u${STATUS_FD} "global_progress $PROGRESS" i=0 while ((i<MAXIMUM_PACKAGES)) ; do + DONE=$(cmd otadexopt done) + if [ "$DONE" = "OTA complete." ] ; then + break + fi + DEXOPT_PARAMS=$(cmd otadexopt next) /system/bin/otapreopt_chroot $STATUS_FD $TARGET_SLOT_SUFFIX $DEXOPT_PARAMS >&- 2>&- @@ -67,13 +72,8 @@ while ((i<MAXIMUM_PACKAGES)) ; do PROGRESS=$(cmd otadexopt progress) print -u${STATUS_FD} "global_progress $PROGRESS" - DONE=$(cmd otadexopt done) - if [ "$DONE" = "OTA incomplete." ] ; then - sleep 1 - i=$((i+1)) - continue - fi - break + sleep 1 + i=$((i+1)) done DONE=$(cmd otadexopt done) diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 3b589dc581..5c4e1a45fc 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -185,7 +185,7 @@ protected: std::optional<std::string> volume_uuid_; std::string package_name_; std::string apk_path_; - std::string empty_dm_file_; + std::string dm_file_; std::string app_apk_dir_; std::string app_private_dir_ce_; std::string app_private_dir_de_; @@ -248,26 +248,6 @@ protected: << " : " << error_msg; } - // Create an empty dm file. - empty_dm_file_ = apk_path_ + ".dm"; - { - int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); - if (fd < 0) { - return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_; - } - FILE* file = fdopen(fd, "wb"); - if (file == nullptr) { - return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_ - << " fd=" << fd; - } - ZipWriter writer(file); - // Add vdex to zip. - writer.StartEntry("primary.prof", ZipWriter::kCompress); - writer.FinishEntry(); - writer.Finish(); - fclose(file); - } - // Create the app user data. binder::Status status = service_->createAppData( volume_uuid_, @@ -316,6 +296,46 @@ protected: << secondary_dex_de_ << " : " << error_msg; } + // Create a non-empty dm file. + dm_file_ = apk_path_ + ".dm"; + { + android::base::unique_fd fd(open(dm_file_.c_str(), + O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR)); + if (fd.get() < 0) { + return ::testing::AssertionFailure() << "Could not open " << dm_file_; + } + FILE* file = fdopen(fd.release(), "wb"); + if (file == nullptr) { + return ::testing::AssertionFailure() << "Null file for " << dm_file_ + << " fd=" << fd.get(); + } + + // Create a profile file. + std::string profile_file = app_private_dir_ce_ + "/primary.prof"; + run_cmd("profman --generate-test-profile=" + profile_file); + + // Add profile to zip. + ZipWriter writer(file); + writer.StartEntry("primary.prof", ZipWriter::kCompress); + android::base::unique_fd profile_fd(open(profile_file.c_str(), O_RDONLY)); + if (profile_fd.get() < 0) { + return ::testing::AssertionFailure() << "Failed to open profile '" + << profile_file << "'"; + } + std::string profile_content; + if (!android::base::ReadFdToString(profile_fd, &profile_content)) { + return ::testing::AssertionFailure() << "Failed to read profile " + << profile_file << "'"; + } + writer.WriteBytes(profile_content.c_str(), profile_content.length()); + writer.FinishEntry(); + writer.Finish(); + fclose(file); + + // Delete the temp file. + unlink(profile_file.c_str()); + } + // Fix app data uid. status = service_->fixupAppData(volume_uuid_, kTestUserId); if (!status.isOk()) { @@ -608,7 +628,7 @@ protected: kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); int64_t odex_size = GetSize(GetPrimaryDexArtifact(oat_dir, apk_path_, @@ -657,13 +677,13 @@ protected: DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_PUBLIC | DEXOPT_GENERATE_APP_IMAGE, oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH, - /*binder_result=*/nullptr, empty_dm_file_.c_str()); + /*binder_result=*/nullptr, dm_file_.c_str()); checkVisibility(in_dalvik_cache, ODEX_IS_PUBLIC); CompilePrimaryDexOk("speed-profile", DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE, oat_dir, kTestAppGid, DEX2OAT_FROM_SCRATCH, - /*binder_result=*/nullptr, empty_dm_file_.c_str()); + /*binder_result=*/nullptr, dm_file_.c_str()); checkVisibility(in_dalvik_cache, ODEX_IS_PRIVATE); } }; @@ -787,7 +807,7 @@ TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); } TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { @@ -799,7 +819,7 @@ TEST_F(DexoptTest, DexoptPrimaryProfilePublic) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); } TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { @@ -811,7 +831,7 @@ TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); } TEST_F(DexoptTest, DexoptBlockPrimary) { @@ -874,7 +894,7 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { @@ -893,7 +913,7 @@ TEST_F(DexoptTest, ResolveStartupConstStrings) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); run_cmd_and_process_output( "oatdump --header-only --oat-file=" + odex, [&](const std::string& line) { @@ -926,7 +946,7 @@ TEST_F(DexoptTest, DexoptDex2oat64Enabled) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); // Enable the property and use dex2oat64. ASSERT_TRUE(android::base::SetProperty(property, "true")) << property; CompilePrimaryDexOk("speed-profile", @@ -936,7 +956,7 @@ TEST_F(DexoptTest, DexoptDex2oat64Enabled) { kTestAppGid, DEX2OAT_FROM_SCRATCH, /*binder_result=*/nullptr, - empty_dm_file_.c_str()); + dm_file_.c_str()); } class PrimaryDexReCompilationTest : public DexoptTest { @@ -1143,7 +1163,7 @@ class ProfileTest : public DexoptTest { service_->prepareAppProfile(package_name, has_user_id ? kTestUserId : USER_NULL, kTestAppId, profile_name, apk_path_, has_dex_metadata ? std::make_optional<std::string>( - empty_dm_file_) + dm_file_) : std::nullopt, &result)); ASSERT_EQ(expected_result, result); diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index d5ca725eb9..5e8ef5d7d8 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -75,7 +75,7 @@ int main(int argc, char* const argv[]) ProcessState::initWithDriver("/dev/vndbinder"); #endif #ifndef __ANDROID__ - setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1})); + setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1})); #endif sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 07809e21f4..4da0cd6897 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -228,8 +228,13 @@ static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::s #endif // !VENDORSERVICEMANAGER ServiceManager::Service::~Service() { - if (!hasClients) { - // only expected to happen on process death + if (hasClients) { + // only expected to happen on process death, we don't store the service + // name this late (it's in the map that holds this service), but if it + // is happening, we might want to change 'unlinkToDeath' to explicitly + // clear this bit so that we can abort in other cases, where it would + // mean inconsistent logic in servicemanager (unexpected and tested, but + // the original lazy service impl here had that bug). LOG(WARNING) << "a service was removed when there are clients"; } } diff --git a/data/etc/Android.bp b/data/etc/Android.bp index a737bd3fb7..bdd5172e60 100644 --- a/data/etc/Android.bp +++ b/data/etc/Android.bp @@ -167,12 +167,6 @@ prebuilt_etc { } prebuilt_etc { - name: "android.hardware.telephony.satellite.prebuilt.xml", - src: "android.hardware.telephony.satellite.xml", - defaults: ["frameworks_native_data_etc_defaults"], -} - -prebuilt_etc { name: "android.hardware.usb.accessory.prebuilt.xml", src: "android.hardware.usb.accessory.xml", defaults: ["frameworks_native_data_etc_defaults"], diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml deleted file mode 100644 index 5966cba277..0000000000 --- a/data/etc/android.hardware.telephony.satellite.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?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. ---> - -<!-- Feature for devices that support Satellite communication via Satellite HAL APIs. --> -<permissions> - <feature name="android.hardware.telephony.satellite" /> -</permissions> diff --git a/headers/media_plugin/media/openmax/OMX_AsString.h b/headers/media_plugin/media/openmax/OMX_AsString.h index ce30b417e0..165a868d57 100644 --- a/headers/media_plugin/media/openmax/OMX_AsString.h +++ b/headers/media_plugin/media/openmax/OMX_AsString.h @@ -561,6 +561,7 @@ inline static const char *asString(OMX_INDEXEXTTYPE i, const char *def = "??") { case OMX_IndexConfigPriority: return "ConfigPriority"; case OMX_IndexConfigOperatingRate: return "ConfigOperatingRate"; case OMX_IndexParamConsumerUsageBits: return "ParamConsumerUsageBits"; + case OMX_IndexParamConsumerUsageBits64: return "ParamConsumerUsageBits64"; case OMX_IndexConfigLatency: return "ConfigLatency"; default: return asString((OMX_INDEXTYPE)i, def); } diff --git a/headers/media_plugin/media/openmax/OMX_IndexExt.h b/headers/media_plugin/media/openmax/OMX_IndexExt.h index 0af40dd28e..5ddd719ba1 100644 --- a/headers/media_plugin/media/openmax/OMX_IndexExt.h +++ b/headers/media_plugin/media/openmax/OMX_IndexExt.h @@ -105,6 +105,7 @@ typedef enum OMX_INDEXEXTTYPE { OMX_IndexConfigLowLatency, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexConfigAndroidTunnelPeek, /**< reference: OMX_CONFIG_BOOLEANTYPE */ OMX_IndexConfigAndroidTunnelPeekLegacyMode, /**< reference: OMX_CONFIG_BOOLEANTYPE */ + OMX_IndexParamConsumerUsageBits64, /**< reference: OMX_PARAM_U64TYPE */ OMX_IndexExtOtherEndUnused, /* Time configurations */ diff --git a/include/android/OWNERS b/include/android/OWNERS new file mode 100644 index 0000000000..38f9c5563a --- /dev/null +++ b/include/android/OWNERS @@ -0,0 +1 @@ +per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS diff --git a/include/android/choreographer.h b/include/android/choreographer.h index cd8e63dec2..f999708f04 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -219,12 +219,16 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, * * Note that this time should \b not be used to advance animation clocks. * Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(). + * + * Available since API level 33. */ int64_t AChoreographerFrameCallbackData_getFrameTimeNanos( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); /** * The number of possible frame timelines. + * + * Available since API level 33. */ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); @@ -233,15 +237,20 @@ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( * Gets the index of the platform-preferred frame timeline. * The preferred frame timeline is the default * by which the platform scheduled the app, based on the device configuration. + * + * Available since API level 33. */ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); /** * Gets the token used by the platform to identify the frame timeline at the given \c index. + * q + * Available since API level 33. * * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See * AChoreographerFrameCallbackData_getFrameTimelinesLength() + * */ AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); @@ -250,6 +259,8 @@ AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId( * Gets the time in nanoseconds at which the frame described at the given \c index is expected to * be presented. This time should be used to advance any animation clocks. * + * Available since API level 33. + * * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See * AChoreographerFrameCallbackData_getFrameTimelinesLength() */ @@ -260,6 +271,8 @@ int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTime * Gets the time in nanoseconds at which the frame described at the given \c index needs to be * ready by in order to be presented on time. * + * Available since API level 33. + * * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See * AChoreographerFrameCallbackData_getFrameTimelinesLength() */ diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 79c59f28fb..e4926a6a8d 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -524,6 +524,8 @@ void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transactio * Sets the desired extended range brightness for the layer. This only applies for layers whose * dataspace has RANGE_EXTENDED set on it. * + * Available since API level 34. + * * @param surface_control The layer whose extended range brightness is being specified * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as * peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the @@ -653,6 +655,8 @@ void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction, * and pushing buffers earlier for server side queuing will be advantageous * in such cases. * + * Available since API level 31. + * * \param transaction The transaction in which to make the change. * \param surface_control The ASurfaceControl on which to control buffer backpressure behavior. * \param enableBackPressure Whether to enable back pressure. @@ -674,6 +678,8 @@ void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction, * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId(). * + * Available since API level 33. + * * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target * to the corresponding expected presentation time and deadline from the frame to be rendered. A * stale or invalid value will be ignored. diff --git a/include/ftl/details/optional.h b/include/ftl/details/optional.h index bff7c1e000..e45c1f5e23 100644 --- a/include/ftl/details/optional.h +++ b/include/ftl/details/optional.h @@ -54,5 +54,15 @@ struct and_then_result { template <typename F, typename T> using and_then_result_t = typename and_then_result<F, T>::type; +template <typename F, typename T> +struct or_else_result { + using type = remove_cvref_t<std::invoke_result_t<F>>; + static_assert(std::is_same_v<type, std::optional<T>> || std::is_same_v<type, Optional<T>>, + "or_else function must return an optional T"); +}; + +template <typename F, typename T> +using or_else_result_t = typename or_else_result<F, T>::type; + } // namespace details } // namespace android::ftl diff --git a/include/ftl/optional.h b/include/ftl/optional.h index a818128c92..94d8e3d7cb 100644 --- a/include/ftl/optional.h +++ b/include/ftl/optional.h @@ -96,13 +96,25 @@ struct Optional final : std::optional<T> { return R(); } + // Returns this Optional<T> if not nullopt, or else the Optional<T> returned by the function F. + template <typename F> + constexpr auto or_else(F&& f) const& -> details::or_else_result_t<F, T> { + if (has_value()) return *this; + return std::forward<F>(f)(); + } + + template <typename F> + constexpr auto or_else(F&& f) && -> details::or_else_result_t<F, T> { + if (has_value()) return std::move(*this); + return std::forward<F>(f)(); + } + // 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> diff --git a/include/input/Input.h b/include/input/Input.h index 608519b70e..e8af5f7d46 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -855,6 +855,8 @@ public: const PointerCoords&); static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&, const PointerCoords&); + // The rounding precision for transformed motion events. + static constexpr float ROUNDING_PRECISION = 0.001f; protected: int32_t mAction; diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h index 4668fce116..9dedd2b2da 100644 --- a/include/input/InputEventLabels.h +++ b/include/input/InputEventLabels.h @@ -30,6 +30,12 @@ struct InputEventLabel { int value; }; +struct EvdevEventLabel { + std::string type; + std::string code; + std::string value; +}; + // NOTE: If you want a new key code, axis code, led code or flag code in keylayout file, // then you must add it to InputEventLabels.cpp. @@ -52,6 +58,8 @@ public: static std::optional<int> getLedByLabel(const char* label); + static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value); + private: static const std::unordered_map<std::string, int> KEYCODES; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 1c52792cf6..a1be542d7b 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -38,6 +38,7 @@ #include <binder/IBinder.h> #include <binder/Parcelable.h> #include <input/Input.h> +#include <input/InputVerifier.h> #include <sys/stat.h> #include <ui/Transform.h> #include <utils/BitSet.h> @@ -444,6 +445,7 @@ public: private: std::shared_ptr<InputChannel> mChannel; + InputVerifier mInputVerifier; }; /* diff --git a/include/input/InputVerifier.h b/include/input/InputVerifier.h new file mode 100644 index 0000000000..d4589f53b5 --- /dev/null +++ b/include/input/InputVerifier.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <input/Input.h> +#include <map> + +namespace android { + +/* + * Crash if the provided touch stream is inconsistent. + * + * TODO(b/211379801): Add support for hover events: + * - No hover move without enter + * - No touching pointers when hover enter + * - No hovering pointers when touching + * - Only 1 hovering pointer max + */ +class InputVerifier { +public: + InputVerifier(const std::string& name); + + void processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, int32_t flags); + +private: + const std::string mName; + std::map<int32_t /*deviceId*/, std::bitset<MAX_POINTER_ID + 1>> mTouchingPointerIdsByDevice; + void ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const char* action) const; +}; + +} // namespace android diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index b5e6f65e48..c67310eaec 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -235,7 +235,7 @@ private: KeyedVector<int32_t, Key*> mKeys; KeyboardType mType; std::string mLoadFileName; - bool mLayoutOverlayApplied; + bool mLayoutOverlayApplied = false; std::map<int32_t /* fromAndroidKeyCode */, int32_t /* toAndroidKeyCode */> mKeyRemapping; std::map<int32_t /* fromScanCode */, int32_t /* toAndroidKeyCode */> mKeysByScanCode; diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h index 68ebf75fc6..de8ddcabeb 100644 --- a/include/input/MotionPredictor.h +++ b/include/input/MotionPredictor.h @@ -22,6 +22,7 @@ #include <string> #include <unordered_map> +#include <android-base/result.h> #include <android-base/thread_annotations.h> #include <android/sysprop/InputProperties.sysprop.h> #include <input/Input.h> @@ -66,21 +67,27 @@ public: * checkEnableMotionPredition: the function to check whether the prediction should run. Used to * provide an additional way of turning prediction on and off. Can be toggled at runtime. */ - MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath = nullptr, + MotionPredictor(nsecs_t predictionTimestampOffsetNanos, std::function<bool()> checkEnableMotionPrediction = isMotionPredictionEnabled); - void record(const MotionEvent& event); - std::vector<std::unique_ptr<MotionEvent>> predict(nsecs_t timestamp); + /** + * Record the actual motion received by the view. This event will be used for calculating the + * predictions. + * + * @return empty result if the event was processed correctly, error if the event is not + * consistent with the previously recorded events. + */ + android::base::Result<void> record(const MotionEvent& event); + std::unique_ptr<MotionEvent> predict(nsecs_t timestamp); bool isPredictionAvailable(int32_t deviceId, int32_t source); private: const nsecs_t mPredictionTimestampOffsetNanos; - const std::string mModelPath; const std::function<bool()> mCheckMotionPredictionEnabled; std::unique_ptr<TfLiteMotionPredictorModel> mModel; - // Buffers/events for each device seen by record(). - std::unordered_map</*deviceId*/ int32_t, TfLiteMotionPredictorBuffers> mDeviceBuffers; - std::unordered_map</*deviceId*/ int32_t, MotionEvent> mLastEvents; + + std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers; + std::optional<MotionEvent> mLastEvent; }; } // namespace android diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h index 54e2851a7a..7de551b417 100644 --- a/include/input/TfLiteMotionPredictor.h +++ b/include/input/TfLiteMotionPredictor.h @@ -99,7 +99,7 @@ private: class TfLiteMotionPredictorModel { public: // Creates a model from an encoded Flatbuffer model. - static std::unique_ptr<TfLiteMotionPredictorModel> create(const char* modelPath); + static std::unique_ptr<TfLiteMotionPredictorModel> create(); ~TfLiteMotionPredictorModel(); diff --git a/include/input/VirtualInputDevice.h b/include/input/VirtualInputDevice.h new file mode 100644 index 0000000000..13ffb581b4 --- /dev/null +++ b/include/input/VirtualInputDevice.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/unique_fd.h> + +namespace android { + +enum class UinputAction { + RELEASE = 0, + PRESS = 1, + MOVE = 2, + CANCEL = 3, +}; + +class VirtualInputDevice { +public: + VirtualInputDevice(android::base::unique_fd fd); + virtual ~VirtualInputDevice(); + +protected: + const android::base::unique_fd mFd; + bool writeInputEvent(uint16_t type, uint16_t code, int32_t value); + bool writeEvKeyEvent(int32_t androidCode, int32_t androidAction, + const std::map<int, int>& evKeyCodeMapping, + const std::map<int, UinputAction>& actionMapping); +}; + +class VirtualKeyboard : public VirtualInputDevice { +public: + static const std::map<int, int> KEY_CODE_MAPPING; + // Expose to share with VirtualDpad. + static const std::map<int, UinputAction> KEY_ACTION_MAPPING; + VirtualKeyboard(android::base::unique_fd fd); + virtual ~VirtualKeyboard() override; + bool writeKeyEvent(int32_t androidKeyCode, int32_t androidAction); +}; + +class VirtualDpad : public VirtualInputDevice { +public: + static const std::map<int, int> DPAD_KEY_CODE_MAPPING; + VirtualDpad(android::base::unique_fd fd); + virtual ~VirtualDpad() override; + bool writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction); +}; + +class VirtualMouse : public VirtualInputDevice { +public: + VirtualMouse(android::base::unique_fd fd); + virtual ~VirtualMouse() override; + bool writeButtonEvent(int32_t androidButtonCode, int32_t androidAction); + // TODO(b/259554911): changing float parameters to int32_t. + bool writeRelativeEvent(float relativeX, float relativeY); + bool writeScrollEvent(float xAxisMovement, float yAxisMovement); + +private: + static const std::map<int, UinputAction> BUTTON_ACTION_MAPPING; + static const std::map<int, int> BUTTON_CODE_MAPPING; +}; + +class VirtualTouchscreen : public VirtualInputDevice { +public: + VirtualTouchscreen(android::base::unique_fd fd); + virtual ~VirtualTouchscreen() override; + // TODO(b/259554911): changing float parameters to int32_t. + bool writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action, float locationX, + float locationY, float pressure, float majorAxisSize); + +private: + static const std::map<int, UinputAction> TOUCH_ACTION_MAPPING; + static const std::map<int, int> TOOL_TYPE_MAPPING; + + /* The set of active touch pointers on this device. + * We only allow pointer id to go up to MAX_POINTERS because the maximum slots of virtual + * touchscreen is set up with MAX_POINTERS. Note that in other cases Android allows pointer id + * to go up to MAX_POINTERS_ID. + */ + std::bitset<MAX_POINTERS> mActivePointers{}; + bool isValidPointerId(int32_t pointerId, UinputAction uinputAction); + bool handleTouchDown(int32_t pointerId); + bool handleTouchUp(int32_t pointerId); +}; +} // namespace android diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index c4c8ffb656..6a354b4148 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -74,9 +74,6 @@ cc_defaults { name: "libbinder_common_defaults", host_supported: true, - // for vndbinder and binderRpcTest - vendor_available: true, - srcs: [ "Binder.cpp", "BpBinder.cpp", @@ -200,7 +197,6 @@ cc_defaults { cc_library_headers { name: "trusty_mock_headers", - vendor_available: true, host_supported: true, export_include_dirs: [ @@ -215,7 +211,6 @@ cc_library_headers { cc_defaults { name: "trusty_mock_defaults", - vendor_available: true, host_supported: true, header_libs: [ @@ -309,6 +304,8 @@ cc_library { version_script: "libbinder.map", + // for vndbinder + vendor_available: true, vndk: { enabled: true, }, @@ -467,7 +464,6 @@ cc_library_shared { cc_library_static { name: "libbinder_tls_static", defaults: ["libbinder_tls_defaults"], - vendor_available: true, visibility: [ ":__subpackages__", ], @@ -547,8 +543,10 @@ cc_library { // Do not expand the visibility. visibility: [ ":__subpackages__", - "//packages/modules/Virtualization:__subpackages__", + "//packages/modules/Virtualization/javalib/jni", + "//packages/modules/Virtualization/vm_payload", "//device/google/cuttlefish/shared/minidroid:__subpackages__", + "//system/software_defined_vehicle:__subpackages__", ], } diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS index f954e74eba..bb17683a23 100644 --- a/libs/binder/OWNERS +++ b/libs/binder/OWNERS @@ -1,6 +1,4 @@ # Bug component: 32456 -ctate@google.com -hackbod@google.com maco@google.com smoreland@google.com tkjos@google.com diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 2e7030437c..51b971651d 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -16,6 +16,7 @@ #include <android-base/file.h> #include <android-base/logging.h> +#include <android-base/scopeguard.h> #include <android-base/unique_fd.h> #include <binder/RecordedTransaction.h> #include <sys/mman.h> @@ -176,13 +177,33 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd RecordedTransaction t; ChunkDescriptor chunk; const long pageSize = sysconf(_SC_PAGE_SIZE); + struct stat fileStat; + if (fstat(fd.get(), &fileStat) != 0) { + LOG(ERROR) << "Unable to get file information"; + return std::nullopt; + } + + off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); + if (fdCurrentPosition == -1) { + LOG(ERROR) << "Invalid offset in file descriptor."; + return std::nullopt; + } do { + if (fileStat.st_size < (fdCurrentPosition + (off_t)sizeof(ChunkDescriptor))) { + LOG(ERROR) << "Not enough file remains to contain expected chunk descriptor"; + return std::nullopt; + } transaction_checksum_t checksum = 0; if (NO_ERROR != readChunkDescriptor(fd, &chunk, &checksum)) { LOG(ERROR) << "Failed to read chunk descriptor."; return std::nullopt; } - off_t fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); + + fdCurrentPosition = lseek(fd.get(), 0, SEEK_CUR); + if (fdCurrentPosition == -1) { + LOG(ERROR) << "Invalid offset in file descriptor."; + return std::nullopt; + } off_t mmapPageAlignedStart = (fdCurrentPosition / pageSize) * pageSize; off_t mmapPayloadStartOffset = fdCurrentPosition - mmapPageAlignedStart; @@ -194,14 +215,24 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd size_t chunkPayloadSize = chunk.dataSize + PADDING8(chunk.dataSize) + sizeof(transaction_checksum_t); + if (chunkPayloadSize > (size_t)(fileStat.st_size - fdCurrentPosition)) { + LOG(ERROR) << "Chunk payload exceeds remaining file size."; + return std::nullopt; + } + if (PADDING8(chunkPayloadSize) != 0) { LOG(ERROR) << "Invalid chunk size, not aligned " << chunkPayloadSize; return std::nullopt; } - transaction_checksum_t* payloadMap = reinterpret_cast<transaction_checksum_t*>( - mmap(NULL, chunkPayloadSize + mmapPayloadStartOffset, PROT_READ, MAP_SHARED, - fd.get(), mmapPageAlignedStart)); + size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset; + void* mappedMemory = + mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart); + auto mmap_guard = android::base::make_scope_guard( + [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); }); + + transaction_checksum_t* payloadMap = + reinterpret_cast<transaction_checksum_t*>(mappedMemory); payloadMap += mmapPayloadStartOffset / sizeof(transaction_checksum_t); // Skip chunk descriptor and required mmap // page-alignment @@ -218,7 +249,12 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd LOG(ERROR) << "Checksum failed."; return std::nullopt; } - lseek(fd.get(), chunkPayloadSize, SEEK_CUR); + + fdCurrentPosition = lseek(fd.get(), chunkPayloadSize, SEEK_CUR); + if (fdCurrentPosition == -1) { + LOG(ERROR) << "Invalid offset in file descriptor."; + return std::nullopt; + } switch (chunk.chunkType) { case HEADER_CHUNK: { @@ -255,7 +291,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd break; default: LOG(INFO) << "Unrecognized chunk."; - continue; + break; } } while (chunk.chunkType != END_CHUNK); diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index ce6ef2becf..fbad0f7756 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -20,6 +20,7 @@ #include <dlfcn.h> #include <inttypes.h> +#include <netinet/tcp.h> #include <poll.h> #include <unistd.h> @@ -90,16 +91,16 @@ size_t RpcSession::getMaxIncomingThreads() { return mMaxIncomingThreads; } -void RpcSession::setMaxOutgoingThreads(size_t threads) { +void RpcSession::setMaxOutgoingConnections(size_t connections) { RpcMutexLockGuard _l(mMutex); LOG_ALWAYS_FATAL_IF(mStartedSetup, "Must set max outgoing threads before setting up connections"); - mMaxOutgoingThreads = threads; + mMaxOutgoingConnections = connections; } size_t RpcSession::getMaxOutgoingThreads() { RpcMutexLockGuard _l(mMutex); - return mMaxOutgoingThreads; + return mMaxOutgoingConnections; } bool RpcSession::setProtocolVersionInternal(uint32_t version, bool checkStarted) { @@ -558,11 +559,11 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< return status; } - size_t outgoingThreads = std::min(numThreadsAvailable, mMaxOutgoingThreads); - ALOGI_IF(outgoingThreads != numThreadsAvailable, + size_t outgoingConnections = std::min(numThreadsAvailable, mMaxOutgoingConnections); + ALOGI_IF(outgoingConnections != numThreadsAvailable, "Server hints client to start %zu outgoing threads, but client will only start %zu " "because it is preconfigured to start at most %zu outgoing threads.", - numThreadsAvailable, outgoingThreads, mMaxOutgoingThreads); + numThreadsAvailable, outgoingConnections, mMaxOutgoingConnections); // TODO(b/189955605): we should add additional sessions dynamically // instead of all at once - the other side should be responsible for setting @@ -571,10 +572,10 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< // any requests at all. // we've already setup one client - LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing (server max: %zu) and %zu " - "incoming threads", - outgoingThreads, numThreadsAvailable, mMaxIncomingThreads); - for (size_t i = 0; i + 1 < outgoingThreads; i++) { + LOG_RPC_DETAIL("RpcSession::setupClient() instantiating %zu outgoing connections (server max: " + "%zu) and %zu incoming threads", + outgoingConnections, numThreadsAvailable, mMaxIncomingThreads); + for (size_t i = 0; i + 1 < outgoingConnections; i++) { if (status_t status = connectAndInit(mId, false /*incoming*/); status != OK) return status; } @@ -608,6 +609,18 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr, return -savedErrno; } + if (addr.addr()->sa_family == AF_INET || addr.addr()->sa_family == AF_INET6) { + int noDelay = 1; + int result = + setsockopt(serverFd.get(), IPPROTO_TCP, TCP_NODELAY, &noDelay, sizeof(noDelay)); + if (result < 0) { + int savedErrno = errno; + ALOGE("Could not set TCP_NODELAY on %s: %s", addr.toString().c_str(), + strerror(savedErrno)); + return -savedErrno; + } + } + RpcTransportFd transportFd(std::move(serverFd)); if (0 != TEMP_FAILURE_RETRY(connect(transportFd.fd.get(), addr.addr(), addr.addrSize()))) { @@ -932,7 +945,8 @@ status_t RpcSession::ExclusiveConnection::find(const sp<RpcSession>& session, Co (session->server() ? "This is a server session, so see RpcSession::setMaxIncomingThreads " "for the corresponding client" - : "This is a client session, so see RpcSession::setMaxOutgoingThreads " + : "This is a client session, so see " + "RpcSession::setMaxOutgoingConnections " "for this client or RpcServer::setMaxThreads for the corresponding " "server")); return WOULD_BLOCK; diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index b27f1028d4..38bd081b8b 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -557,13 +557,12 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti .parcelDataSize = static_cast<uint32_t>(data.dataSize()), }; - constexpr size_t kWaitMaxUs = 1000000; - constexpr size_t kWaitLogUs = 10000; - size_t waitUs = 0; - // Oneway calls have no sync point, so if many are sent before, whether this // is a twoway or oneway transaction, they may have filled up the socket. // So, make sure we drain them before polling + constexpr size_t kWaitMaxUs = 1000000; + constexpr size_t kWaitLogUs = 10000; + size_t waitUs = 0; iovec iovs[]{ {&command, sizeof(RpcWireHeader)}, @@ -591,8 +590,9 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti }, rpcFields->mFds.get()); status != OK) { - // TODO(b/167966510): need to undo onBinderLeaving - we know the - // refcount isn't successfully transferred. + // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate + // errors here, then we may need to undo the binder-sent counts for the transaction as + // well as for the binder objects in the Parcel return status; } @@ -1036,8 +1036,8 @@ processTransactInternalTailCall: return DEAD_OBJECT; } - if (it->second.asyncTodo.size() == 0) return OK; - if (it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) { + if (it->second.asyncTodo.size() != 0 && + it->second.asyncTodo.top().asyncNumber == it->second.asyncNumber) { LOG_RPC_DETAIL("Found next async transaction %" PRIu64 " on %" PRIu64, it->second.asyncNumber, addr); diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp index 194254ac69..2b67f030e0 100644 --- a/libs/binder/ServiceManagerHost.cpp +++ b/libs/binder/ServiceManagerHost.cpp @@ -159,8 +159,8 @@ sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs, LOG_ALWAYS_FATAL_IF(!forwardResult->hostPort().has_value()); auto rpcSession = RpcSession::make(); - if (options.maxOutgoingThreads.has_value()) { - rpcSession->setMaxOutgoingThreads(*options.maxOutgoingThreads); + if (options.maxOutgoingConnections.has_value()) { + rpcSession->setMaxOutgoingConnections(*options.maxOutgoingConnections); } if (status_t status = rpcSession->setupInetClient("127.0.0.1", *forwardResult->hostPort()); diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index c78f870153..55167a7db0 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -224,12 +224,12 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, // } // Resources are cleaned up when the object is destroyed. // -// For each returned binder object, at most |maxOutgoingThreads| outgoing threads are instantiated. -// Hence, only |maxOutgoingThreads| calls can be made simultaneously. Additional calls are blocked -// if there are |maxOutgoingThreads| ongoing calls. See RpcSession::setMaxOutgoingThreads. -// If |maxOutgoingThreads| is not set, default is |RpcSession::kDefaultMaxOutgoingThreads|. +// For each returned binder object, at most |maxOutgoingConnections| outgoing connections are +// instantiated, depending on how many the service on the device is configured with. +// Hence, only |maxOutgoingConnections| calls can be made simultaneously. +// See also RpcSession::setMaxOutgoingConnections. struct RpcDelegateServiceManagerOptions { - std::optional<size_t> maxOutgoingThreads; + std::optional<size_t> maxOutgoingConnections; }; sp<IServiceManager> createRpcDelegateServiceManager( const RpcDelegateServiceManagerOptions& options); diff --git a/libs/binder/include/binder/RpcServer.h b/libs/binder/include/binder/RpcServer.h index 25193a3428..1001b64ede 100644 --- a/libs/binder/include/binder/RpcServer.h +++ b/libs/binder/include/binder/RpcServer.h @@ -119,7 +119,10 @@ public: [[nodiscard]] status_t setupExternalServer(base::unique_fd serverFd); /** - * This must be called before adding a client session. + * This must be called before adding a client session. This corresponds + * to the number of incoming connections to RpcSession objects in the + * server, which will correspond to the number of outgoing connections + * in client RpcSession objects. * * If this is not specified, this will be a single-threaded server. * diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index 40faf2c3b1..0750ccfc3f 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -54,8 +54,6 @@ constexpr uint32_t RPC_WIRE_PROTOCOL_VERSION_RPC_HEADER_FEATURE_EXPLICIT_PARCEL_ */ class RpcSession final : public virtual RefBase { public: - static constexpr size_t kDefaultMaxOutgoingThreads = 10; - // Create an RpcSession with default configuration (raw sockets). static sp<RpcSession> make(); @@ -67,26 +65,30 @@ public: /** * Set the maximum number of incoming threads allowed to be made (for things like callbacks). * By default, this is 0. This must be called before setting up this connection as a client. - * Server sessions will inherits this value from RpcServer. + * Server sessions will inherits this value from RpcServer. Each thread will serve a + * connection to the remote RpcSession. * * If this is called, 'shutdown' on this session must also be called. * Otherwise, a threadpool will leak. * - * TODO(b/189955605): start these dynamically + * TODO(b/189955605): start these lazily - currently all are started */ void setMaxIncomingThreads(size_t threads); size_t getMaxIncomingThreads(); /** - * Set the maximum number of outgoing threads allowed to be made. - * By default, this is |kDefaultMaxOutgoingThreads|. This must be called before setting up this - * connection as a client. + * Set the maximum number of outgoing connections allowed to be made. + * By default, this is |kDefaultMaxOutgoingConnections|. This must be called before setting up + * this connection as a client. * - * This limits the number of outgoing threads on top of the remote peer setting. This RpcSession - * will only instantiate |min(maxOutgoingThreads, remoteMaxThreads)| outgoing threads, where - * |remoteMaxThreads| can be retrieved from the remote peer via |getRemoteMaxThreads()|. + * For an RpcSession client, if you are connecting to a server which starts N threads, + * then this must be set to >= N. If you set the maximum number of outgoing connections + * to 1, but the server requests 10, then it would be considered an error. If you set a + * maximum number of connections to 10, and the server requests 1, then only 1 will be + * created. This API is used to limit the amount of resources a server can request you + * create. */ - void setMaxOutgoingThreads(size_t threads); + void setMaxOutgoingConnections(size_t connections); size_t getMaxOutgoingThreads(); /** @@ -219,6 +221,8 @@ private: friend RpcState; explicit RpcSession(std::unique_ptr<RpcTransportCtx> ctx); + static constexpr size_t kDefaultMaxOutgoingConnections = 10; + // internal version of setProtocolVersion that // optionally skips the mStartedSetup check [[nodiscard]] bool setProtocolVersionInternal(uint32_t version, bool checkStarted); @@ -368,7 +372,7 @@ private: bool mStartedSetup = false; size_t mMaxIncomingThreads = 0; - size_t mMaxOutgoingThreads = kDefaultMaxOutgoingThreads; + size_t mMaxOutgoingConnections = kDefaultMaxOutgoingConnections; std::optional<uint32_t> mProtocolVersion; FileDescriptorTransportMode mFileDescriptorTransportMode = FileDescriptorTransportMode::NONE; diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp index 42d226b805..a157792156 100644 --- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp +++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp @@ -126,11 +126,11 @@ AIBinder* ARpcSession_setupPreconnectedClient(ARpcSession* session, void ARpcSession_setFileDescriptorTransportMode(ARpcSession* session, ARpcSession_FileDescriptorTransportMode mode); -// Sets the maximum number of incoming threads. +// Sets the maximum number of incoming threads, to service connections. void ARpcSession_setMaxIncomingThreads(ARpcSession* session, size_t threads); -// Sets the maximum number of outgoing threads. -void ARpcSession_setMaxOutgoingThreads(ARpcSession* session, size_t threads); +// Sets the maximum number of outgoing connections. +void ARpcSession_setMaxOutgoingConnections(ARpcSession* session, size_t connections); // Decrements the refcount of the underlying RpcSession object. void ARpcSession_free(ARpcSession* session); diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp index daff8c1e16..a167f235d5 100644 --- a/libs/binder/libbinder_rpc_unstable.cpp +++ b/libs/binder/libbinder_rpc_unstable.cpp @@ -265,8 +265,8 @@ void ARpcSession_setMaxIncomingThreads(ARpcSession* handle, size_t threads) { session->setMaxIncomingThreads(threads); } -void ARpcSession_setMaxOutgoingThreads(ARpcSession* handle, size_t threads) { +void ARpcSession_setMaxOutgoingConnections(ARpcSession* handle, size_t connections) { auto session = handleToStrongPointer<RpcSession>(handle); - session->setMaxOutgoingThreads(threads); + session->setMaxOutgoingConnections(connections); } } diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index f68612c3ba..d833b837d0 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -26,11 +26,11 @@ #pragma once +#include <android/binder_status.h> #include <stdbool.h> #include <stddef.h> #include <sys/cdefs.h> - -#include <android/binder_status.h> +#include <uchar.h> struct AIBinder; typedef struct AIBinder AIBinder; diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 86d5ed27b8..43159d8ba2 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -22,6 +22,16 @@ __BEGIN_DECLS +enum AServiceManager_AddServiceFlag : uint32_t { + /** + * This allows processes with AID_ISOLATED to get the binder of the service added. + * + * Services with methods that perform file IO, web socket creation or ways to egress data must + * not be added with this flag for privacy concerns. + */ + ADD_SERVICE_ALLOW_ISOLATED = 1, +}; + /** * This registers the service with the default service manager under this instance name. This does * not take ownership of binder. @@ -46,12 +56,13 @@ __attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServic * * \param binder object to register globally with the service manager. * \param instance identifier of the service. This will be used to lookup the service. - * \param allowIsolated allows if this service can be isolated. + * \param flag an AServiceManager_AddServiceFlag enum to denote how the service should be added. * * \return EX_NONE on success. */ -__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithAllowIsolated( - AIBinder* binder, const char* instance, bool allowIsolated) __INTRODUCED_IN(34); +__attribute__((warn_unused_result)) binder_exception_t AServiceManager_addServiceWithFlag( + AIBinder* binder, const char* instance, const AServiceManager_AddServiceFlag flag) + __INTRODUCED_IN(34); /** * Gets a binder object with this specific instance name. Will return nullptr immediately if the diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 5f2f617946..1078fb2b16 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -158,12 +158,12 @@ LIBBINDER_NDK34 { # introduced=UpsideDownCake AServiceManager_getUpdatableApexName; # systemapi AServiceManager_registerForServiceNotifications; # systemapi llndk AServiceManager_NotificationRegistration_delete; # systemapi llndk + AServiceManager_addServiceWithFlag; # systemapi llndk }; LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; - AServiceManager_addServiceWithAllowIsolated; extern "C++" { AIBinder_fromPlatformBinder*; AIBinder_toPlatformBinder*; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 2763ddb622..84da459454 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -42,14 +42,15 @@ binder_exception_t AServiceManager_addService(AIBinder* binder, const char* inst return PruneException(exception); } -binder_exception_t AServiceManager_addServiceWithAllowIsolated(AIBinder* binder, - const char* instance, - bool allowIsolated) { +binder_exception_t AServiceManager_addServiceWithFlag(AIBinder* binder, const char* instance, + const AServiceManager_AddServiceFlag flag) { if (binder == nullptr || instance == nullptr) { return EX_ILLEGAL_ARGUMENT; } sp<IServiceManager> sm = defaultServiceManager(); + + bool allowIsolated = flag & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED; status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated); return PruneException(exception); } diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 5b2532ab4e..882f1d66b7 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -52,7 +52,7 @@ constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest"; constexpr char kForcePersistNdkUnitTestService[] = "ForcePersistNdkUnitTestService"; constexpr char kActiveServicesNdkUnitTestService[] = "ActiveServicesNdkUnitTestService"; -constexpr unsigned int kShutdownWaitTime = 10; +constexpr unsigned int kShutdownWaitTime = 11; constexpr uint64_t kContextTestValue = 0xb4e42fb4d9a1d715; class MyTestFoo : public IFoo { diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp index afd414a7cb..d36ebac109 100644 --- a/libs/binder/rust/Android.bp +++ b/libs/binder/rust/Android.bp @@ -21,6 +21,7 @@ rust_library { ], host_supported: true, vendor_available: true, + product_available: true, target: { darwin: { enabled: false, @@ -72,6 +73,7 @@ rust_library { ], host_supported: true, vendor_available: true, + product_available: true, target: { darwin: { enabled: false, @@ -129,6 +131,7 @@ rust_bindgen { ], host_supported: true, vendor_available: true, + product_available: true, // Currently necessary for host builds // TODO(b/31559095): bionic on host should define this diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp index da9797b25c..0067a20484 100644 --- a/libs/binder/rust/rpcbinder/Android.bp +++ b/libs/binder/rust/rpcbinder/Android.bp @@ -26,6 +26,7 @@ rust_library { visibility: [ "//device/google/cuttlefish/shared/minidroid/sample", "//packages/modules/Virtualization:__subpackages__", + "//system/software_defined_vehicle:__subpackages__", ], apex_available: [ "//apex_available:platform", diff --git a/libs/binder/rust/rpcbinder/src/session.rs b/libs/binder/rust/rpcbinder/src/session.rs index 0b517cf613..28c5390665 100644 --- a/libs/binder/rust/rpcbinder/src/session.rs +++ b/libs/binder/rust/rpcbinder/src/session.rs @@ -75,11 +75,14 @@ impl RpcSessionRef { }; } - /// Sets the maximum number of outgoing threads. - pub fn set_max_outgoing_threads(&self, threads: usize) { + /// Sets the maximum number of outgoing connections. + pub fn set_max_outgoing_connections(&self, connections: usize) { // SAFETY - Only passes the 'self' pointer as an opaque handle. unsafe { - binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingThreads(self.as_ptr(), threads) + binder_rpc_unstable_bindgen::ARpcSession_setMaxOutgoingConnections( + self.as_ptr(), + connections, + ) }; } diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs index 6f686fbd93..5557168055 100644 --- a/libs/binder/rust/src/native.rs +++ b/libs/binder/rust/src/native.rs @@ -209,8 +209,8 @@ impl<T: Remotable> Binder<T> { } /// Mark this binder object with local stability, which is vendor if we are - /// building for the VNDK and system otherwise. - #[cfg(any(vendor_ndk, android_vndk))] + /// building for android_vendor and system otherwise. + #[cfg(android_vendor)] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so @@ -220,8 +220,8 @@ impl<T: Remotable> Binder<T> { } /// Mark this binder object with local stability, which is vendor if we are - /// building for the VNDK and system otherwise. - #[cfg(not(any(vendor_ndk, android_vndk)))] + /// building for android_vendor and system otherwise. + #[cfg(not(android_vendor))] fn mark_local_stability(&mut self) { unsafe { // Safety: Self always contains a valid `AIBinder` pointer, so diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index e609987725..0f0d64aea8 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -138,7 +138,6 @@ cc_test { aidl_interface { name: "binderRpcTestIface", - vendor_available: true, host_supported: true, unstable: true, srcs: [ @@ -159,7 +158,6 @@ aidl_interface { cc_library_static { name: "libbinder_tls_test_utils", - vendor_available: true, host_supported: true, target: { darwin: { @@ -213,7 +211,6 @@ cc_defaults { defaults: [ "binderRpcTest_common_defaults", ], - vendor_available: true, gtest: false, auto_gen_config: false, srcs: [ @@ -224,18 +221,10 @@ cc_defaults { cc_defaults { name: "binderRpcTest_defaults", - vendor_available: true, target: { android: { test_suites: ["vts"], }, - - vendor: { - shared_libs: [ - "libbinder_trusty", - "libtrusty", - ], - }, }, defaults: [ "binderRpcTest_common_defaults", diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp index 464da60dde..77a5fa8d65 100644 --- a/libs/binder/tests/binderHostDeviceTest.cpp +++ b/libs/binder/tests/binderHostDeviceTest.cpp @@ -66,7 +66,7 @@ MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected void initHostRpcServiceManagerOnce() { static std::once_flag gSmOnce; std::call_once(gSmOnce, [] { - setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingThreads = 1})); + setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1})); }); } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 955c650205..8974ad745d 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -507,7 +507,13 @@ TEST_F(BinderLibTest, Freeze) { } EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0)); - EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0)); + + // b/268232063 - succeeds ~0.08% of the time + { + auto ret = IPCThreadState::self()->freeze(pid, true, 0); + EXPECT_TRUE(ret == -EAGAIN || ret == OK); + } + EXPECT_EQ(NO_ERROR, IPCThreadState::self()->freeze(pid, true, 1000)); EXPECT_EQ(FAILED_TRANSACTION, m_server->transact(BINDER_LIB_TEST_NOP_TRANSACTION, data, &reply)); @@ -1370,7 +1376,7 @@ TEST_F(BinderLibTest, ThreadPoolAvailableThreads) { })); } - data.writeInt32(100); + data.writeInt32(500); // Give a chance for all threads to be used EXPECT_THAT(server->transact(BINDER_LIB_TEST_UNLOCK_AFTER_MS, data, &reply), NO_ERROR); diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp index 84c93ddc30..5952c4172e 100644 --- a/libs/binder/tests/binderRpcTest.cpp +++ b/libs/binder/tests/binderRpcTest.cpp @@ -129,7 +129,7 @@ private: static std::string allocateSocketAddress() { static size_t id = 0; std::string temp = getenv("TMPDIR") ?: "/tmp"; - auto ret = temp + "/binderRpcTest_" + std::to_string(id++); + auto ret = temp + "/binderRpcTest_" + std::to_string(getpid()) + "_" + std::to_string(id++); unlink(ret.c_str()); return ret; }; @@ -237,9 +237,13 @@ std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& i std::to_string(clientVersion) + "_serverV" + std::to_string(serverVersion); if (singleThreaded) { ret += "_single_threaded"; + } else { + ret += "_multi_threaded"; } if (noKernel) { ret += "_no_kernel"; + } else { + ret += "_with_kernel"; } return ret; } @@ -350,7 +354,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( for (const auto& session : sessions) { CHECK(session->setProtocolVersion(clientVersion)); session->setMaxIncomingThreads(options.numIncomingConnections); - session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setMaxOutgoingConnections(options.numOutgoingConnections); session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); switch (socketType) { @@ -435,8 +439,7 @@ TEST_P(BinderRpc, ThreadPoolGreaterThanEqualRequested) { for (auto& t : ts) t.join(); } -static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, - size_t sleepMs = 500) { +static void testThreadPoolOverSaturated(sp<IBinderRpcTest> iface, size_t numCalls, size_t sleepMs) { size_t epochMsBefore = epochMillis(); std::vector<std::thread> ts; @@ -462,7 +465,7 @@ TEST_P(BinderRpc, ThreadPoolOverSaturated) { constexpr size_t kNumThreads = 10; constexpr size_t kNumCalls = kNumThreads + 3; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumThreads}); - testThreadPoolOverSaturated(proc.rootIface, kNumCalls); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/); } TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { @@ -475,7 +478,7 @@ TEST_P(BinderRpc, ThreadPoolLimitOutgoing) { constexpr size_t kNumCalls = kNumOutgoingConnections + 3; auto proc = createRpcTestSocketServerProcess( {.numThreads = kNumThreads, .numOutgoingConnections = kNumOutgoingConnections}); - testThreadPoolOverSaturated(proc.rootIface, kNumCalls); + testThreadPoolOverSaturated(proc.rootIface, kNumCalls, 250 /*ms*/); } TEST_P(BinderRpc, ThreadingStressTest) { @@ -483,9 +486,9 @@ TEST_P(BinderRpc, ThreadingStressTest) { GTEST_SKIP() << "This test requires multiple threads"; } - constexpr size_t kNumClientThreads = 10; - constexpr size_t kNumServerThreads = 10; - constexpr size_t kNumCalls = 100; + constexpr size_t kNumClientThreads = 5; + constexpr size_t kNumServerThreads = 5; + constexpr size_t kNumCalls = 50; auto proc = createRpcTestSocketServerProcess({.numThreads = kNumServerThreads}); @@ -544,6 +547,8 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { GTEST_SKIP() << "This test requires multiple threads"; } + constexpr size_t kNumServerThreads = 3; + // This test forces a oneway transaction to be queued by issuing two // `blockingSendFdOneway` calls, then drains the queue by issuing two // `blockingRecvFd` calls. @@ -552,7 +557,7 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { // https://developer.android.com/reference/android/os/IBinder#FLAG_ONEWAY auto proc = createRpcTestSocketServerProcess({ - .numThreads = 3, + .numThreads = kNumServerThreads, .clientFileDescriptorTransportMode = RpcSession::FileDescriptorTransportMode::UNIX, .serverSupportedFileDescriptorTransportModes = {RpcSession::FileDescriptorTransportMode::UNIX}, @@ -573,6 +578,8 @@ TEST_P(BinderRpc, OnewayCallQueueingWithFds) { EXPECT_OK(proc.rootIface->blockingRecvFd(&fdB)); CHECK(android::base::ReadFdToString(fdB.get(), &result)); EXPECT_EQ(result, "b"); + + saturateThreadPool(kNumServerThreads, proc.rootIface); } TEST_P(BinderRpc, OnewayCallQueueing) { diff --git a/libs/binder/tests/binderRpcTestService.cpp b/libs/binder/tests/binderRpcTestService.cpp index 714f0636f9..a27bd2f2e6 100644 --- a/libs/binder/tests/binderRpcTestService.cpp +++ b/libs/binder/tests/binderRpcTestService.cpp @@ -85,7 +85,9 @@ public: } }; -int main(int argc, const char* argv[]) { +int main(int argc, char* argv[]) { + android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter); + LOG_ALWAYS_FATAL_IF(argc != 3, "Invalid number of arguments: %d", argc); base::unique_fd writeEnd(atoi(argv[1])); base::unique_fd readEnd(atoi(argv[2])); diff --git a/libs/binder/tests/binderRpcTestTrusty.cpp b/libs/binder/tests/binderRpcTestTrusty.cpp index b3bb5ebda7..63b56a3a64 100644 --- a/libs/binder/tests/binderRpcTestTrusty.cpp +++ b/libs/binder/tests/binderRpcTestTrusty.cpp @@ -45,9 +45,13 @@ std::string BinderRpc::PrintParamInfo(const testing::TestParamInfo<ParamType>& i std::to_string(serverVersion); if (singleThreaded) { ret += "_single_threaded"; + } else { + ret += "_multi_threaded"; } if (noKernel) { ret += "_no_kernel"; + } else { + ret += "_with_kernel"; } return ret; } @@ -71,7 +75,7 @@ std::unique_ptr<ProcessSession> BinderRpc::createRpcTestSocketServerProcessEtc( auto session = android::RpcSession::make(std::move(factory)); EXPECT_TRUE(session->setProtocolVersion(clientVersion)); - session->setMaxOutgoingThreads(options.numOutgoingConnections); + session->setMaxOutgoingConnections(options.numOutgoingConnections); session->setFileDescriptorTransportMode(options.clientFileDescriptorTransportMode); status = session->setupPreconnectedClient({}, [&]() { diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp index 8ea948cc15..a88158299e 100644 --- a/libs/binder/tests/unit_fuzzers/Android.bp +++ b/libs/binder/tests/unit_fuzzers/Android.bp @@ -104,3 +104,42 @@ cc_fuzz { defaults: ["binder_fuzz_defaults"], srcs: ["MemoryDealerFuzz.cpp"], } + +cc_fuzz { + name: "binder_recordedTransactionFileFuzz", + defaults: ["binder_fuzz_defaults"], + srcs: ["RecordedTransactionFileFuzz.cpp"], + corpus: [ + "recorded_transaction_corpus/*", + ], +} + +cc_fuzz { + name: "binder_recordedTransactionFuzz", + defaults: ["binder_fuzz_defaults"], + srcs: ["RecordedTransactionFuzz.cpp"], + target: { + android: { + shared_libs: [ + "libcutils", + "libutils", + "libbase", + "libbinder", + ], + static_libs: ["libbinder_random_parcel"], + }, + host: { + static_libs: [ + "libcutils", + "liblog", + "libutils", + "libbase", + "libbinder", + "libbinder_random_parcel", + ], + }, + darwin: { + enabled: false, + }, + }, +} diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp new file mode 100644 index 0000000000..73790fae49 --- /dev/null +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/macros.h> +#include <binder/RecordedTransaction.h> +#include <filesystem> + +#include "fuzzer/FuzzedDataProvider.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + std::FILE* intermediateFile = std::tmpfile(); + fwrite(data, sizeof(uint8_t), size, intermediateFile); + rewind(intermediateFile); + int fileNumber = fileno(intermediateFile); + + android::base::unique_fd fd(fileNumber); + + auto transaction = android::binder::debug::RecordedTransaction::fromFile(fd); + + std::fclose(intermediateFile); + + if (transaction.has_value()) { + intermediateFile = std::tmpfile(); + + android::base::unique_fd fdForWriting(fileno(intermediateFile)); + auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + + std::fclose(intermediateFile); + } + + return 0; +} diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp new file mode 100644 index 0000000000..943fb9f285 --- /dev/null +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/macros.h> +#include <binder/RecordedTransaction.h> +#include <fuzzbinder/random_parcel.h> +#include <filesystem> +#include <string> + +#include "fuzzer/FuzzedDataProvider.h" + +using android::fillRandomParcel; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider provider = FuzzedDataProvider(data, size); + + android::String16 interfaceName = + android::String16(provider.ConsumeRandomLengthString().c_str()); + + uint32_t code = provider.ConsumeIntegral<uint32_t>(); + uint32_t flags = provider.ConsumeIntegral<uint32_t>(); + time_t sec = provider.ConsumeIntegral<time_t>(); + long nsec = provider.ConsumeIntegral<long>(); + timespec timestamp = {.tv_sec = sec, .tv_nsec = nsec}; + android::status_t transactionStatus = provider.ConsumeIntegral<android::status_t>(); + + std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>( + provider.ConsumeIntegralInRange<size_t>(0, provider.remaining_bytes())); + + // same options so that FDs and binders could be shared in both Parcels + android::RandomParcelOptions options; + + android::Parcel p0, p1; + fillRandomParcel(&p0, FuzzedDataProvider(bytes.data(), bytes.size()), &options); + fillRandomParcel(&p1, std::move(provider), &options); + + auto transaction = + android::binder::debug::RecordedTransaction::fromDetails(interfaceName, code, flags, + timestamp, p0, p1, + transactionStatus); + + if (transaction.has_value()) { + std::FILE* intermediateFile = std::tmpfile(); + android::base::unique_fd fdForWriting(fileno(intermediateFile)); + auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + + std::fclose(intermediateFile); + } + + return 0; +} diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording Binary files differnew file mode 100644 index 0000000000..79442078c2 --- /dev/null +++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/power_recording diff --git a/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction Binary files differnew file mode 100644 index 0000000000..658addbed5 --- /dev/null +++ b/libs/binder/tests/unit_fuzzers/recorded_transaction_corpus/recorded_binder_transaction diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index 29924ff500..96dcce11ea 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -11,7 +11,7 @@ cc_defaults { name: "fakeservicemanager_defaults", host_supported: true, srcs: [ - "ServiceManager.cpp", + "FakeServiceManager.cpp", ], shared_libs: [ @@ -28,7 +28,7 @@ cc_defaults { cc_library { name: "libfakeservicemanager", defaults: ["fakeservicemanager_defaults"], - export_include_dirs: ["include/fakeservicemanager"], + export_include_dirs: ["include"], } cc_test_host { @@ -38,5 +38,5 @@ cc_test_host { "test_sm.cpp", ], static_libs: ["libgmock"], - local_include_dirs: ["include/fakeservicemanager"], + local_include_dirs: ["include"], } diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp index 1109ad8594..3272bbc1aa 100644 --- a/libs/fakeservicemanager/ServiceManager.cpp +++ b/libs/fakeservicemanager/FakeServiceManager.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "ServiceManager.h" +#include "fakeservicemanager/FakeServiceManager.h" namespace android { -ServiceManager::ServiceManager() {} +FakeServiceManager::FakeServiceManager() {} -sp<IBinder> ServiceManager::getService( const String16& name) const { +sp<IBinder> FakeServiceManager::getService( const String16& name) const { // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons. return checkService(name); } -sp<IBinder> ServiceManager::checkService( const String16& name) const { +sp<IBinder> FakeServiceManager::checkService( const String16& name) const { auto it = mNameToService.find(name); if (it == mNameToService.end()) { return nullptr; @@ -33,7 +33,7 @@ sp<IBinder> ServiceManager::checkService( const String16& name) const { return it->second; } -status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service, +status_t FakeServiceManager::addService(const String16& name, const sp<IBinder>& service, bool /*allowIsolated*/, int /*dumpsysFlags*/) { if (service == nullptr) { @@ -43,7 +43,7 @@ status_t ServiceManager::addService(const String16& name, const sp<IBinder>& ser return NO_ERROR; } -Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) { +Vector<String16> FakeServiceManager::listServices(int /*dumpsysFlags*/) { Vector<String16> services; for (auto const& [name, service] : mNameToService) { (void) service; @@ -52,19 +52,19 @@ Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) { return services; } -IBinder* ServiceManager::onAsBinder() { +IBinder* FakeServiceManager::onAsBinder() { return nullptr; } -sp<IBinder> ServiceManager::waitForService(const String16& name) { +sp<IBinder> FakeServiceManager::waitForService(const String16& name) { return checkService(name); } -bool ServiceManager::isDeclared(const String16& name) { +bool FakeServiceManager::isDeclared(const String16& name) { return mNameToService.find(name) != mNameToService.end(); } -Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) { +Vector<String16> FakeServiceManager::getDeclaredInstances(const String16& name) { Vector<String16> out; const String16 prefix = name + String16("/"); for (const auto& [registeredName, service] : mNameToService) { @@ -76,38 +76,38 @@ Vector<String16> ServiceManager::getDeclaredInstances(const String16& name) { return out; } -std::optional<String16> ServiceManager::updatableViaApex(const String16& name) { +std::optional<String16> FakeServiceManager::updatableViaApex(const String16& name) { (void)name; return std::nullopt; } -Vector<String16> ServiceManager::getUpdatableNames(const String16& apexName) { +Vector<String16> FakeServiceManager::getUpdatableNames(const String16& apexName) { (void)apexName; return {}; } -std::optional<IServiceManager::ConnectionInfo> ServiceManager::getConnectionInfo( +std::optional<IServiceManager::ConnectionInfo> FakeServiceManager::getConnectionInfo( const String16& name) { (void)name; return std::nullopt; } -status_t ServiceManager::registerForNotifications(const String16&, +status_t FakeServiceManager::registerForNotifications(const String16&, const sp<LocalRegistrationCallback>&) { return INVALID_OPERATION; } -status_t ServiceManager::unregisterForNotifications(const String16&, +status_t FakeServiceManager::unregisterForNotifications(const String16&, const sp<LocalRegistrationCallback>&) { return INVALID_OPERATION; } -std::vector<IServiceManager::ServiceDebugInfo> ServiceManager::getServiceDebugInfo() { +std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDebugInfo() { std::vector<IServiceManager::ServiceDebugInfo> ret; return ret; } -void ServiceManager::clear() { +void FakeServiceManager::clear() { mNameToService.clear(); } } // namespace android diff --git a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h index ba6bb7d95b..97add24ac8 100644 --- a/libs/fakeservicemanager/include/fakeservicemanager/ServiceManager.h +++ b/libs/fakeservicemanager/include/fakeservicemanager/FakeServiceManager.h @@ -28,9 +28,9 @@ namespace android { * A local host simple implementation of IServiceManager, that does not * communicate over binder. */ -class ServiceManager : public IServiceManager { +class FakeServiceManager : public IServiceManager { public: - ServiceManager(); + FakeServiceManager(); sp<IBinder> getService( const String16& name) const override; diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp index 8682c1c795..6fc21c65d1 100644 --- a/libs/fakeservicemanager/test_sm.cpp +++ b/libs/fakeservicemanager/test_sm.cpp @@ -21,14 +21,14 @@ #include <binder/ProcessState.h> #include <binder/IServiceManager.h> -#include "ServiceManager.h" +#include "fakeservicemanager/FakeServiceManager.h" using android::sp; using android::BBinder; using android::IBinder; using android::OK; using android::status_t; -using android::ServiceManager; +using android::FakeServiceManager; using android::String16; using android::IServiceManager; using testing::ElementsAre; @@ -45,19 +45,19 @@ static sp<IBinder> getBinder() { } TEST(AddService, HappyHappy) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); } TEST(AddService, SadNullBinder) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), nullptr, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), android::UNEXPECTED_NULL); } TEST(AddService, HappyOverExistingService) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, @@ -65,7 +65,7 @@ TEST(AddService, HappyOverExistingService) { } TEST(AddService, HappyClearAddedService) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); EXPECT_NE(sm->getService(String16("foo")), nullptr); @@ -74,7 +74,7 @@ TEST(AddService, HappyClearAddedService) { } TEST(GetService, HappyHappy) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); sp<IBinder> service = getBinder(); EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, @@ -84,13 +84,13 @@ TEST(GetService, HappyHappy) { } TEST(GetService, NonExistant) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->getService(String16("foo")), nullptr); } TEST(ListServices, AllServices) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK); @@ -109,13 +109,13 @@ TEST(ListServices, AllServices) { } TEST(WaitForService, NonExistant) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_EQ(sm->waitForService(String16("foo")), nullptr); } TEST(WaitForService, HappyHappy) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); sp<IBinder> service = getBinder(); EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, @@ -125,13 +125,13 @@ TEST(WaitForService, HappyHappy) { } TEST(IsDeclared, NonExistant) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); EXPECT_FALSE(sm->isDeclared(String16("foo"))); } TEST(IsDeclared, HappyHappy) { - auto sm = new ServiceManager(); + auto sm = new FakeServiceManager(); sp<IBinder> service = getBinder(); EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/, diff --git a/libs/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp index 6b3b6c49e5..91bf7bc5b6 100644 --- a/libs/ftl/optional_test.cpp +++ b/libs/ftl/optional_test.cpp @@ -164,6 +164,46 @@ TEST(Optional, AndThen) { })); } +TEST(Optional, OrElse) { + // Non-empty. + { + const Optional opt = false; + EXPECT_EQ(false, opt.or_else([] { return Optional(true); })); + EXPECT_EQ('x', Optional('x').or_else([] { return std::make_optional('y'); })); + } + + // Empty. + { + const Optional<int> opt; + EXPECT_EQ(123, opt.or_else([]() -> Optional<int> { return 123; })); + EXPECT_EQ("abc"s, Optional<std::string>().or_else([] { return Optional("abc"s); })); + } + { + bool empty = false; + EXPECT_EQ(Optional<float>(), Optional<float>().or_else([&empty]() -> Optional<float> { + empty = true; + return std::nullopt; + })); + EXPECT_TRUE(empty); + } + + // Chaining. + using StringVector = StaticVector<std::string, 3>; + EXPECT_EQ(999, Optional(StaticVector{"1"s, "0"s, "0"s}) + .and_then([](StringVector&& v) -> Optional<StringVector> { + if (v.push_back("0"s)) return v; + return {}; + }) + .or_else([] { + return Optional(StaticVector{"9"s, "9"s, "9"s}); + }) + .transform([](const StringVector& v) { + return std::accumulate(v.begin(), v.end(), std::string()); + }) + .and_then(parse_int) + .or_else([] { return Optional(-1); })); +} + // Comparison. namespace { diff --git a/libs/graphicsenv/OWNERS b/libs/graphicsenv/OWNERS index 347c4e0db1..1db8cbe52f 100644 --- a/libs/graphicsenv/OWNERS +++ b/libs/graphicsenv/OWNERS @@ -1,10 +1,4 @@ -abdolrashidi@google.com -cclao@google.com chrisforbes@google.com cnorthrop@google.com ianelliott@google.com -lfy@google.com lpy@google.com -romanl@google.com -vantablack@google.com -yuxinhu@google.com diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 9d82c143f5..821dd37a85 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -139,6 +139,11 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } +void BLASTBufferItemConsumer::resizeFrameEventHistory(size_t newSize) { + Mutex::Autolock lock(mMutex); + mFrameEventHistory.resize(newSize); +} + BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame) : mSurfaceControl(nullptr), mSize(1, 1), @@ -486,6 +491,17 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, mSyncedFrameNumbers.erase(callbackId.framenumber); } +static ui::Size getBufferSize(const BufferItem& item) { + uint32_t bufWidth = item.mGraphicBuffer->getWidth(); + uint32_t bufHeight = item.mGraphicBuffer->getHeight(); + + // Take the buffer's orientation into account + if (item.mTransform & ui::Transform::ROT_90) { + std::swap(bufWidth, bufHeight); + } + return ui::Size(bufWidth, bufHeight); +} + status_t BLASTBufferQueue::acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) { // Check if we have frames available and we have not acquired the maximum number of buffers. @@ -563,7 +579,12 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( // Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback. incStrong((void*)transactionCallbackThunk); - mSize = mRequestedSize; + // Only update mSize for destination bounds if the incoming buffer matches the requested size. + // Otherwise, it could cause stretching since the destination bounds will update before the + // buffer with the new size is acquired. + if (mRequestedSize == getBufferSize(bufferItem)) { + mSize = mRequestedSize; + } Rect crop = computeCrop(bufferItem); mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(), bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, @@ -834,14 +855,7 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { return false; } - uint32_t bufWidth = item.mGraphicBuffer->getWidth(); - uint32_t bufHeight = item.mGraphicBuffer->getHeight(); - - // Take the buffer's orientation into account - if (item.mTransform & ui::Transform::ROT_90) { - std::swap(bufWidth, bufHeight); - } - ui::Size bufferSize(bufWidth, bufHeight); + ui::Size bufferSize = getBufferSize(item); if (mRequestedSize != mSize && mRequestedSize == bufferSize) { return false; } @@ -1060,8 +1074,9 @@ public: // can be non-blocking when the producer is in the client process. class BBQBufferQueueProducer : public BufferQueueProducer { public: - BBQBufferQueueProducer(const sp<BufferQueueCore>& core) - : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {} + BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq) + : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/), + mBLASTBufferQueue(std::move(bbq)) {} status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { @@ -1073,6 +1088,26 @@ public: producerControlledByApp, output); } + // We want to resize the frame history when changing the size of the buffer queue + status_t setMaxDequeuedBufferCount(int maxDequeuedBufferCount) override { + int maxBufferCount; + status_t status = BufferQueueProducer::setMaxDequeuedBufferCount(maxDequeuedBufferCount, + &maxBufferCount); + // if we can't determine the max buffer count, then just skip growing the history size + if (status == OK) { + size_t newFrameHistorySize = maxBufferCount + 2; // +2 because triple buffer rendering + // optimize away resizing the frame history unless it will grow + if (newFrameHistorySize > FrameEventHistory::INITIAL_MAX_FRAME_HISTORY) { + sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); + if (bbq != nullptr) { + ALOGV("increasing frame history size to %zu", newFrameHistorySize); + bbq->resizeFrameEventHistory(newFrameHistorySize); + } + } + } + return status; + } + int query(int what, int* value) override { if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) { *value = 1; @@ -1080,6 +1115,9 @@ public: } return BufferQueueProducer::query(what, value); } + +private: + const wp<BLASTBufferQueue> mBLASTBufferQueue; }; // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer. @@ -1094,7 +1132,7 @@ void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer sp<BufferQueueCore> core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore"); - sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core)); + sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this)); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BLASTBufferQueue: failed to create BBQBufferQueueProducer"); @@ -1107,6 +1145,16 @@ void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer *outConsumer = consumer; } +void BLASTBufferQueue::resizeFrameEventHistory(size_t newSize) { + // This can be null during creation of the buffer queue, but resizing won't do anything at that + // point in time, so just ignore. This can go away once the class relationships and lifetimes of + // objects are cleaned up with a major refactor of BufferQueue as a whole. + if (mBufferItemConsumer != nullptr) { + std::unique_lock _lock{mMutex}; + mBufferItemConsumer->resizeFrameEventHistory(newSize); + } +} + PixelFormat BLASTBufferQueue::convertBufferFormat(PixelFormat& format) { PixelFormat convertedFormat = format; switch (format) { diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 5fe5e71db9..9eb1a9f526 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -119,6 +119,12 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { status_t BufferQueueProducer::setMaxDequeuedBufferCount( int maxDequeuedBuffers) { + int maxBufferCount; + return setMaxDequeuedBufferCount(maxDequeuedBuffers, &maxBufferCount); +} + +status_t BufferQueueProducer::setMaxDequeuedBufferCount(int maxDequeuedBuffers, + int* maxBufferCount) { ATRACE_CALL(); BQ_LOGV("setMaxDequeuedBufferCount: maxDequeuedBuffers = %d", maxDequeuedBuffers); @@ -134,6 +140,8 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( return NO_INIT; } + *maxBufferCount = mCore->getMaxBufferCountLocked(); + if (maxDequeuedBuffers == mCore->mMaxDequeuedBufferCount) { return NO_ERROR; } @@ -183,6 +191,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( return BAD_VALUE; } mCore->mMaxDequeuedBufferCount = maxDequeuedBuffers; + *maxBufferCount = mCore->getMaxBufferCountLocked(); VALIDATE_CONSISTENCY(); if (delta < 0) { listener = mCore->mConsumerListener; diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 99bf6badee..46fb068dee 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -15,8 +15,10 @@ */ // #define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <gui/Choreographer.h> +#include <gui/TraceUtils.h> #include <jni.h> #undef LOG_TAG @@ -297,6 +299,8 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t mLastVsyncEventData = vsyncEventData; for (const auto& cb : callbacks) { if (cb.vsyncCallback != nullptr) { + ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64, + vsyncEventData.preferredVsyncId()); const ChoreographerFrameCallbackDataImpl frameCallbackData = createFrameCallbackData(timestamp); registerStartTime(); @@ -306,8 +310,10 @@ void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t cb.data); mInCallback = false; } else if (cb.callback64 != nullptr) { + ATRACE_FORMAT("AChoreographer_frameCallback64"); cb.callback64(timestamp, cb.data); } else if (cb.callback != nullptr) { + ATRACE_FORMAT("AChoreographer_frameCallback"); cb.callback(timestamp, cb.data); } } diff --git a/libs/gui/FrameTimestamps.cpp b/libs/gui/FrameTimestamps.cpp index e2ea3f9ab1..f3eb4e83aa 100644 --- a/libs/gui/FrameTimestamps.cpp +++ b/libs/gui/FrameTimestamps.cpp @@ -168,10 +168,11 @@ struct FrameNumberEqual { } // namespace -const size_t FrameEventHistory::MAX_FRAME_HISTORY = +const size_t FrameEventHistory::INITIAL_MAX_FRAME_HISTORY = sysprop::LibGuiProperties::frame_event_history_size().value_or(8); -FrameEventHistory::FrameEventHistory() : mFrames(std::vector<FrameEvents>(MAX_FRAME_HISTORY)) {} +FrameEventHistory::FrameEventHistory() + : mFrames(std::vector<FrameEvents>(INITIAL_MAX_FRAME_HISTORY)) {} FrameEventHistory::~FrameEventHistory() = default; @@ -227,7 +228,6 @@ void FrameEventHistory::dump(std::string& outString) const { } } - // ============================================================================ // ProducerFrameEventHistory // ============================================================================ @@ -273,6 +273,13 @@ void ProducerFrameEventHistory::applyDelta( const FrameEventHistoryDelta& delta) { mCompositorTiming = delta.mCompositorTiming; + // Deltas should have enough reserved capacity for the consumer-side, therefore if there's a + // different capacity, we re-sized on the consumer side and now need to resize on the producer + // side. + if (delta.mDeltas.capacity() > mFrames.capacity()) { + resize(delta.mDeltas.capacity()); + } + for (auto& d : delta.mDeltas) { // Avoid out-of-bounds access. if (CC_UNLIKELY(d.mIndex >= mFrames.size())) { @@ -349,13 +356,48 @@ std::shared_ptr<FenceTime> ProducerFrameEventHistory::createFenceTime( return std::make_shared<FenceTime>(fence); } +void ProducerFrameEventHistory::resize(size_t newSize) { + // we don't want to drop events by resizing too small, so don't resize in the negative direction + if (newSize <= mFrames.size()) { + return; + } + + // This algorithm for resizing needs to be the same as ConsumerFrameEventHistory::resize, + // because the indexes need to match when communicating the FrameEventDeltas. + + // We need to find the oldest frame, because that frame needs to move to index 0 in the new + // frame history. + size_t oldestFrameIndex = 0; + size_t oldestFrameNumber = INT32_MAX; + for (size_t i = 0; i < mFrames.size(); ++i) { + if (mFrames[i].frameNumber < oldestFrameNumber && mFrames[i].valid) { + oldestFrameNumber = mFrames[i].frameNumber; + oldestFrameIndex = i; + } + } + + // move the existing frame information into a new vector, so that the oldest frames are at + // index 0, and the latest frames are at the end of the vector + std::vector<FrameEvents> newFrames(newSize); + size_t oldI = oldestFrameIndex; + size_t newI = 0; + do { + if (mFrames[oldI].valid) { + newFrames[newI++] = std::move(mFrames[oldI]); + } + oldI = (oldI + 1) % mFrames.size(); + } while (oldI != oldestFrameIndex); + + mFrames = std::move(newFrames); + mAcquireOffset = 0; // this is just a hint, so setting this to anything is fine +} // ============================================================================ // ConsumerFrameEventHistory // ============================================================================ ConsumerFrameEventHistory::ConsumerFrameEventHistory() - : mFramesDirty(std::vector<FrameEventDirtyFields>(MAX_FRAME_HISTORY)) {} + : mFramesDirty(std::vector<FrameEventDirtyFields>(INITIAL_MAX_FRAME_HISTORY)) {} ConsumerFrameEventHistory::~ConsumerFrameEventHistory() = default; @@ -489,6 +531,36 @@ void ConsumerFrameEventHistory::getAndResetDelta( } } +void ConsumerFrameEventHistory::resize(size_t newSize) { + // we don't want to drop events by resizing too small, so don't resize in the negative direction + if (newSize <= mFrames.size()) { + return; + } + + // This algorithm for resizing needs to be the same as ProducerFrameEventHistory::resize, + // because the indexes need to match when communicating the FrameEventDeltas. + + // move the existing frame information into a new vector, so that the oldest frames are at + // index 0, and the latest frames are towards the end of the vector + std::vector<FrameEvents> newFrames(newSize); + std::vector<FrameEventDirtyFields> newFramesDirty(newSize); + size_t oldestFrameIndex = mQueueOffset; + size_t oldI = oldestFrameIndex; + size_t newI = 0; + do { + if (mFrames[oldI].valid) { + newFrames[newI] = std::move(mFrames[oldI]); + newFramesDirty[newI] = mFramesDirty[oldI]; + newI += 1; + } + oldI = (oldI + 1) % mFrames.size(); + } while (oldI != oldestFrameIndex); + + mFrames = std::move(newFrames); + mFramesDirty = std::move(newFramesDirty); + mQueueOffset = newI; + mCompositionOffset = 0; // this is just a hint, so setting this to anything is fine +} // ============================================================================ // FrameEventsDelta @@ -558,8 +630,7 @@ status_t FrameEventsDelta::flatten(void*& buffer, size_t& size, int*& fds, return NO_MEMORY; } - if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY || - mIndex > std::numeric_limits<uint16_t>::max()) { + if (mIndex >= UINT8_MAX || mIndex < 0) { return BAD_VALUE; } @@ -601,7 +672,7 @@ status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, uint16_t temp16 = 0; FlattenableUtils::read(buffer, size, temp16); mIndex = temp16; - if (mIndex >= FrameEventHistory::MAX_FRAME_HISTORY) { + if (mIndex >= UINT8_MAX) { return BAD_VALUE; } uint8_t temp8 = 0; @@ -627,6 +698,25 @@ status_t FrameEventsDelta::unflatten(void const*& buffer, size_t& size, return NO_ERROR; } +uint64_t FrameEventsDelta::getFrameNumber() const { + return mFrameNumber; +} + +bool FrameEventsDelta::getLatchTime(nsecs_t* latchTime) const { + if (mLatchTime == FrameEvents::TIMESTAMP_PENDING) { + return false; + } + *latchTime = mLatchTime; + return true; +} + +bool FrameEventsDelta::getDisplayPresentFence(sp<Fence>* fence) const { + if (mDisplayPresentFence.fence == Fence::NO_FENCE) { + return false; + } + *fence = mDisplayPresentFence.fence; + return true; +} // ============================================================================ // FrameEventHistoryDelta @@ -665,7 +755,7 @@ size_t FrameEventHistoryDelta::getFdCount() const { status_t FrameEventHistoryDelta::flatten( void*& buffer, size_t& size, int*& fds, size_t& count) const { - if (mDeltas.size() > FrameEventHistory::MAX_FRAME_HISTORY) { + if (mDeltas.size() > UINT8_MAX) { return BAD_VALUE; } if (size < getFlattenedSize()) { @@ -695,7 +785,7 @@ status_t FrameEventHistoryDelta::unflatten( uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); - if (deltaCount > FrameEventHistory::MAX_FRAME_HISTORY) { + if (deltaCount > UINT8_MAX) { return BAD_VALUE; } mDeltas.resize(deltaCount); @@ -708,5 +798,12 @@ status_t FrameEventHistoryDelta::unflatten( return NO_ERROR; } +std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::begin() const { + return mDeltas.begin(); +} + +std::vector<FrameEventsDelta>::const_iterator FrameEventHistoryDelta::end() const { + return mDeltas.end(); +} } // namespace android diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index a6276e500c..b391337d1d 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -74,7 +74,7 @@ layer_state_t::layer_state_t() surfaceDamageRegion(), api(-1), colorTransform(mat4()), - bgColorAlpha(0), + bgColor(0), bgColorDataspace(ui::Dataspace::UNKNOWN), colorSpaceAgnostic(false), shadowRadius(0.0f), @@ -140,7 +140,10 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, cornerRadius); SAFE_PARCEL(output.writeUint32, backgroundBlurRadius); SAFE_PARCEL(output.writeParcelable, metadata); - SAFE_PARCEL(output.writeFloat, bgColorAlpha); + SAFE_PARCEL(output.writeFloat, bgColor.r); + SAFE_PARCEL(output.writeFloat, bgColor.g); + SAFE_PARCEL(output.writeFloat, bgColor.b); + SAFE_PARCEL(output.writeFloat, bgColor.a); SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(bgColorDataspace)); SAFE_PARCEL(output.writeBool, colorSpaceAgnostic); SAFE_PARCEL(output.writeVectorSize, listeners); @@ -189,6 +192,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, trustedPresentationListener); SAFE_PARCEL(output.writeFloat, currentSdrHdrRatio); SAFE_PARCEL(output.writeFloat, desiredSdrHdrRatio); + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)) return NO_ERROR; } @@ -258,7 +262,14 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readUint32, &backgroundBlurRadius); SAFE_PARCEL(input.readParcelable, &metadata); - SAFE_PARCEL(input.readFloat, &bgColorAlpha); + SAFE_PARCEL(input.readFloat, &tmpFloat); + bgColor.r = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + bgColor.g = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + bgColor.b = tmpFloat; + SAFE_PARCEL(input.readFloat, &tmpFloat); + bgColor.a = tmpFloat; SAFE_PARCEL(input.readUint32, &tmpUint32); bgColorDataspace = static_cast<ui::Dataspace>(tmpUint32); SAFE_PARCEL(input.readBool, &colorSpaceAgnostic); @@ -328,6 +339,10 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.readFloat, &tmpFloat); desiredSdrHdrRatio = tmpFloat; + int32_t tmpInt32; + SAFE_PARCEL(input.readInt32, &tmpInt32); + cachingHint = static_cast<gui::CachingHint>(tmpInt32); + return NO_ERROR; } @@ -580,6 +595,10 @@ void layer_state_t::merge(const layer_state_t& other) { desiredSdrHdrRatio = other.desiredSdrHdrRatio; currentSdrHdrRatio = other.currentSdrHdrRatio; } + if (other.what & eCachingHintChanged) { + what |= eCachingHintChanged; + cachingHint = other.cachingHint; + } if (other.what & eHdrMetadataChanged) { what |= eHdrMetadataChanged; hdrMetadata = other.hdrMetadata; @@ -609,8 +628,7 @@ void layer_state_t::merge(const layer_state_t& other) { } if (other.what & eBackgroundColorChanged) { what |= eBackgroundColorChanged; - color.rgb = other.color.rgb; - bgColorAlpha = other.bgColorAlpha; + bgColor = other.bgColor; bgColorDataspace = other.bgColorDataspace; } if (other.what & eMetadataChanged) { @@ -731,6 +749,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { CHECK_DIFF(diff, eDataspaceChanged, other, dataspace); CHECK_DIFF2(diff, eExtendedRangeBrightnessChanged, other, currentSdrHdrRatio, desiredSdrHdrRatio); + CHECK_DIFF(diff, eCachingHintChanged, other, cachingHint); CHECK_DIFF(diff, eHdrMetadataChanged, other, hdrMetadata); if (other.what & eSurfaceDamageRegionChanged && (!surfaceDamageRegion.hasSameRects(other.surfaceDamageRegion))) { @@ -742,7 +761,7 @@ uint64_t layer_state_t::diff(const layer_state_t& other) const { 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); + CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace); if (other.what & eMetadataChanged) diff |= eMetadataChanged; CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius); CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor); diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index c5089173bd..2f5830d362 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1363,7 +1363,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFlags (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)) { + (mask & layer_state_t::eLayerIsDisplayDecoration) || + (mask & layer_state_t::eLayerIsRefreshRateIndicator)) { s->what |= layer_state_t::eFlagsChanged; } s->flags &= ~mask; @@ -1560,8 +1561,8 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBackg } s->what |= layer_state_t::eBackgroundColorChanged; - s->color.rgb = color; - s->bgColorAlpha = alpha; + s->bgColor.rgb = color; + s->bgColor.a = alpha; s->bgColorDataspace = dataspace; registerSurfaceControlForCallback(sc); @@ -1729,6 +1730,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setExten return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setCachingHint( + const sp<SurfaceControl>& sc, gui::CachingHint cachingHint) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eCachingHintChanged; + s->cachingHint = cachingHint; + + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setHdrMetadata( const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata) { layer_state_t* s = getLayerState(sc); diff --git a/libs/gui/VsyncEventData.cpp b/libs/gui/VsyncEventData.cpp index 23f0921e99..76c60c23c4 100644 --- a/libs/gui/VsyncEventData.cpp +++ b/libs/gui/VsyncEventData.cpp @@ -23,6 +23,9 @@ namespace android::gui { +static_assert(VsyncEventData::kFrameTimelinesLength == 7, + "Must update value in DisplayEventReceiver.java#FRAME_TIMELINES_LENGTH (and here)"); + int64_t VsyncEventData::preferredVsyncId() const { return frameTimelines[preferredFrameTimelineIndex].vsyncId; } diff --git a/libs/gui/WindowInfosListenerReporter.cpp b/libs/gui/WindowInfosListenerReporter.cpp index 01e865da6a..2b34a0fc47 100644 --- a/libs/gui/WindowInfosListenerReporter.cpp +++ b/libs/gui/WindowInfosListenerReporter.cpp @@ -62,6 +62,10 @@ status_t WindowInfosListenerReporter::removeWindowInfosListener( status_t status = OK; { std::scoped_lock lock(mListenersMutex); + if (mWindowInfosListeners.find(windowInfosListener) == mWindowInfosListeners.end()) { + return status; + } + if (mWindowInfosListeners.size() == 1) { binder::Status s = surfaceComposer->removeWindowInfosListener(this); status = statusTFromBinderStatus(s); diff --git a/libs/gui/aidl/android/gui/CachingHint.aidl b/libs/gui/aidl/android/gui/CachingHint.aidl new file mode 100644 index 0000000000..b35c79547f --- /dev/null +++ b/libs/gui/aidl/android/gui/CachingHint.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; + +/* + * Hint for configuring caching behavior for a layer + * @hide + */ +@Backing(type="int") +enum CachingHint { + // Caching is disabled. A layer may explicitly disable caching for + // improving image quality for some scenes. + Disabled = 0, + // Caching is enabled. A layer is cacheable by default. + Enabled = 1 +} diff --git a/libs/gui/bufferqueue/1.0/Conversion.cpp b/libs/gui/bufferqueue/1.0/Conversion.cpp index 55462c3b1f..96679549b2 100644 --- a/libs/gui/bufferqueue/1.0/Conversion.cpp +++ b/libs/gui/bufferqueue/1.0/Conversion.cpp @@ -725,12 +725,7 @@ status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, // These were written as uint8_t for alignment. uint8_t temp = 0; FlattenableUtils::read(buffer, size, temp); - size_t index = static_cast<size_t>(temp); - if (index >= ::android::FrameEventHistory::MAX_FRAME_HISTORY) { - return BAD_VALUE; - } - t->index = static_cast<uint32_t>(index); - + t->index = static_cast<uint32_t>(temp); FlattenableUtils::read(buffer, size, temp); t->addPostCompositeCalled = static_cast<bool>(temp); FlattenableUtils::read(buffer, size, temp); @@ -786,8 +781,7 @@ status_t unflatten(HGraphicBufferProducer::FrameEventsDelta* t, status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, void*& buffer, size_t& size, int*& fds, size_t numFds) { // Check that t.index is within a valid range. - if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY) - || t.index > std::numeric_limits<uint8_t>::max()) { + if (t.index > UINT8_MAX || t.index < 0) { return BAD_VALUE; } @@ -887,8 +881,7 @@ status_t unflatten( uint32_t deltaCount = 0; FlattenableUtils::read(buffer, size, deltaCount); - if (static_cast<size_t>(deltaCount) > - ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + if (deltaCount > UINT8_MAX) { return BAD_VALUE; } t->deltas.resize(deltaCount); @@ -919,7 +912,7 @@ status_t unflatten( status_t flatten( HGraphicBufferProducer::FrameEventHistoryDelta const& t, void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + if (t.deltas.size() > UINT8_MAX) { return BAD_VALUE; } if (size < getFlattenedSize(t)) { diff --git a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp index cee1b811a4..f684874aec 100644 --- a/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp +++ b/libs/gui/bufferqueue/1.0/H2BGraphicBufferProducer.cpp @@ -725,8 +725,7 @@ inline status_t flatten(HGraphicBufferProducer::FrameEventsDelta const& t, std::vector<native_handle_t*>* nh, void*& buffer, size_t& size, int*& fds, size_t numFds) { // Check that t.index is within a valid range. - if (t.index >= static_cast<uint32_t>(FrameEventHistory::MAX_FRAME_HISTORY) - || t.index > std::numeric_limits<uint8_t>::max()) { + if (t.index > UINT8_MAX || t.index < 0) { return BAD_VALUE; } @@ -829,7 +828,7 @@ inline status_t flatten( HGraphicBufferProducer::FrameEventHistoryDelta const& t, std::vector<std::vector<native_handle_t*> >* nh, void*& buffer, size_t& size, int*& fds, size_t& numFds) { - if (t.deltas.size() > ::android::FrameEventHistory::MAX_FRAME_HISTORY) { + if (t.deltas.size() > UINT8_MAX) { return BAD_VALUE; } if (size < getFlattenedSize(t)) { diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index b9e06473bf..69e9f8a4fb 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -54,6 +54,8 @@ public: nsecs_t dequeueReadyTime) EXCLUDES(mMutex); void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) EXCLUDES(mMutex); + void resizeFrameEventHistory(size_t newSize); + protected: void onSidebandStreamChanged() override EXCLUDES(mMutex); @@ -123,6 +125,7 @@ public: private: friend class BLASTBufferQueueHelper; + friend class BBQBufferQueueProducer; // can't be copied BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); @@ -130,6 +133,8 @@ private: void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer); + void resizeFrameEventHistory(size_t newSize); + status_t acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 0ad3075a4d..1d13dab623 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -202,6 +202,11 @@ public: // See IGraphicBufferProducer::setAutoPrerotation virtual status_t setAutoPrerotation(bool autoPrerotation); +protected: + // see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the + // total maximum buffer count for the buffer queue (dequeued AND acquired) + status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers, int* maxBufferCount); + private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp<IBinder>& who); diff --git a/libs/gui/include/gui/FrameTimestamps.h b/libs/gui/include/gui/FrameTimestamps.h index c08a9b1b6c..3d1be4d2eb 100644 --- a/libs/gui/include/gui/FrameTimestamps.h +++ b/libs/gui/include/gui/FrameTimestamps.h @@ -97,9 +97,11 @@ public: void checkFencesForCompletion(); void dump(std::string& outString) const; - static const size_t MAX_FRAME_HISTORY; + static const size_t INITIAL_MAX_FRAME_HISTORY; protected: + void resize(size_t newSize); + std::vector<FrameEvents> mFrames; CompositorTiming mCompositorTiming; @@ -138,6 +140,8 @@ protected: virtual std::shared_ptr<FenceTime> createFenceTime( const sp<Fence>& fence) const; + void resize(size_t newSize); + size_t mAcquireOffset{0}; // The consumer updates it's timelines in Layer and SurfaceFlinger since @@ -208,6 +212,8 @@ public: void getAndResetDelta(FrameEventHistoryDelta* delta); + void resize(size_t newSize); + private: void getFrameDelta(FrameEventHistoryDelta* delta, const std::vector<FrameEvents>::iterator& frame); @@ -250,6 +256,10 @@ public: status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + uint64_t getFrameNumber() const; + bool getLatchTime(nsecs_t* latchTime) const; + bool getDisplayPresentFence(sp<Fence>* fence) const; + private: static constexpr size_t minFlattenedSize(); @@ -310,6 +320,9 @@ public: status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + std::vector<FrameEventsDelta>::const_iterator begin() const; + std::vector<FrameEventsDelta>::const_iterator end() const; + private: static constexpr size_t minFlattenedSize(); diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index ae56f9fdb5..1e67225a4e 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -16,6 +16,7 @@ #pragma once +#include <android/gui/CachingHint.h> #include <android/gui/DisplayBrightness.h> #include <android/gui/FrameTimelineInfo.h> #include <android/gui/IDisplayEventConnection.h> diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index ddaf473855..6e3be5cef8 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -160,6 +160,7 @@ struct layer_state_t { // This is needed to maintain compatibility for SurfaceView scaling behavior. // See SurfaceView scaling behavior for more details. eIgnoreDestinationFrame = 0x400, + eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR }; enum { @@ -172,7 +173,7 @@ struct layer_state_t { eFlagsChanged = 0x00000040, eLayerStackChanged = 0x00000080, eFlushJankData = 0x00000100, - /* unused = 0x00000200, */ + eCachingHintChanged = 0x00000200, eDimmingEnabledChanged = 0x00000400, eShadowRadiusChanged = 0x00000800, eRenderBorderChanged = 0x00001000, @@ -211,7 +212,8 @@ struct layer_state_t { eStretchChanged = 0x2000'00000000, eTrustedOverlayChanged = 0x4000'00000000, eDropInputModeChanged = 0x8000'00000000, - eExtendedRangeBrightnessChanged = 0x10000'00000000 + eExtendedRangeBrightnessChanged = 0x10000'00000000, + }; layer_state_t(); @@ -225,9 +227,9 @@ struct layer_state_t { bool hasBufferChanges() const; // Layer hierarchy updates. - static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eBackgroundColorChanged | - layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged | - layer_state_t::eReparent; + static constexpr uint64_t HIERARCHY_CHANGES = layer_state_t::eLayerChanged | + layer_state_t::eRelativeLayerChanged | layer_state_t::eReparent | + layer_state_t::eLayerStackChanged; // Geometry updates. static constexpr uint64_t GEOMETRY_CHANGES = layer_state_t::eBufferCropChanged | @@ -263,9 +265,8 @@ struct layer_state_t { static constexpr uint64_t AFFECTS_CHILDREN = layer_state_t::GEOMETRY_CHANGES | layer_state_t::HIERARCHY_CHANGES | layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged | layer_state_t::eCornerRadiusChanged | - layer_state_t::eFlagsChanged | layer_state_t::eLayerStackChanged | - layer_state_t::eTrustedOverlayChanged | layer_state_t::eFrameRateChanged | - layer_state_t::eFixedTransformHintChanged; + layer_state_t::eFlagsChanged | layer_state_t::eTrustedOverlayChanged | + layer_state_t::eFrameRateChanged | layer_state_t::eFixedTransformHintChanged; // Changes affecting data sent to input. static constexpr uint64_t INPUT_CHANGES = layer_state_t::GEOMETRY_CHANGES | @@ -332,7 +333,7 @@ struct layer_state_t { // The following refer to the alpha, and dataspace, respectively of // the background color layer - float bgColorAlpha; + half4 bgColor; ui::Dataspace bgColorDataspace; // A color space agnostic layer means the color of this layer can be @@ -391,6 +392,8 @@ struct layer_state_t { float currentSdrHdrRatio = 1.f; float desiredSdrHdrRatio = 1.f; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; + TrustedPresentationThresholds trustedPresentationThresholds; TrustedPresentationListener trustedPresentationListener; }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 44e78ec0c4..d431b4381a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -566,6 +566,7 @@ public: Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace); Transaction& setExtendedRangeBrightness(const sp<SurfaceControl>& sc, float currentBufferRatio, float desiredRatio); + Transaction& setCachingHint(const sp<SurfaceControl>& sc, gui::CachingHint cachingHint); Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata); Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc, const Region& surfaceDamageRegion); diff --git a/libs/gui/view/Surface.cpp b/libs/gui/view/Surface.cpp index 1bfe4624f1..198908d334 100644 --- a/libs/gui/view/Surface.cpp +++ b/libs/gui/view/Surface.cpp @@ -16,17 +16,59 @@ #define LOG_TAG "Surface" -#include <gui/view/Surface.h> - +#include <android/binder_libbinder.h> +#include <android/binder_parcel.h> +#include <android/native_window.h> #include <binder/Parcel.h> - -#include <utils/Log.h> - #include <gui/IGraphicBufferProducer.h> +#include <gui/Surface.h> +#include <gui/view/Surface.h> +#include <system/window.h> +#include <utils/Log.h> namespace android { namespace view { +// Since this is a parcelable utility and we want to keep the wire format stable, only build this +// when building the system libgui to detect any issues loading the wrong libgui from +// libnativewindow + +#if (!defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__)) + +extern "C" status_t android_view_Surface_writeToParcel(ANativeWindow* _Nonnull window, + Parcel* _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 + android::view::Surface shimSurface; + shimSurface.graphicBufferProducer = android::Surface::getIGraphicBufferProducer(window); + shimSurface.surfaceControlHandle = android::Surface::getSurfaceControlHandle(window); + return shimSurface.writeToParcel(parcel); +} + +extern "C" status_t android_view_Surface_readFromParcel( + const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow) { + // Use a android::view::Surface to unparcel the window + android::view::Surface shimSurface; + status_t ret = shimSurface.readFromParcel(parcel); + if (ret != OK) { + ALOGE("%s: Error: Failed to create android::view::Surface from AParcel", __FUNCTION__); + return STATUS_BAD_VALUE; + } + auto surface = sp<android::Surface>::make(shimSurface.graphicBufferProducer, false, + shimSurface.surfaceControlHandle); + ANativeWindow* anw = surface.get(); + ANativeWindow_acquire(anw); + *outWindow = anw; + return STATUS_OK; +} + +#endif + status_t Surface::writeToParcel(Parcel* parcel) const { return writeToParcel(parcel, false); } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index fd4fc16deb..f38dd98428 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -47,6 +47,7 @@ cc_library { "Input.cpp", "InputDevice.cpp", "InputEventLabels.cpp", + "InputVerifier.cpp", "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", @@ -57,6 +58,7 @@ cc_library { "TouchVideoFrame.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", + "VirtualInputDevice.cpp", "VirtualKeyMap.cpp", ], @@ -67,6 +69,10 @@ cc_library { ], export_header_lib_headers: ["jni_headers"], + generated_headers: [ + "toolbox_input_labels", + ], + shared_libs: [ "libbase", "libcutils", @@ -124,6 +130,8 @@ cc_library { enabled: false, }, include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi", "frameworks/native/libs/arect/include", ], }, diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 133b260a61..53b22cb883 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -151,10 +151,23 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- +// Due to precision limitations when working with floating points, transforming - namely +// scaling - floating points can lead to minute errors. We round transformed values to approximately +// three decimal places so that values like 0.99997 show up as 1.0. +inline float roundTransformedCoords(float val) { + // Use a power to two to approximate three decimal places to potentially reduce some cycles. + // This should be at least as precise as MotionEvent::ROUNDING_PRECISION. + return std::round(val * 1024.f) / 1024.f; +} + +inline vec2 roundTransformedCoords(vec2 p) { + return {roundTransformedCoords(p.x), roundTransformedCoords(p.y)}; +} + vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { const vec2 transformedXy = transform.transform(xy); const vec2 transformedOrigin = transform.transform(0, 0); - return transformedXy - transformedOrigin; + return roundTransformedCoords(transformedXy - transformedOrigin); } float transformAngle(const ui::Transform& transform, float angleRadians) { @@ -606,12 +619,12 @@ std::optional<ui::Rotation> MotionEvent::getSurfaceRotation() const { float MotionEvent::getXCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); - return vals.x; + return roundTransformedCoords(vals.x); } float MotionEvent::getYCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); - return vals.y; + return roundTransformedCoords(vals.y); } void MotionEvent::setCursorPosition(float x, float y) { @@ -933,7 +946,7 @@ std::string MotionEvent::actionToString(int32_t action) { static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, const vec2& xy) { return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy) - : transform.transform(xy); + : roundTransformedCoords(transform.transform(xy)); } vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index d97c1bb629..4a19227694 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -16,6 +16,9 @@ #include <input/InputEventLabels.h> +#include <linux/input-event-codes.h> +#include <linux/input.h> + #define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } #define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } #define DEFINE_LED(led) { #led, ALED_##led } @@ -480,4 +483,85 @@ std::optional<int> InputEventLookup::getLedByLabel(const char* label) { return lookupValueByLabel(LEDS, label); } +namespace { + +struct label { + const char* name; + int value; +}; + +#define LABEL(constant) \ + { #constant, constant } +#define LABEL_END \ + { nullptr, -1 } + +static struct label ev_key_value_labels[] = { + {"UP", 0}, + {"DOWN", 1}, + {"REPEAT", 2}, + LABEL_END, +}; + +#include "input.h-labels.h" + +#undef LABEL +#undef LABEL_END + +std::string getLabel(const label* labels, int value) { + if (labels == nullptr) return std::to_string(value); + while (labels->name != nullptr && value != labels->value) { + labels++; + } + return labels->name != nullptr ? labels->name : std::to_string(value); +} + +const label* getCodeLabelsForType(int32_t type) { + switch (type) { + case EV_SYN: + return syn_labels; + case EV_KEY: + return key_labels; + case EV_REL: + return rel_labels; + case EV_ABS: + return abs_labels; + case EV_SW: + return sw_labels; + case EV_MSC: + return msc_labels; + case EV_LED: + return led_labels; + case EV_REP: + return rep_labels; + case EV_SND: + return snd_labels; + case EV_FF: + return ff_labels; + case EV_FF_STATUS: + return ff_status_labels; + default: + return nullptr; + } +} + +const label* getValueLabelsForTypeAndCode(int32_t type, int32_t code) { + if (type == EV_KEY) { + return ev_key_value_labels; + } + if (type == EV_MSC && code == ABS_MT_TOOL_TYPE) { + return mt_tool_labels; + } + return nullptr; +} + +} // namespace + +EvdevEventLabel InputEventLookup::getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value) { + return { + .type = getLabel(ev_labels, type), + .code = getLabel(getCodeLabelsForType(type), code), + .value = getLabel(getValueLabelsForTypeAndCode(type, code), value), + }; +} + } // namespace android diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 9f0a314041..311b2441a4 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -5,20 +5,6 @@ // #define LOG_TAG "InputTransport" -//#define LOG_NDEBUG 0 - -// Log debug messages about channel messages (send message, receive message) -#define DEBUG_CHANNEL_MESSAGES 0 - -// Log debug messages whenever InputChannel objects are created/destroyed -static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false; - -// Log debug messages about transport actions -static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; - -// Log debug messages about touch event resampling -#define DEBUG_RESAMPLING 0 - #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -27,6 +13,7 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; #include <sys/types.h> #include <unistd.h> +#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <binder/Parcel.h> #include <cutils/properties.h> @@ -36,6 +23,63 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; #include <input/InputTransport.h> +namespace { + +/** + * Log debug messages about channel messages (send message, receive message). + * Enable this via "adb shell setprop log.tag.InputTransportMessages DEBUG" + * (requires restart) + */ +const bool DEBUG_CHANNEL_MESSAGES = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Messages", ANDROID_LOG_INFO); + +/** + * Log debug messages whenever InputChannel objects are created/destroyed. + * Enable this via "adb shell setprop log.tag.InputTransportLifecycle DEBUG" + * (requires restart) + */ +const bool DEBUG_CHANNEL_LIFECYCLE = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO); + +/** + * Log debug messages relating to the consumer end of the transport channel. + * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart) + */ + +const bool DEBUG_TRANSPORT_CONSUMER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO); + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +/** + * Log debug messages relating to the producer end of the transport channel. + * Enable this via "adb shell setprop log.tag.InputTransportPublisher DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). + */ +bool debugTransportPublisher() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_TRANSPORT_PUBLISHER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO); + return DEBUG_TRANSPORT_PUBLISHER; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO); +} + +/** + * Log debug messages about touch event resampling. + * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG" (requires restart) + */ +const bool DEBUG_RESAMPLING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); + +} // namespace + using android::base::StringPrintf; namespace android { @@ -76,6 +120,14 @@ static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; */ static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; +/** + * Crash if the events that are getting sent to the InputPublisher are inconsistent. + * Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG" + */ +static bool verifyEvents() { + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO); +} + template<typename T> inline static T min(const T& a, const T& b) { return a < b ? a : b; @@ -132,7 +184,7 @@ bool InputMessage::isValid(size_t actualSize) const { return valid; } } - ALOGE("Invalid message type: %" PRIu32, header.type); + ALOGE("Invalid message type: %s", ftl::enum_string(header.type).c_str()); return false; } @@ -322,15 +374,13 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { - if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel constructed: name='%s', fd=%d", getName().c_str(), getFd().get()); - } + ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d", + getName().c_str(), getFd().get()); } InputChannel::~InputChannel() { - if (DEBUG_CHANNEL_LIFECYCLE) { - ALOGD("Input channel destroyed: name='%s', fd=%d", getName().c_str(), getFd().get()); - } + ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d", + getName().c_str(), getFd().get()); } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -375,10 +425,8 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (nWrite < 0) { int error = errno; -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(), - msg->header.type, strerror(error)); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s", + mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -389,16 +437,14 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { } if (size_t(nWrite) != msgLength) { -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", - mName.c_str(), msg->header.type); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, + "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(), + ftl::enum_string(msg->header.type).c_str()); return DEAD_OBJECT; } -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ sent message of type %d", mName.c_str(), msg->header.type); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(), + ftl::enum_string(msg->header.type).c_str()); return OK; } @@ -410,9 +456,8 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { if (nRead < 0) { int error = errno; -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.c_str(), errno); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", + mName.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -423,9 +468,8 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { } if (nRead == 0) { // check for EOF -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.c_str()); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, + "channel '%s' ~ receive message failed because peer was closed", mName.c_str()); return DEAD_OBJECT; } @@ -434,9 +478,8 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { return BAD_VALUE; } -#if DEBUG_CHANNEL_MESSAGES - ALOGD("channel '%s' ~ received message of type %d", mName.c_str(), msg->header.type); -#endif + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(), + ftl::enum_string(msg->header.type).c_str()); return OK; } @@ -492,7 +535,8 @@ base::unique_fd InputChannel::dupFd() const { // --- InputPublisher --- -InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) : mChannel(channel) {} +InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) + : mChannel(channel), mInputVerifier(channel->getName()) {} InputPublisher::~InputPublisher() { } @@ -504,17 +548,19 @@ status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t int32_t metaState, int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime) { if (ATRACE_ENABLED()) { - std::string message = StringPrintf("publishKeyEvent(inputChannel=%s, keyCode=%" PRId32 ")", - mChannel->getName().c_str(), keyCode); + std::string message = + StringPrintf("publishKeyEvent(inputChannel=%s, action=%s, keyCode=%s)", + mChannel->getName().c_str(), KeyEvent::actionToString(action), + KeyEvent::getLabel(keyCode)); ATRACE_NAME(message.c_str()); } - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " - "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," - "downTime=%" PRId64 ", eventTime=%" PRId64, - mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode, - metaState, repeatCount, downTime, eventTime); - } + ALOGD_IF(debugTransportPublisher(), + "channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, " + "action=%s, flags=0x%x, keyCode=%s, scanCode=%d, metaState=0x%x, repeatCount=%d," + "downTime=%" PRId64 ", eventTime=%" PRId64, + mChannel->getName().c_str(), __func__, seq, eventId, deviceId, + inputEventSourceToString(source).c_str(), KeyEvent::actionToString(action), flags, + KeyEvent::getLabel(keyCode), scanCode, metaState, repeatCount, downTime, eventTime); if (!seq) { ALOGE("Attempted to publish a key event with sequence number 0."); @@ -550,24 +596,29 @@ status_t InputPublisher::publishMotionEvent( uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { - std::string message = StringPrintf( - "publishMotionEvent(inputChannel=%s, action=%" PRId32 ")", - mChannel->getName().c_str(), action); + std::string message = StringPrintf("publishMotionEvent(inputChannel=%s, action=%s)", + mChannel->getName().c_str(), + MotionEvent::actionToString(action).c_str()); ATRACE_NAME(message.c_str()); } - if (DEBUG_TRANSPORT_ACTIONS) { + if (verifyEvents()) { + mInputVerifier.processMovement(deviceId, action, pointerCount, pointerProperties, + pointerCoords, flags); + } + if (debugTransportPublisher()) { std::string transformString; transform.dump(transformString, "transform", " "); - ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, " "displayId=%" PRId32 ", " - "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " + "action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, " "metaState=0x%x, buttonState=0x%x, classification=%s," "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", " "pointerCount=%" PRIu32 " \n%s", - mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton, - flags, edgeFlags, metaState, buttonState, - motionClassificationToString(classification), xPrecision, yPrecision, downTime, - eventTime, pointerCount, transformString.c_str()); + mChannel->getName().c_str(), __func__, seq, eventId, deviceId, + inputEventSourceToString(source).c_str(), displayId, + MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags, + metaState, buttonState, motionClassificationToString(classification), xPrecision, + yPrecision, downTime, eventTime, pointerCount, transformString.c_str()); } if (!seq) { @@ -629,6 +680,8 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h mChannel->getName().c_str(), toString(hasFocus)); ATRACE_NAME(message.c_str()); } + ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: seq=%u, id=%d, hasFocus=%s", + mChannel->getName().c_str(), __func__, seq, eventId, toString(hasFocus)); InputMessage msg; msg.header.type = InputMessage::Type::FOCUS; @@ -646,6 +699,9 @@ status_t InputPublisher::publishCaptureEvent(uint32_t seq, int32_t eventId, mChannel->getName().c_str(), toString(pointerCaptureEnabled)); ATRACE_NAME(message.c_str()); } + ALOGD_IF(debugTransportPublisher(), + "channel '%s' publisher ~ %s: seq=%u, id=%d, pointerCaptureEnabled=%s", + mChannel->getName().c_str(), __func__, seq, eventId, toString(pointerCaptureEnabled)); InputMessage msg; msg.header.type = InputMessage::Type::CAPTURE; @@ -663,6 +719,9 @@ status_t InputPublisher::publishDragEvent(uint32_t seq, int32_t eventId, float x mChannel->getName().c_str(), x, y, toString(isExiting)); ATRACE_NAME(message.c_str()); } + ALOGD_IF(debugTransportPublisher(), + "channel '%s' publisher ~ %s: seq=%u, id=%d, x=%f, y=%f, isExiting=%s", + mChannel->getName().c_str(), __func__, seq, eventId, x, y, toString(isExiting)); InputMessage msg; msg.header.type = InputMessage::Type::DRAG; @@ -681,6 +740,9 @@ status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bo mChannel->getName().c_str(), toString(isInTouchMode)); ATRACE_NAME(message.c_str()); } + ALOGD_IF(debugTransportPublisher(), + "channel '%s' publisher ~ %s: seq=%u, id=%d, isInTouchMode=%s", + mChannel->getName().c_str(), __func__, seq, eventId, toString(isInTouchMode)); InputMessage msg; msg.header.type = InputMessage::Type::TOUCH_MODE; @@ -691,16 +753,18 @@ status_t InputPublisher::publishTouchModeEvent(uint32_t seq, int32_t eventId, bo } android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveConsumerResponse() { - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' publisher ~ %s", mChannel->getName().c_str(), __func__); - } - InputMessage msg; status_t result = mChannel->receiveMessage(&msg); if (result) { + ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: %s", + mChannel->getName().c_str(), __func__, strerror(result)); return android::base::Error(result); } if (msg.header.type == InputMessage::Type::FINISHED) { + ALOGD_IF(debugTransportPublisher(), + "channel '%s' publisher ~ %s: finished: seq=%u, handled=%s", + mChannel->getName().c_str(), __func__, msg.header.seq, + toString(msg.body.finished.handled)); return Finished{ .seq = msg.header.seq, .handled = msg.body.finished.handled, @@ -709,6 +773,8 @@ android::base::Result<InputPublisher::ConsumerResponse> InputPublisher::receiveC } if (msg.header.type == InputMessage::Type::TIMELINE) { + ALOGD_IF(debugTransportPublisher(), "channel '%s' publisher ~ %s: timeline: id=%d", + mChannel->getName().c_str(), __func__, msg.body.timeline.eventId); return Timeline{ .inputEventId = msg.body.timeline.eventId, .graphicsTimeline = msg.body.timeline.graphicsTimeline, @@ -738,10 +804,9 @@ bool InputConsumer::isTouchResamplingEnabled() { status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, - mChannel->getName().c_str(), toString(consumeBatches), frameTime); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64, + mChannel->getName().c_str(), toString(consumeBatches), frameTime); *outSeq = 0; *outEvent = nullptr; @@ -767,10 +832,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (consumeBatches || result != WOULD_BLOCK) { result = consumeBatch(factory, frameTime, outSeq, outEvent); if (*outEvent) { - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().c_str(), *outSeq); break; } } @@ -786,11 +850,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum initializeKeyEvent(keyEvent, &mMsg); *outSeq = mMsg.header.seq; *outEvent = keyEvent; - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } - break; + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; } case InputMessage::Type::MOTION: { @@ -799,11 +862,10 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum Batch& batch = mBatches[batchIndex]; if (canAddSample(batch, &mMsg)) { batch.samples.push_back(mMsg); - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ appended to batch event", - mChannel->getName().c_str()); - } - break; + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ appended to batch event", + mChannel->getName().c_str()); + break; } else if (isPointerEvent(mMsg.body.motion.source) && mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) { // No need to process events that we are going to cancel anyways @@ -824,12 +886,11 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum if (result) { return result; } - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed batch event and " - "deferred current event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } - break; + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().c_str(), *outSeq); + break; } } @@ -839,10 +900,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum Batch batch; batch.samples.push_back(mMsg); mBatches.push_back(batch); - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ started batch event", - mChannel->getName().c_str()); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ started batch event", + mChannel->getName().c_str()); break; } @@ -854,10 +914,9 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum *outSeq = mMsg.header.seq; *outEvent = motionEvent; - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", - mChannel->getName().c_str(), *outSeq); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().c_str(), *outSeq); break; } @@ -1074,11 +1133,9 @@ void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) { state.recentCoordinatesAreIdentical(id)) { PointerCoords& msgCoords = msg.body.motion.pointers[i].coords; const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); -#if DEBUG_RESAMPLING - ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, - resampleCoords.getX(), resampleCoords.getY(), - msgCoords.getX(), msgCoords.getY()); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(), + msgCoords.getY()); msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); msgCoords.isResampled = true; @@ -1099,17 +1156,13 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); if (index < 0) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, no touch state for device."); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no touch state for device."); return; } TouchState& touchState = mTouchStates[index]; if (touchState.historySize < 1) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, no history for device."); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, no history for device."); return; } @@ -1119,9 +1172,7 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, for (size_t i = 0; i < pointerCount; i++) { uint32_t id = event->getPointerId(i); if (!current->idBits.hasBit(id)) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, missing id %d", id); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, missing id %d", id); return; } } @@ -1137,9 +1188,8 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, other = &future; nsecs_t delta = future.eventTime - current->eventTime; if (delta < RESAMPLE_MIN_DELTA) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); return; } alpha = float(sampleTime - current->eventTime) / delta; @@ -1149,30 +1199,25 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, other = touchState.getHistory(1); nsecs_t delta = current->eventTime - other->eventTime; if (delta < RESAMPLE_MIN_DELTA) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too small: %" PRId64 " ns.", delta); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too small: %" PRId64 " ns.", + delta); return; } else if (delta > RESAMPLE_MAX_DELTA) { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, delta time is too large: %" PRId64 " ns.", delta); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, delta time is too large: %" PRId64 " ns.", + delta); return; } nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); if (sampleTime > maxPredict) { -#if DEBUG_RESAMPLING - ALOGD("Sample time is too far in the future, adjusting prediction " - "from %" PRId64 " to %" PRId64 " ns.", - sampleTime - current->eventTime, maxPredict - current->eventTime); -#endif + ALOGD_IF(DEBUG_RESAMPLING, + "Sample time is too far in the future, adjusting prediction " + "from %" PRId64 " to %" PRId64 " ns.", + sampleTime - current->eventTime, maxPredict - current->eventTime); sampleTime = maxPredict; } alpha = float(current->eventTime - sampleTime) / delta; } else { -#if DEBUG_RESAMPLING - ALOGD("Not resampled, insufficient data."); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "Not resampled, insufficient data."); return; } @@ -1207,28 +1252,22 @@ void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; const PointerCoords& currentCoords = current->getPointerById(id); resampledCoords.copyFrom(currentCoords); - if (other->idBits.hasBit(id) - && shouldResampleTool(event->getToolType(i))) { + if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) { const PointerCoords& otherCoords = other->getPointerById(id); resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, - lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, - lerp(currentCoords.getY(), otherCoords.getY(), alpha)); + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); resampledCoords.isResampled = true; -#if DEBUG_RESAMPLING - ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " - "other (%0.3f, %0.3f), alpha %0.3f", - id, resampledCoords.getX(), resampledCoords.getY(), - currentCoords.getX(), currentCoords.getY(), - otherCoords.getX(), otherCoords.getY(), - alpha); -#endif + ALOGD_IF(DEBUG_RESAMPLING, + "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha); } else { -#if DEBUG_RESAMPLING - ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", - id, resampledCoords.getX(), resampledCoords.getY(), - currentCoords.getX(), currentCoords.getY()); -#endif + ALOGD_IF(DEBUG_RESAMPLING, "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id, + resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(), + currentCoords.getY()); } } @@ -1241,10 +1280,9 @@ bool InputConsumer::shouldResampleTool(int32_t toolType) { } status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", - mChannel->getName().c_str(), seq, toString(handled)); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().c_str(), seq, toString(handled)); if (!seq) { ALOGE("Attempted to send a finished signal with sequence number 0."); @@ -1291,13 +1329,12 @@ status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { status_t InputConsumer::sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) { - if (DEBUG_TRANSPORT_ACTIONS) { - ALOGD("channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 - ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, - mChannel->getName().c_str(), inputEventId, - graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], - graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); - } + ALOGD_IF(DEBUG_TRANSPORT_CONSUMER, + "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32 + ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64, + mChannel->getName().c_str(), inputEventId, + graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME], + graphicsTimeline[GraphicsTimeline::PRESENT_TIME]); InputMessage msg; msg.header.type = InputMessage::Type::TIMELINE; diff --git a/libs/input/InputVerifier.cpp b/libs/input/InputVerifier.cpp new file mode 100644 index 0000000000..eb758045cc --- /dev/null +++ b/libs/input/InputVerifier.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "InputVerifier" + +#include <android-base/logging.h> +#include <input/InputVerifier.h> + +namespace android { + +/** + * Log all of the movements that are sent to this verifier. Helps to identify the streams that lead + * to inconsistent events. + * Enable this via "adb shell setprop log.tag.InputVerifierLogEvents DEBUG" + */ +static bool logEvents() { + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LogEvents", ANDROID_LOG_INFO); +} + +// --- InputVerifier --- + +InputVerifier::InputVerifier(const std::string& name) : mName(name){}; + +void InputVerifier::processMovement(int32_t deviceId, int32_t action, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, int32_t flags) { + if (logEvents()) { + LOG(ERROR) << "Processing " << MotionEvent::actionToString(action) << " for device " + << deviceId << " (" << pointerCount << " pointer" + << (pointerCount == 1 ? "" : "s") << ") on " << mName; + } + + switch (MotionEvent::getActionMasked(action)) { + case AMOTION_EVENT_ACTION_DOWN: { + auto [it, inserted] = mTouchingPointerIdsByDevice.insert({deviceId, {}}); + if (!inserted) { + LOG(FATAL) << "Got ACTION_DOWN, but already have touching pointers " << it->second + << " for device " << deviceId << " on " << mName; + } + it->second.set(pointerProperties[0].id); + break; + } + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + auto it = mTouchingPointerIdsByDevice.find(deviceId); + if (it == mTouchingPointerIdsByDevice.end()) { + LOG(FATAL) << "Got POINTER_DOWN, but no touching pointers for device " << deviceId + << " on " << mName; + } + it->second.set(pointerProperties[MotionEvent::getActionIndex(action)].id); + break; + } + case AMOTION_EVENT_ACTION_MOVE: { + ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "MOVE"); + break; + } + case AMOTION_EVENT_ACTION_POINTER_UP: { + auto it = mTouchingPointerIdsByDevice.find(deviceId); + if (it == mTouchingPointerIdsByDevice.end()) { + LOG(FATAL) << "Got POINTER_UP, but no touching pointers for device " << deviceId + << " on " << mName; + } + it->second.reset(pointerProperties[MotionEvent::getActionIndex(action)].id); + break; + } + case AMOTION_EVENT_ACTION_UP: { + auto it = mTouchingPointerIdsByDevice.find(deviceId); + if (it == mTouchingPointerIdsByDevice.end()) { + LOG(FATAL) << "Got ACTION_UP, but no record for deviceId " << deviceId << " on " + << mName; + } + const auto& [_, touchingPointerIds] = *it; + if (touchingPointerIds.count() != 1) { + LOG(FATAL) << "Got ACTION_UP, but we have pointers: " << touchingPointerIds + << " for deviceId " << deviceId << " on " << mName; + } + const int32_t pointerId = pointerProperties[0].id; + if (!touchingPointerIds.test(pointerId)) { + LOG(FATAL) << "Got ACTION_UP, but pointerId " << pointerId + << " is not touching. Touching pointers: " << touchingPointerIds + << " for deviceId " << deviceId << " on " << mName; + } + mTouchingPointerIdsByDevice.erase(it); + break; + } + case AMOTION_EVENT_ACTION_CANCEL: { + if ((flags & AMOTION_EVENT_FLAG_CANCELED) != AMOTION_EVENT_FLAG_CANCELED) { + LOG(FATAL) << "For ACTION_CANCEL, must set FLAG_CANCELED"; + } + ensureTouchingPointersMatch(deviceId, pointerCount, pointerProperties, "CANCEL"); + mTouchingPointerIdsByDevice.erase(deviceId); + break; + } + } +} + +void InputVerifier::ensureTouchingPointersMatch(int32_t deviceId, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const char* action) const { + auto it = mTouchingPointerIdsByDevice.find(deviceId); + if (it == mTouchingPointerIdsByDevice.end()) { + LOG(FATAL) << "Got " << action << ", but no touching pointers for device " << deviceId + << " on " << mName; + } + const auto& [_, touchingPointerIds] = *it; + for (size_t i = 0; i < pointerCount; i++) { + const int32_t pointerId = pointerProperties[i].id; + if (!touchingPointerIds.test(pointerId)) { + LOG(FATAL) << "Got " << action << " for pointerId " << pointerId + << " but the touching pointers are " << touchingPointerIds << " on " + << mName; + } + } +}; + +} // namespace android diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp index 7d11ef2575..b4151c6ea1 100644 --- a/libs/input/MotionPredictor.cpp +++ b/libs/input/MotionPredictor.cpp @@ -35,7 +35,6 @@ namespace android { namespace { -const char DEFAULT_MODEL_PATH[] = "/system/etc/motion_predictor_model.fb"; const int64_t PREDICTION_INTERVAL_NANOS = 12500000 / 3; // TODO(b/266747937): Get this from the model. @@ -62,48 +61,58 @@ TfLiteMotionPredictorSample::Point convertPrediction( // --- MotionPredictor --- -MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, const char* modelPath, +MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos, std::function<bool()> checkMotionPredictionEnabled) : mPredictionTimestampOffsetNanos(predictionTimestampOffsetNanos), - mModelPath(modelPath == nullptr ? DEFAULT_MODEL_PATH : modelPath), mCheckMotionPredictionEnabled(std::move(checkMotionPredictionEnabled)) {} -void MotionPredictor::record(const MotionEvent& event) { +android::base::Result<void> MotionPredictor::record(const MotionEvent& event) { + if (mLastEvent && mLastEvent->getDeviceId() != event.getDeviceId()) { + // We still have an active gesture for another device. The provided MotionEvent is not + // consistent the previous gesture. + LOG(ERROR) << "Inconsistent event stream: last event is " << *mLastEvent << ", but " + << __func__ << " is called with " << event; + return android::base::Error() + << "Inconsistent event stream: still have an active gesture from device " + << mLastEvent->getDeviceId() << ", but received " << event; + } if (!isPredictionAvailable(event.getDeviceId(), event.getSource())) { ALOGE("Prediction not supported for device %d's %s source", event.getDeviceId(), inputEventSourceToString(event.getSource()).c_str()); - return; + return {}; } // Initialise the model now that it's likely to be used. if (!mModel) { - mModel = TfLiteMotionPredictorModel::create(mModelPath.c_str()); + mModel = TfLiteMotionPredictorModel::create(); } - TfLiteMotionPredictorBuffers& buffers = - mDeviceBuffers.try_emplace(event.getDeviceId(), mModel->inputLength()).first->second; + if (mBuffers == nullptr) { + mBuffers = std::make_unique<TfLiteMotionPredictorBuffers>(mModel->inputLength()); + } const int32_t action = event.getActionMasked(); - if (action == AMOTION_EVENT_ACTION_UP) { + if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) { ALOGD_IF(isDebug(), "End of event stream"); - buffers.reset(); - return; + mBuffers->reset(); + mLastEvent.reset(); + return {}; } else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) { ALOGD_IF(isDebug(), "Skipping unsupported %s action", MotionEvent::actionToString(action).c_str()); - return; + return {}; } if (event.getPointerCount() != 1) { ALOGD_IF(isDebug(), "Prediction not supported for multiple pointers"); - return; + return {}; } const int32_t toolType = event.getPointerProperties(0)->toolType; if (toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS) { ALOGD_IF(isDebug(), "Prediction not supported for non-stylus tool: %s", motionToolTypeToString(toolType)); - return; + return {}; } for (size_t i = 0; i <= event.getHistorySize(); ++i) { @@ -111,100 +120,98 @@ void MotionPredictor::record(const MotionEvent& event) { continue; } const PointerCoords* coords = event.getHistoricalRawPointerCoords(0, i); - buffers.pushSample(event.getHistoricalEventTime(i), - { - .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X), - .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y), - .pressure = event.getHistoricalPressure(0, i), - .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT, 0, - i), - .orientation = event.getHistoricalOrientation(0, i), - }); + mBuffers->pushSample(event.getHistoricalEventTime(i), + { + .position.x = coords->getAxisValue(AMOTION_EVENT_AXIS_X), + .position.y = coords->getAxisValue(AMOTION_EVENT_AXIS_Y), + .pressure = event.getHistoricalPressure(0, i), + .tilt = event.getHistoricalAxisValue(AMOTION_EVENT_AXIS_TILT, + 0, i), + .orientation = event.getHistoricalOrientation(0, i), + }); } - mLastEvents.try_emplace(event.getDeviceId()) - .first->second.copyFrom(&event, /*keepHistory=*/false); + if (!mLastEvent) { + mLastEvent = MotionEvent(); + } + mLastEvent->copyFrom(&event, /*keepHistory=*/false); + return {}; } -std::vector<std::unique_ptr<MotionEvent>> MotionPredictor::predict(nsecs_t timestamp) { - std::vector<std::unique_ptr<MotionEvent>> predictions; +std::unique_ptr<MotionEvent> MotionPredictor::predict(nsecs_t timestamp) { + if (mBuffers == nullptr || !mBuffers->isReady()) { + return nullptr; + } - for (const auto& [deviceId, buffer] : mDeviceBuffers) { - if (!buffer.isReady()) { - continue; - } + LOG_ALWAYS_FATAL_IF(!mModel); + mBuffers->copyTo(*mModel); + LOG_ALWAYS_FATAL_IF(!mModel->invoke()); + + // Read out the predictions. + const std::span<const float> predictedR = mModel->outputR(); + const std::span<const float> predictedPhi = mModel->outputPhi(); + const std::span<const float> predictedPressure = mModel->outputPressure(); + + TfLiteMotionPredictorSample::Point axisFrom = mBuffers->axisFrom().position; + TfLiteMotionPredictorSample::Point axisTo = mBuffers->axisTo().position; + + if (isDebug()) { + ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y); + ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y); + ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str()); + ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str()); + ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str()); + ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str()); + ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str()); + ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str()); + ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str()); + ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str()); + } - LOG_ALWAYS_FATAL_IF(!mModel); - buffer.copyTo(*mModel); - LOG_ALWAYS_FATAL_IF(!mModel->invoke()); - - // Read out the predictions. - const std::span<const float> predictedR = mModel->outputR(); - const std::span<const float> predictedPhi = mModel->outputPhi(); - const std::span<const float> predictedPressure = mModel->outputPressure(); - - TfLiteMotionPredictorSample::Point axisFrom = buffer.axisFrom().position; - TfLiteMotionPredictorSample::Point axisTo = buffer.axisTo().position; - - if (isDebug()) { - ALOGD("deviceId: %d", deviceId); - ALOGD("axisFrom: %f, %f", axisFrom.x, axisFrom.y); - ALOGD("axisTo: %f, %f", axisTo.x, axisTo.y); - ALOGD("mInputR: %s", base::Join(mModel->inputR(), ", ").c_str()); - ALOGD("mInputPhi: %s", base::Join(mModel->inputPhi(), ", ").c_str()); - ALOGD("mInputPressure: %s", base::Join(mModel->inputPressure(), ", ").c_str()); - ALOGD("mInputTilt: %s", base::Join(mModel->inputTilt(), ", ").c_str()); - ALOGD("mInputOrientation: %s", base::Join(mModel->inputOrientation(), ", ").c_str()); - ALOGD("predictedR: %s", base::Join(predictedR, ", ").c_str()); - ALOGD("predictedPhi: %s", base::Join(predictedPhi, ", ").c_str()); - ALOGD("predictedPressure: %s", base::Join(predictedPressure, ", ").c_str()); + LOG_ALWAYS_FATAL_IF(!mLastEvent); + const MotionEvent& event = *mLastEvent; + bool hasPredictions = false; + std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>(); + int64_t predictionTime = mBuffers->lastTimestamp(); + const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos; + + for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) { + const TfLiteMotionPredictorSample::Point point = + convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]); + // TODO(b/266747654): Stop predictions if confidence is < some threshold. + + ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y); + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y); + // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold. + coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]); + + predictionTime += PREDICTION_INTERVAL_NANOS; + if (i == 0) { + hasPredictions = true; + prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(), + event.getDisplayId(), INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, + event.getActionButton(), event.getFlags(), event.getEdgeFlags(), + event.getMetaState(), event.getButtonState(), + event.getClassification(), event.getTransform(), + event.getXPrecision(), event.getYPrecision(), + event.getRawXCursorPosition(), event.getRawYCursorPosition(), + event.getRawTransform(), event.getDownTime(), predictionTime, + event.getPointerCount(), event.getPointerProperties(), &coords); + } else { + prediction->addSample(predictionTime, &coords); } - const MotionEvent& event = mLastEvents[deviceId]; - bool hasPredictions = false; - std::unique_ptr<MotionEvent> prediction = std::make_unique<MotionEvent>(); - int64_t predictionTime = buffer.lastTimestamp(); - const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos; - - for (int i = 0; i < predictedR.size() && predictionTime <= futureTime; ++i) { - const TfLiteMotionPredictorSample::Point point = - convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]); - // TODO(b/266747654): Stop predictions if confidence is < some threshold. - - ALOGD_IF(isDebug(), "prediction %d: %f, %f", i, point.x, point.y); - PointerCoords coords; - coords.clear(); - coords.setAxisValue(AMOTION_EVENT_AXIS_X, point.x); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, point.y); - // TODO(b/266747654): Stop predictions if predicted pressure is < some threshold. - coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, predictedPressure[i]); - - predictionTime += PREDICTION_INTERVAL_NANOS; - if (i == 0) { - hasPredictions = true; - prediction->initialize(InputEvent::nextId(), event.getDeviceId(), event.getSource(), - event.getDisplayId(), INVALID_HMAC, - AMOTION_EVENT_ACTION_MOVE, event.getActionButton(), - event.getFlags(), event.getEdgeFlags(), event.getMetaState(), - event.getButtonState(), event.getClassification(), - event.getTransform(), event.getXPrecision(), - event.getYPrecision(), event.getRawXCursorPosition(), - event.getRawYCursorPosition(), event.getRawTransform(), - event.getDownTime(), predictionTime, event.getPointerCount(), - event.getPointerProperties(), &coords); - } else { - prediction->addSample(predictionTime, &coords); - } - - axisFrom = axisTo; - axisTo = point; - } - // TODO(b/266747511): Interpolate to futureTime? - if (hasPredictions) { - predictions.push_back(std::move(prediction)); - } + axisFrom = axisTo; + axisTo = point; + } + // TODO(b/266747511): Interpolate to futureTime? + if (!hasPredictions) { + return nullptr; } - return predictions; + return prediction; } bool MotionPredictor::isPredictionAvailable(int32_t /*deviceId*/, int32_t source) { diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp index 10510d675c..691e87c366 100644 --- a/libs/input/TfLiteMotionPredictor.cpp +++ b/libs/input/TfLiteMotionPredictor.cpp @@ -30,6 +30,7 @@ #include <type_traits> #include <utility> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/mapped_file.h> #define ATRACE_TAG ATRACE_TAG_INPUT @@ -60,6 +61,14 @@ constexpr char OUTPUT_R[] = "r"; constexpr char OUTPUT_PHI[] = "phi"; constexpr char OUTPUT_PRESSURE[] = "pressure"; +std::string getModelPath() { +#if defined(__ANDROID__) + return "/system/etc/motion_predictor_model.fb"; +#else + return base::GetExecutableDirectory() + "/motion_predictor_model.fb"; +#endif +} + // A TFLite ErrorReporter that logs to logcat. class LoggingErrorReporter : public tflite::ErrorReporter { public: @@ -206,9 +215,9 @@ void TfLiteMotionPredictorBuffers::pushSample(int64_t timestamp, mInputOrientation.pushBack(orientation); } -std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create( - const char* modelPath) { - const int fd = open(modelPath, O_RDONLY); +std::unique_ptr<TfLiteMotionPredictorModel> TfLiteMotionPredictorModel::create() { + const std::string modelPath = getModelPath(); + const int fd = open(modelPath.c_str(), O_RDONLY); if (fd == -1) { PLOG(FATAL) << "Could not read model from " << modelPath; } diff --git a/libs/input/VirtualInputDevice.cpp b/libs/input/VirtualInputDevice.cpp new file mode 100644 index 0000000000..3c1f2b6b56 --- /dev/null +++ b/libs/input/VirtualInputDevice.cpp @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "VirtualInputDevice" + +#include <android/input.h> +#include <android/keycodes.h> +#include <fcntl.h> +#include <input/Input.h> +#include <input/VirtualInputDevice.h> +#include <linux/uinput.h> +#include <math.h> +#include <utils/Log.h> + +#include <map> +#include <string> + +using android::base::unique_fd; + +/** + * Log debug messages about native virtual input devices. + * Enable this via "adb shell setprop log.tag.VirtualInputDevice DEBUG" + */ +static bool isDebug() { + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG, ANDROID_LOG_INFO); +} + +namespace android { +VirtualInputDevice::VirtualInputDevice(unique_fd fd) : mFd(std::move(fd)) {} +VirtualInputDevice::~VirtualInputDevice() { + ioctl(mFd, UI_DEV_DESTROY); +} + +bool VirtualInputDevice::writeInputEvent(uint16_t type, uint16_t code, int32_t value) { + struct input_event ev = {.type = type, .code = code, .value = value}; + return TEMP_FAILURE_RETRY(write(mFd, &ev, sizeof(struct input_event))) == sizeof(ev); +} + +/** Utility method to write keyboard key events or mouse button events. */ +bool VirtualInputDevice::writeEvKeyEvent(int32_t androidCode, int32_t androidAction, + const std::map<int, int>& evKeyCodeMapping, + const std::map<int, UinputAction>& actionMapping) { + auto evKeyCodeIterator = evKeyCodeMapping.find(androidCode); + if (evKeyCodeIterator == evKeyCodeMapping.end()) { + ALOGE("Unsupported native EV keycode for android code %d", androidCode); + return false; + } + auto actionIterator = actionMapping.find(androidAction); + if (actionIterator == actionMapping.end()) { + return false; + } + if (!writeInputEvent(EV_KEY, static_cast<uint16_t>(evKeyCodeIterator->second), + static_cast<int32_t>(actionIterator->second))) { + return false; + } + if (!writeInputEvent(EV_SYN, SYN_REPORT, 0)) { + return false; + } + return true; +} + +// --- VirtualKeyboard --- +const std::map<int, UinputAction> VirtualKeyboard::KEY_ACTION_MAPPING = { + {AKEY_EVENT_ACTION_DOWN, UinputAction::PRESS}, + {AKEY_EVENT_ACTION_UP, UinputAction::RELEASE}, +}; +// Keycode mapping from https://source.android.com/devices/input/keyboard-devices +const std::map<int, int> VirtualKeyboard::KEY_CODE_MAPPING = { + {AKEYCODE_0, KEY_0}, + {AKEYCODE_1, KEY_1}, + {AKEYCODE_2, KEY_2}, + {AKEYCODE_3, KEY_3}, + {AKEYCODE_4, KEY_4}, + {AKEYCODE_5, KEY_5}, + {AKEYCODE_6, KEY_6}, + {AKEYCODE_7, KEY_7}, + {AKEYCODE_8, KEY_8}, + {AKEYCODE_9, KEY_9}, + {AKEYCODE_A, KEY_A}, + {AKEYCODE_B, KEY_B}, + {AKEYCODE_C, KEY_C}, + {AKEYCODE_D, KEY_D}, + {AKEYCODE_E, KEY_E}, + {AKEYCODE_F, KEY_F}, + {AKEYCODE_G, KEY_G}, + {AKEYCODE_H, KEY_H}, + {AKEYCODE_I, KEY_I}, + {AKEYCODE_J, KEY_J}, + {AKEYCODE_K, KEY_K}, + {AKEYCODE_L, KEY_L}, + {AKEYCODE_M, KEY_M}, + {AKEYCODE_N, KEY_N}, + {AKEYCODE_O, KEY_O}, + {AKEYCODE_P, KEY_P}, + {AKEYCODE_Q, KEY_Q}, + {AKEYCODE_R, KEY_R}, + {AKEYCODE_S, KEY_S}, + {AKEYCODE_T, KEY_T}, + {AKEYCODE_U, KEY_U}, + {AKEYCODE_V, KEY_V}, + {AKEYCODE_W, KEY_W}, + {AKEYCODE_X, KEY_X}, + {AKEYCODE_Y, KEY_Y}, + {AKEYCODE_Z, KEY_Z}, + {AKEYCODE_GRAVE, KEY_GRAVE}, + {AKEYCODE_MINUS, KEY_MINUS}, + {AKEYCODE_EQUALS, KEY_EQUAL}, + {AKEYCODE_LEFT_BRACKET, KEY_LEFTBRACE}, + {AKEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE}, + {AKEYCODE_BACKSLASH, KEY_BACKSLASH}, + {AKEYCODE_SEMICOLON, KEY_SEMICOLON}, + {AKEYCODE_APOSTROPHE, KEY_APOSTROPHE}, + {AKEYCODE_COMMA, KEY_COMMA}, + {AKEYCODE_PERIOD, KEY_DOT}, + {AKEYCODE_SLASH, KEY_SLASH}, + {AKEYCODE_ALT_LEFT, KEY_LEFTALT}, + {AKEYCODE_ALT_RIGHT, KEY_RIGHTALT}, + {AKEYCODE_CTRL_LEFT, KEY_LEFTCTRL}, + {AKEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL}, + {AKEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT}, + {AKEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT}, + {AKEYCODE_META_LEFT, KEY_LEFTMETA}, + {AKEYCODE_META_RIGHT, KEY_RIGHTMETA}, + {AKEYCODE_CAPS_LOCK, KEY_CAPSLOCK}, + {AKEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK}, + {AKEYCODE_NUM_LOCK, KEY_NUMLOCK}, + {AKEYCODE_ENTER, KEY_ENTER}, + {AKEYCODE_TAB, KEY_TAB}, + {AKEYCODE_SPACE, KEY_SPACE}, + {AKEYCODE_DPAD_DOWN, KEY_DOWN}, + {AKEYCODE_DPAD_UP, KEY_UP}, + {AKEYCODE_DPAD_LEFT, KEY_LEFT}, + {AKEYCODE_DPAD_RIGHT, KEY_RIGHT}, + {AKEYCODE_MOVE_END, KEY_END}, + {AKEYCODE_MOVE_HOME, KEY_HOME}, + {AKEYCODE_PAGE_DOWN, KEY_PAGEDOWN}, + {AKEYCODE_PAGE_UP, KEY_PAGEUP}, + {AKEYCODE_DEL, KEY_BACKSPACE}, + {AKEYCODE_FORWARD_DEL, KEY_DELETE}, + {AKEYCODE_INSERT, KEY_INSERT}, + {AKEYCODE_ESCAPE, KEY_ESC}, + {AKEYCODE_BREAK, KEY_PAUSE}, + {AKEYCODE_F1, KEY_F1}, + {AKEYCODE_F2, KEY_F2}, + {AKEYCODE_F3, KEY_F3}, + {AKEYCODE_F4, KEY_F4}, + {AKEYCODE_F5, KEY_F5}, + {AKEYCODE_F6, KEY_F6}, + {AKEYCODE_F7, KEY_F7}, + {AKEYCODE_F8, KEY_F8}, + {AKEYCODE_F9, KEY_F9}, + {AKEYCODE_F10, KEY_F10}, + {AKEYCODE_F11, KEY_F11}, + {AKEYCODE_F12, KEY_F12}, + {AKEYCODE_BACK, KEY_BACK}, + {AKEYCODE_FORWARD, KEY_FORWARD}, + {AKEYCODE_NUMPAD_1, KEY_KP1}, + {AKEYCODE_NUMPAD_2, KEY_KP2}, + {AKEYCODE_NUMPAD_3, KEY_KP3}, + {AKEYCODE_NUMPAD_4, KEY_KP4}, + {AKEYCODE_NUMPAD_5, KEY_KP5}, + {AKEYCODE_NUMPAD_6, KEY_KP6}, + {AKEYCODE_NUMPAD_7, KEY_KP7}, + {AKEYCODE_NUMPAD_8, KEY_KP8}, + {AKEYCODE_NUMPAD_9, KEY_KP9}, + {AKEYCODE_NUMPAD_0, KEY_KP0}, + {AKEYCODE_NUMPAD_ADD, KEY_KPPLUS}, + {AKEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS}, + {AKEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK}, + {AKEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH}, + {AKEYCODE_NUMPAD_DOT, KEY_KPDOT}, + {AKEYCODE_NUMPAD_ENTER, KEY_KPENTER}, + {AKEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL}, + {AKEYCODE_NUMPAD_COMMA, KEY_KPCOMMA}, +}; +VirtualKeyboard::VirtualKeyboard(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} +VirtualKeyboard::~VirtualKeyboard() {} + +bool VirtualKeyboard::writeKeyEvent(int32_t androidKeyCode, int32_t androidAction) { + return writeEvKeyEvent(androidKeyCode, androidAction, KEY_CODE_MAPPING, KEY_ACTION_MAPPING); +} + +// --- VirtualDpad --- +// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices +const std::map<int, int> VirtualDpad::DPAD_KEY_CODE_MAPPING = { + // clang-format off + {AKEYCODE_DPAD_DOWN, KEY_DOWN}, + {AKEYCODE_DPAD_UP, KEY_UP}, + {AKEYCODE_DPAD_LEFT, KEY_LEFT}, + {AKEYCODE_DPAD_RIGHT, KEY_RIGHT}, + {AKEYCODE_DPAD_CENTER, KEY_SELECT}, + {AKEYCODE_BACK, KEY_BACK}, + // clang-format on +}; + +VirtualDpad::VirtualDpad(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} + +VirtualDpad::~VirtualDpad() {} + +bool VirtualDpad::writeDpadKeyEvent(int32_t androidKeyCode, int32_t androidAction) { + return writeEvKeyEvent(androidKeyCode, androidAction, DPAD_KEY_CODE_MAPPING, + VirtualKeyboard::KEY_ACTION_MAPPING); +} + +// --- VirtualMouse --- +const std::map<int, UinputAction> VirtualMouse::BUTTON_ACTION_MAPPING = { + {AMOTION_EVENT_ACTION_BUTTON_PRESS, UinputAction::PRESS}, + {AMOTION_EVENT_ACTION_BUTTON_RELEASE, UinputAction::RELEASE}, +}; + +// Button code mapping from https://source.android.com/devices/input/touch-devices +const std::map<int, int> VirtualMouse::BUTTON_CODE_MAPPING = { + // clang-format off + {AMOTION_EVENT_BUTTON_PRIMARY, BTN_LEFT}, + {AMOTION_EVENT_BUTTON_SECONDARY, BTN_RIGHT}, + {AMOTION_EVENT_BUTTON_TERTIARY, BTN_MIDDLE}, + {AMOTION_EVENT_BUTTON_BACK, BTN_BACK}, + {AMOTION_EVENT_BUTTON_FORWARD, BTN_FORWARD}, + // clang-format on +}; + +VirtualMouse::VirtualMouse(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} + +VirtualMouse::~VirtualMouse() {} + +bool VirtualMouse::writeButtonEvent(int32_t androidButtonCode, int32_t androidAction) { + return writeEvKeyEvent(androidButtonCode, androidAction, BUTTON_CODE_MAPPING, + BUTTON_ACTION_MAPPING); +} + +bool VirtualMouse::writeRelativeEvent(float relativeX, float relativeY) { + return writeInputEvent(EV_REL, REL_X, relativeX) && writeInputEvent(EV_REL, REL_Y, relativeY) && + writeInputEvent(EV_SYN, SYN_REPORT, 0); +} + +bool VirtualMouse::writeScrollEvent(float xAxisMovement, float yAxisMovement) { + return writeInputEvent(EV_REL, REL_HWHEEL, xAxisMovement) && + writeInputEvent(EV_REL, REL_WHEEL, yAxisMovement) && + writeInputEvent(EV_SYN, SYN_REPORT, 0); +} + +// --- VirtualTouchscreen --- +const std::map<int, UinputAction> VirtualTouchscreen::TOUCH_ACTION_MAPPING = { + {AMOTION_EVENT_ACTION_DOWN, UinputAction::PRESS}, + {AMOTION_EVENT_ACTION_UP, UinputAction::RELEASE}, + {AMOTION_EVENT_ACTION_MOVE, UinputAction::MOVE}, + {AMOTION_EVENT_ACTION_CANCEL, UinputAction::CANCEL}, +}; +// Tool type mapping from https://source.android.com/devices/input/touch-devices +const std::map<int, int> VirtualTouchscreen::TOOL_TYPE_MAPPING = { + {AMOTION_EVENT_TOOL_TYPE_FINGER, MT_TOOL_FINGER}, + {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM}, +}; + +VirtualTouchscreen::VirtualTouchscreen(unique_fd fd) : VirtualInputDevice(std::move(fd)) {} + +VirtualTouchscreen::~VirtualTouchscreen() {} + +bool VirtualTouchscreen::isValidPointerId(int32_t pointerId, UinputAction uinputAction) { + if (pointerId < -1 || pointerId >= (int)MAX_POINTERS) { + ALOGE("Virtual touch event has invalid pointer id %d; value must be between -1 and %zu", + pointerId, MAX_POINTERS - 0); + return false; + } + + if (uinputAction == UinputAction::PRESS && mActivePointers.test(pointerId)) { + ALOGE("Repetitive action DOWN event received on a pointer %d that is already down.", + pointerId); + return false; + } + if (uinputAction == UinputAction::RELEASE && !mActivePointers.test(pointerId)) { + ALOGE("PointerId %d action UP received with no prior action DOWN on touchscreen %d.", + pointerId, mFd.get()); + return false; + } + return true; +} + +bool VirtualTouchscreen::writeTouchEvent(int32_t pointerId, int32_t toolType, int32_t action, + float locationX, float locationY, float pressure, + float majorAxisSize) { + auto actionIterator = TOUCH_ACTION_MAPPING.find(action); + if (actionIterator == TOUCH_ACTION_MAPPING.end()) { + return false; + } + UinputAction uinputAction = actionIterator->second; + if (!isValidPointerId(pointerId, uinputAction)) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_MT_SLOT, pointerId)) { + return false; + } + auto toolTypeIterator = TOOL_TYPE_MAPPING.find(toolType); + if (toolTypeIterator == TOOL_TYPE_MAPPING.end()) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_MT_TOOL_TYPE, + static_cast<int32_t>(toolTypeIterator->second))) { + return false; + } + if (uinputAction == UinputAction::PRESS && !handleTouchDown(pointerId)) { + return false; + } + if (uinputAction == UinputAction::RELEASE && !handleTouchUp(pointerId)) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_X, locationX)) { + return false; + } + if (!writeInputEvent(EV_ABS, ABS_MT_POSITION_Y, locationY)) { + return false; + } + if (!isnan(pressure)) { + if (!writeInputEvent(EV_ABS, ABS_MT_PRESSURE, pressure)) { + return false; + } + } + if (!isnan(majorAxisSize)) { + if (!writeInputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, majorAxisSize)) { + return false; + } + } + return writeInputEvent(EV_SYN, SYN_REPORT, 0); +} + +bool VirtualTouchscreen::handleTouchUp(int32_t pointerId) { + if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(-1))) { + return false; + } + // When a pointer is no longer in touch, remove the pointer id from the corresponding + // entry in the unreleased touches map. + mActivePointers.reset(pointerId); + ALOGD_IF(isDebug(), "Pointer %d erased from the touchscreen %d", pointerId, mFd.get()); + + // Only sends the BTN UP event when there's no pointers on the touchscreen. + if (mActivePointers.none()) { + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::RELEASE))) { + return false; + } + ALOGD_IF(isDebug(), "No pointers on touchscreen %d, BTN UP event sent.", mFd.get()); + } + return true; +} + +bool VirtualTouchscreen::handleTouchDown(int32_t pointerId) { + // When a new pointer is down on the touchscreen, add the pointer id in the corresponding + // entry in the unreleased touches map. + if (mActivePointers.none()) { + // Only sends the BTN Down event when the first pointer on the touchscreen is down. + if (!writeInputEvent(EV_KEY, BTN_TOUCH, static_cast<int32_t>(UinputAction::PRESS))) { + return false; + } + ALOGD_IF(isDebug(), "First pointer %d down under touchscreen %d, BTN DOWN event sent", + pointerId, mFd.get()); + } + + mActivePointers.set(pointerId); + ALOGD_IF(isDebug(), "Added pointer %d under touchscreen %d in the map", pointerId, mFd.get()); + if (!writeInputEvent(EV_ABS, ABS_MT_TRACKING_ID, static_cast<int32_t>(pointerId))) { + return false; + } + return true; +} + +} // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 37faf91936..42bdf57514 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -43,6 +43,13 @@ cc_test { "-Werror", "-Wno-unused-parameter", ], + sanitize: { + undefined: true, + all_undefined: true, + diag: { + undefined: true, + }, + }, shared_libs: [ "libbase", "libbinder", diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 8a6e983bb5..81c23df97a 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -29,6 +29,8 @@ namespace android { // Default display id. static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; +static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; + class BaseTest : public testing::Test { protected: static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -235,102 +237,110 @@ protected: static constexpr float RAW_X_OFFSET = 12; static constexpr float RAW_Y_OFFSET = -41.1; + void SetUp() override; + int32_t mId; ui::Transform mTransform; ui::Transform mRawTransform; + PointerProperties mPointerProperties[2]; + struct Sample { + PointerCoords pointerCoords[2]; + }; + std::array<Sample, 3> mSamples{}; void initializeEventWithHistory(MotionEvent* event); void assertEqualsEventWithHistory(const MotionEvent* event); }; -void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { +void MotionEventTest::SetUp() { mId = InputEvent::nextId(); mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1}); - PointerProperties pointerProperties[2]; - pointerProperties[0].clear(); - pointerProperties[0].id = 1; - pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; - pointerProperties[1].clear(); - pointerProperties[1].id = 2; - pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; - - PointerCoords pointerCoords[2]; - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); - pointerCoords[0].isResampled = true; - pointerCoords[1].clear(); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + mPointerProperties[0].clear(); + mPointerProperties[0].id = 1; + mPointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + mPointerProperties[1].clear(); + mPointerProperties[1].id = 2; + mPointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + + mSamples[0].pointerCoords[0].clear(); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 10); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 11); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 12); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 13); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 14); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 15); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 16); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 17); + mSamples[0].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 18); + mSamples[0].pointerCoords[0].isResampled = true; + mSamples[0].pointerCoords[1].clear(); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 20); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 21); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 22); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 23); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 24); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 25); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 26); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 27); + mSamples[0].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 28); + + mSamples[1].pointerCoords[0].clear(); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); + mSamples[1].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); + mSamples[1].pointerCoords[0].isResampled = true; + mSamples[1].pointerCoords[1].clear(); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); + mSamples[1].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); + mSamples[1].pointerCoords[1].isResampled = true; + + mSamples[2].pointerCoords[0].clear(); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); + mSamples[2].pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); + mSamples[2].pointerCoords[1].clear(); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); + mSamples[2].pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); +} + +void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, - pointerProperties, pointerCoords); - - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 112); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 113); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 114); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 115); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 116); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 117); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 118); - pointerCoords[0].isResampled = true; - pointerCoords[1].clear(); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 120); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 121); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 122); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 123); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 124); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 125); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 126); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 127); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 128); - pointerCoords[1].isResampled = true; - event->addSample(ARBITRARY_EVENT_TIME + 1, pointerCoords); - - pointerCoords[0].clear(); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 210); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 211); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 212); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 213); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 214); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 215); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 216); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 217); - pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 218); - pointerCoords[1].clear(); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 220); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 221); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 222); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 223); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 224); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 225); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 226); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, 227); - pointerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 228); - event->addSample(ARBITRARY_EVENT_TIME + 2, pointerCoords); + mPointerProperties, mSamples[0].pointerCoords); + event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords); + event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords); } void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { @@ -367,51 +377,65 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); - ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); - - ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, - event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); - - ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0)); - ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0)); - ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1)); - ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1)); - ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0)); - ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1)); - - ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0)); - ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1)); - ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0)); - ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1)); - - ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); - ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); - ASSERT_EQ(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1)); - ASSERT_EQ(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1)); - ASSERT_EQ(X_OFFSET + 210 * X_SCALE, event->getX(0)); - ASSERT_EQ(X_OFFSET + 220 * X_SCALE, event->getX(1)); - - ASSERT_EQ(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0)); - ASSERT_EQ(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0)); - ASSERT_EQ(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1)); - ASSERT_EQ(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1)); - ASSERT_EQ(Y_OFFSET + 211 * Y_SCALE, event->getY(0)); - ASSERT_EQ(Y_OFFSET + 221 * Y_SCALE, event->getY(1)); + // Ensure the underlying PointerCoords are identical. + for (int sampleIdx = 0; sampleIdx < 3; sampleIdx++) { + for (int pointerIdx = 0; pointerIdx < 2; pointerIdx++) { + ASSERT_EQ(mSamples[sampleIdx].pointerCoords[pointerIdx], + event->getSamplePointerCoords()[sampleIdx * 2 + pointerIdx]); + } + } + + ASSERT_NEAR(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y), + EPSILON); + ASSERT_NEAR(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON); + ASSERT_NEAR(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y), EPSILON); + + ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0), + EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1), + EPSILON); + + ASSERT_NEAR(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0), EPSILON); + ASSERT_NEAR(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1), EPSILON); + + ASSERT_NEAR(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0), EPSILON); + ASSERT_NEAR(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1), EPSILON); + + ASSERT_NEAR(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0), EPSILON); + ASSERT_NEAR(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0), EPSILON); + ASSERT_NEAR(X_OFFSET + 110 * X_SCALE, event->getHistoricalX(0, 1), EPSILON); + ASSERT_NEAR(X_OFFSET + 120 * X_SCALE, event->getHistoricalX(1, 1), EPSILON); + ASSERT_NEAR(X_OFFSET + 210 * X_SCALE, event->getX(0), EPSILON); + ASSERT_NEAR(X_OFFSET + 220 * X_SCALE, event->getX(1), EPSILON); + + ASSERT_NEAR(Y_OFFSET + 11 * Y_SCALE, event->getHistoricalY(0, 0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 21 * Y_SCALE, event->getHistoricalY(1, 0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 111 * Y_SCALE, event->getHistoricalY(0, 1), EPSILON); + ASSERT_NEAR(Y_OFFSET + 121 * Y_SCALE, event->getHistoricalY(1, 1), EPSILON); + ASSERT_NEAR(Y_OFFSET + 211 * Y_SCALE, event->getY(0), EPSILON); + ASSERT_NEAR(Y_OFFSET + 221 * Y_SCALE, event->getY(1), EPSILON); ASSERT_EQ(12, event->getHistoricalPressure(0, 0)); ASSERT_EQ(22, event->getHistoricalPressure(1, 0)); @@ -550,10 +574,10 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); - ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0)); - ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0)); - ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); - ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); + ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON); + ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON); + ASSERT_NEAR((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0), EPSILON); + ASSERT_NEAR((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0), EPSILON); ASSERT_EQ(212, event.getPressure(0)); ASSERT_EQ(213, event.getSize(0)); ASSERT_EQ(214 * 2, event.getTouchMajor(0)); @@ -791,18 +815,18 @@ TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) { // The x and y axes should have the window transform applied. const auto newPoint = transform.transform(60, 100); - ASSERT_EQ(newPoint.x, event.getX(0)); - ASSERT_EQ(newPoint.y, event.getY(0)); + ASSERT_NEAR(newPoint.x, event.getX(0), EPSILON); + ASSERT_NEAR(newPoint.y, event.getY(0), EPSILON); // The raw values should have the display transform applied. const auto raw = rawTransform.transform(60, 100); - ASSERT_EQ(raw.x, event.getRawX(0)); - ASSERT_EQ(raw.y, event.getRawY(0)); + ASSERT_NEAR(raw.x, event.getRawX(0), EPSILON); + ASSERT_NEAR(raw.y, event.getRawY(0), EPSILON); // Relative values should have the window transform applied without any translation. const auto rel = transformWithoutTranslation(transform, 42, 96); - ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); + ASSERT_NEAR(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0), EPSILON); + ASSERT_NEAR(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), EPSILON); } TEST_F(MotionEventTest, Initialize_SetsClassification) { @@ -869,4 +893,42 @@ TEST_F(MotionEventTest, SetCursorPosition) { ASSERT_EQ(4, event.getYCursorPosition()); } +TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) { + // These are specifically integral values, since we are testing for rounding. + const vec2 EXPECTED{400.f, 700.f}; + + // Pick a transform such that transforming the point with its inverse and bringing that + // back to the original coordinate space results in a non-zero error amount due to the + // nature of floating point arithmetics. This can happen when the display is scaled. + // For example, the 'adb shell wm size' command can be used to set an override for the + // logical display size, which could result in the display being scaled. + constexpr float scale = 720.f / 1080.f; + ui::Transform transform; + transform.set(scale, 0, 0, scale); + ASSERT_NE(EXPECTED, transform.transform(transform.inverse().transform(EXPECTED))); + + // Store the inverse-transformed values in the motion event. + const vec2 rawCoords = transform.inverse().transform(EXPECTED); + PointerCoords pc{}; + pc.setAxisValue(AMOTION_EVENT_AXIS_X, rawCoords.x); + pc.setAxisValue(AMOTION_EVENT_AXIS_Y, rawCoords.y); + PointerProperties pp{}; + MotionEvent event; + event.initialize(InputEvent::nextId(), 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, + AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, + AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, + MotionClassification::NONE, transform, 2.0f, 2.1f, rawCoords.x, rawCoords.y, + transform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 1, &pp, &pc); + + // When using the getters from the MotionEvent to obtain the coordinates, the transformed + // values should be rounded by an appropriate amount so that they now precisely equal the + // original coordinates. + ASSERT_EQ(EXPECTED.x, event.getX(0)); + ASSERT_EQ(EXPECTED.y, event.getY(0)); + ASSERT_EQ(EXPECTED.x, event.getRawX(0)); + ASSERT_EQ(EXPECTED.y, event.getRawY(0)); + ASSERT_EQ(EXPECTED.x, event.getXCursorPosition()); + ASSERT_EQ(EXPECTED.y, event.getYCursorPosition()); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 70e4fda662..57606e8bdc 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -25,6 +25,8 @@ using android::base::Result; namespace android { +constexpr static float EPSILON = MotionEvent::ROUNDING_PRECISION; + class InputPublisherAndConsumerTest : public testing::Test { protected: std::shared_ptr<InputChannel> mServerChannel, mClientChannel; @@ -226,10 +228,10 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yOffset, motionEvent->getYOffset()); EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_EQ(xCursorPosition, motionEvent->getRawXCursorPosition()); - EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); - EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); - EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); + EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON); + EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON); EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); @@ -242,10 +244,12 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); const auto& pc = pointerCoords[i]; - EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i)); - EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i)); - EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i)); - EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i)); + EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp index ce87c8617e..c61efbf9ed 100644 --- a/libs/input/tests/MotionPredictor_test.cpp +++ b/libs/input/tests/MotionPredictor_test.cpp @@ -30,13 +30,6 @@ using ::testing::IsEmpty; using ::testing::SizeIs; using ::testing::UnorderedElementsAre; -const char MODEL_PATH[] = -#if defined(__ANDROID__) - "/system/etc/motion_predictor_model.fb"; -#else - "motion_predictor_model.fb"; -#endif - constexpr int32_t DOWN = AMOTION_EVENT_ACTION_DOWN; constexpr int32_t MOVE = AMOTION_EVENT_ACTION_MOVE; constexpr int32_t UP = AMOTION_EVENT_ACTION_UP; @@ -73,83 +66,74 @@ static MotionEvent getMotionEvent(int32_t action, float x, float y, } TEST(MotionPredictorTest, IsPredictionAvailable) { - MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH, + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return true /*enable prediction*/; }); ASSERT_TRUE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS)); ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN)); } TEST(MotionPredictorTest, Offset) { - MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, MODEL_PATH, + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/1, []() { return true /*enable prediction*/; }); predictor.record(getMotionEvent(DOWN, 0, 1, 30ms)); predictor.record(getMotionEvent(MOVE, 0, 2, 35ms)); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC); - ASSERT_EQ(1u, predicted.size()); - ASSERT_GE(predicted[0]->getEventTime(), 41); + std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC); + ASSERT_NE(nullptr, predicted); + ASSERT_GE(predicted->getEventTime(), 41); } TEST(MotionPredictorTest, FollowsGesture) { - MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH, + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return true /*enable prediction*/; }); // MOVE without a DOWN is ignored. predictor.record(getMotionEvent(MOVE, 1, 3, 10ms)); - EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty()); + EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC)); predictor.record(getMotionEvent(DOWN, 2, 5, 20ms)); predictor.record(getMotionEvent(MOVE, 2, 7, 30ms)); predictor.record(getMotionEvent(MOVE, 3, 9, 40ms)); - EXPECT_THAT(predictor.predict(50 * NSEC_PER_MSEC), SizeIs(1)); + EXPECT_NE(nullptr, predictor.predict(50 * NSEC_PER_MSEC)); predictor.record(getMotionEvent(UP, 4, 11, 50ms)); - EXPECT_THAT(predictor.predict(20 * NSEC_PER_MSEC), IsEmpty()); + EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC)); } -TEST(MotionPredictorTest, MultipleDevicesTracked) { - MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH, +TEST(MotionPredictorTest, MultipleDevicesNotSupported) { + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return true /*enable prediction*/; }); - predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)); - predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)); - predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)); - predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0)); - - predictor.record(getMotionEvent(DOWN, 100, 300, 0ms, /*deviceId=*/1)); - predictor.record(getMotionEvent(MOVE, 100, 300, 10ms, /*deviceId=*/1)); - predictor.record(getMotionEvent(MOVE, 200, 500, 20ms, /*deviceId=*/1)); - predictor.record(getMotionEvent(MOVE, 300, 700, 30ms, /*deviceId=*/1)); - - { - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC); - ASSERT_EQ(2u, predicted.size()); - - // Order of the returned vector is not guaranteed. - std::vector<int32_t> seenDeviceIds; - for (const auto& prediction : predicted) { - seenDeviceIds.push_back(prediction->getDeviceId()); - } - EXPECT_THAT(seenDeviceIds, UnorderedElementsAre(0, 1)); - } + ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 3, 7, 30ms, /*deviceId=*/0)).ok()); - // End the gesture for device 0. - predictor.record(getMotionEvent(UP, 4, 9, 40ms, /*deviceId=*/0)); - predictor.record(getMotionEvent(MOVE, 400, 900, 40ms, /*deviceId=*/1)); + ASSERT_FALSE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok()); + ASSERT_FALSE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok()); +} - { - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC); - ASSERT_EQ(1u, predicted.size()); - ASSERT_EQ(predicted[0]->getDeviceId(), 1); - } +TEST(MotionPredictorTest, IndividualGesturesFromDifferentDevicesAreSupported) { + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, + []() { return true /*enable prediction*/; }); + + ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 1, 3, 0ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 1, 3, 10ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 2, 5, 20ms, /*deviceId=*/0)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(UP, 2, 5, 30ms, /*deviceId=*/0)).ok()); + + // Now, send a gesture from a different device. Since we have no active gesture, the new gesture + // should be processed correctly. + ASSERT_TRUE(predictor.record(getMotionEvent(DOWN, 100, 300, 40ms, /*deviceId=*/1)).ok()); + ASSERT_TRUE(predictor.record(getMotionEvent(MOVE, 100, 300, 50ms, /*deviceId=*/1)).ok()); } TEST(MotionPredictorTest, FlagDisablesPrediction) { - MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, MODEL_PATH, + MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0, []() { return false /*disable prediction*/; }); predictor.record(getMotionEvent(DOWN, 0, 1, 30ms)); predictor.record(getMotionEvent(MOVE, 0, 1, 35ms)); - std::vector<std::unique_ptr<MotionEvent>> predicted = predictor.predict(40 * NSEC_PER_MSEC); - ASSERT_EQ(0u, predicted.size()); + std::unique_ptr<MotionEvent> predicted = predictor.predict(40 * NSEC_PER_MSEC); + ASSERT_EQ(nullptr, predicted); ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_STYLUS)); ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN)); } diff --git a/libs/input/tests/TfLiteMotionPredictor_test.cpp b/libs/input/tests/TfLiteMotionPredictor_test.cpp index 454f2aaac4..6e76ac1e52 100644 --- a/libs/input/tests/TfLiteMotionPredictor_test.cpp +++ b/libs/input/tests/TfLiteMotionPredictor_test.cpp @@ -21,7 +21,6 @@ #include <iterator> #include <string> -#include <android-base/file.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <input/TfLiteMotionPredictor.h> @@ -33,14 +32,6 @@ using ::testing::Each; using ::testing::ElementsAre; using ::testing::FloatNear; -std::string getModelPath() { -#if defined(__ANDROID__) - return "/system/etc/motion_predictor_model.fb"; -#else - return base::GetExecutableDirectory() + "/motion_predictor_model.fb"; -#endif -} - TEST(TfLiteMotionPredictorTest, BuffersReadiness) { TfLiteMotionPredictorBuffers buffers(/*inputLength=*/5); ASSERT_FALSE(buffers.isReady()); @@ -92,8 +83,7 @@ TEST(TfLiteMotionPredictorTest, BuffersRecentData) { } TEST(TfLiteMotionPredictorTest, BuffersCopyTo) { - std::unique_ptr<TfLiteMotionPredictorModel> model = - TfLiteMotionPredictorModel::create(getModelPath().c_str()); + std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create(); TfLiteMotionPredictorBuffers buffers(model->inputLength()); buffers.pushSample(/*timestamp=*/1, @@ -137,8 +127,7 @@ TEST(TfLiteMotionPredictorTest, BuffersCopyTo) { } TEST(TfLiteMotionPredictorTest, ModelInputOutputLength) { - std::unique_ptr<TfLiteMotionPredictorModel> model = - TfLiteMotionPredictorModel::create(getModelPath().c_str()); + std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create(); ASSERT_GT(model->inputLength(), 0u); const int inputLength = model->inputLength(); @@ -155,8 +144,7 @@ TEST(TfLiteMotionPredictorTest, ModelInputOutputLength) { } TEST(TfLiteMotionPredictorTest, ModelOutput) { - std::unique_ptr<TfLiteMotionPredictorModel> model = - TfLiteMotionPredictorModel::create(getModelPath().c_str()); + std::unique_ptr<TfLiteMotionPredictorModel> model = TfLiteMotionPredictorModel::create(); TfLiteMotionPredictorBuffers buffers(model->inputLength()); buffers.pushSample(/*timestamp=*/1, {.position = {.x = 100, .y = 200}, .pressure = 0.2}); diff --git a/libs/jpegrecoverymap/Android.bp b/libs/jpegrecoverymap/Android.bp index 78d1912d24..b470f351f4 100644 --- a/libs/jpegrecoverymap/Android.bp +++ b/libs/jpegrecoverymap/Android.bp @@ -29,9 +29,11 @@ cc_library { local_include_dirs: ["include"], srcs: [ + "icc.cpp", "jpegr.cpp", "recoverymapmath.cpp", "jpegrutils.cpp", + "multipictureformat.cpp", ], shared_libs: [ @@ -40,9 +42,8 @@ cc_library { "libjpegencoder", "libjpegdecoder", "liblog", + "libutils", ], - - static_libs: ["libskia"], } cc_library { diff --git a/libs/jpegrecoverymap/OWNERS b/libs/jpegrecoverymap/OWNERS index 133af5bcd4..6ace354d0b 100644 --- a/libs/jpegrecoverymap/OWNERS +++ b/libs/jpegrecoverymap/OWNERS @@ -1,4 +1,3 @@ arifdikici@google.com -deakin@google.com dichenzhang@google.com kyslov@google.com
\ No newline at end of file diff --git a/libs/jpegrecoverymap/icc.cpp b/libs/jpegrecoverymap/icc.cpp new file mode 100644 index 0000000000..5412cb1102 --- /dev/null +++ b/libs/jpegrecoverymap/icc.cpp @@ -0,0 +1,584 @@ +/* + * 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/icc.h> +#include <jpegrecoverymap/recoverymapmath.h> +#include <vector> +#include <utils/Log.h> + +#ifndef FLT_MAX +#define FLT_MAX 0x1.fffffep127f +#endif + +namespace android::jpegrecoverymap { +static void Matrix3x3_apply(const Matrix3x3* m, float* x) { + float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2]; + float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2]; + float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2]; + x[0] = y0; + x[1] = y1; + x[2] = y2; +} + +bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) { + double a00 = src->vals[0][0], + a01 = src->vals[1][0], + a02 = src->vals[2][0], + a10 = src->vals[0][1], + a11 = src->vals[1][1], + a12 = src->vals[2][1], + a20 = src->vals[0][2], + a21 = src->vals[1][2], + a22 = src->vals[2][2]; + + double b0 = a00*a11 - a01*a10, + b1 = a00*a12 - a02*a10, + b2 = a01*a12 - a02*a11, + b3 = a20, + b4 = a21, + b5 = a22; + + double determinant = b0*b5 + - b1*b4 + + b2*b3; + + if (determinant == 0) { + return false; + } + + double invdet = 1.0 / determinant; + if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) { + return false; + } + + b0 *= invdet; + b1 *= invdet; + b2 *= invdet; + b3 *= invdet; + b4 *= invdet; + b5 *= invdet; + + dst->vals[0][0] = (float)( a11*b5 - a12*b4 ); + dst->vals[1][0] = (float)( a02*b4 - a01*b5 ); + dst->vals[2][0] = (float)( + b2 ); + dst->vals[0][1] = (float)( a12*b3 - a10*b5 ); + dst->vals[1][1] = (float)( a00*b5 - a02*b3 ); + dst->vals[2][1] = (float)( - b1 ); + dst->vals[0][2] = (float)( a10*b4 - a11*b3 ); + dst->vals[1][2] = (float)( a01*b3 - a00*b4 ); + dst->vals[2][2] = (float)( + b0 ); + + for (int r = 0; r < 3; ++r) + for (int c = 0; c < 3; ++c) { + if (!isfinitef_(dst->vals[r][c])) { + return false; + } + } + return true; +} + +static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) { + Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } }; + for (int r = 0; r < 3; r++) + for (int c = 0; c < 3; c++) { + m.vals[r][c] = A->vals[r][0] * B->vals[0][c] + + A->vals[r][1] * B->vals[1][c] + + A->vals[r][2] * B->vals[2][c]; + } + return m; +} + +static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) { + float v[3] = { + xyz_float[0] / kD50_x, + xyz_float[1] / kD50_y, + xyz_float[2] / kD50_z, + }; + for (size_t i = 0; i < 3; ++i) { + v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f); + } + const float L = v[1] * 116.0f - 16.0f; + const float a = (v[0] - v[1]) * 500.0f; + const float b = (v[1] - v[2]) * 200.0f; + const float Lab_unorm[3] = { + L * (1 / 100.f), + (a + 128.0f) * (1 / 255.0f), + (b + 128.0f) * (1 / 255.0f), + }; + // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the + // table, but the spec appears to indicate that the value should be 0xFF00. + // https://crbug.com/skia/13807 + for (size_t i = 0; i < 3; ++i) { + reinterpret_cast<uint16_t*>(grid16_lab)[i] = + Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i])); + } +} + +std::string IccHelper::get_desc_string(const jpegr_transfer_function tf, + const jpegr_color_gamut gamut) { + std::string result; + switch (gamut) { + case JPEGR_COLORGAMUT_BT709: + result += "sRGB"; + break; + case JPEGR_COLORGAMUT_P3: + result += "Display P3"; + break; + case JPEGR_COLORGAMUT_BT2100: + result += "Rec2020"; + break; + default: + result += "Unknown"; + break; + } + result += " Gamut with "; + switch (tf) { + case JPEGR_TF_SRGB: + result += "sRGB"; + break; + case JPEGR_TF_LINEAR: + result += "Linear"; + break; + case JPEGR_TF_PQ: + result += "PQ"; + break; + case JPEGR_TF_HLG: + result += "HLG"; + break; + default: + result += "Unknown"; + break; + } + result += " Transfer"; + return result; +} + +sp<DataStruct> IccHelper::write_text_tag(const char* text) { + uint32_t text_length = strlen(text); + uint32_t header[] = { + Endian_SwapBE32(kTAG_TextType), // Type signature + 0, // Reserved + Endian_SwapBE32(1), // Number of records + Endian_SwapBE32(12), // Record size (must be 12) + Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')), // English USA + Endian_SwapBE32(2 * text_length), // Length of string in bytes + Endian_SwapBE32(28), // Offset of string + }; + + uint32_t total_length = text_length * 2 + sizeof(header); + total_length = (((total_length + 2) >> 2) << 2); // 4 aligned + sp<DataStruct> dataStruct = new DataStruct(total_length); + + if (!dataStruct->write(header, sizeof(header))) { + ALOGE("write_text_tag(): error in writing data"); + return dataStruct; + } + + for (size_t i = 0; i < text_length; i++) { + // Convert ASCII to big-endian UTF-16. + dataStruct->write8(0); + dataStruct->write8(text[i]); + } + + return dataStruct; +} + +sp<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) { + uint32_t data[] = { + Endian_SwapBE32(kXYZ_PCSSpace), + 0, + static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))), + static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))), + static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))), + }; + sp<DataStruct> dataStruct = new DataStruct(sizeof(data)); + dataStruct->write(&data, sizeof(data)); + return dataStruct; +} + +sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* table_16) { + int total_length = 4 + 4 + 4 + table_entries * 2; + total_length = (((total_length + 2) >> 2) << 2); // 4 aligned + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type + dataStruct->write32(0); // Reserved + dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count + for (size_t i = 0; i < table_entries; ++i) { + uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i]; + dataStruct->write16(value); + } + return dataStruct; +} + +sp<DataStruct> IccHelper::write_trc_tag_for_linear() { + int total_length = 16; + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type + dataStruct->write32(0); // Reserved + dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType)); + dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(1.0))); + + return dataStruct; +} + +float IccHelper::compute_tone_map_gain(const jpegr_transfer_function tf, float L) { + if (L <= 0.f) { + return 1.f; + } + if (tf == JPEGR_TF_PQ) { + // The PQ transfer function will map to the range [0, 1]. Linearly scale + // it up to the range [0, 10,000/203]. We will then tone map that back + // down to [0, 1]. + constexpr float kInputMaxLuminance = 10000 / 203.f; + constexpr float kOutputMaxLuminance = 1.0; + L *= kInputMaxLuminance; + + // Compute the tone map gain which will tone map from 10,000/203 to 1.0. + constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance); + constexpr float kToneMapB = 1.f / kOutputMaxLuminance; + return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L); + } + if (tf == JPEGR_TF_HLG) { + // Let Lw be the brightness of the display in nits. + constexpr float Lw = 203.f; + const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f); + return std::pow(L, gamma - 1.f); + } + return 1.f; +} + +sp<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries, + uint32_t transfer_characteristics) { + int total_length = 12; // 4 + 4 + 1 + 1 + 1 + 1 + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature + dataStruct->write32(0); // Reserved + dataStruct->write8(color_primaries); // Color primaries + dataStruct->write8(transfer_characteristics); // Transfer characteristics + dataStruct->write8(0); // RGB matrix + dataStruct->write8(1); // Full range + return dataStruct; +} + +void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) { + // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50. + Matrix3x3 src_to_rec2020; + const Matrix3x3 rec2020_to_XYZD50 = kRec2020; + { + Matrix3x3 XYZD50_to_rec2020; + Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020); + src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50); + } + + // Convert the source signal to linear. + for (size_t i = 0; i < kNumChannels; ++i) { + rgb[i] = pqOetf(rgb[i]); + } + + // Convert source gamut to Rec2020. + Matrix3x3_apply(&src_to_rec2020, rgb); + + // Compute the luminance of the signal. + float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}}); + + // Compute the tone map gain based on the luminance. + float tone_map_gain = compute_tone_map_gain(JPEGR_TF_PQ, L); + + // Apply the tone map gain. + for (size_t i = 0; i < kNumChannels; ++i) { + rgb[i] *= tone_map_gain; + } + + // Convert from Rec2020-linear to XYZD50. + Matrix3x3_apply(&rec2020_to_XYZD50, rgb); +} + +sp<DataStruct> IccHelper::write_clut(const uint8_t* grid_points, const uint8_t* grid_16) { + uint32_t value_count = kNumChannels; + for (uint32_t i = 0; i < kNumChannels; ++i) { + value_count *= grid_points[i]; + } + + int total_length = 20 + 2 * value_count; + total_length = (((total_length + 2) >> 2) << 2); // 4 aligned + sp<DataStruct> dataStruct = new DataStruct(total_length); + + for (size_t i = 0; i < 16; ++i) { + dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size + } + dataStruct->write8(2); // Grid byte width (always 16-bit) + dataStruct->write8(0); // Reserved + dataStruct->write8(0); // Reserved + dataStruct->write8(0); // Reserved + + for (uint32_t i = 0; i < value_count; ++i) { + uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i]; + dataStruct->write16(value); + } + + return dataStruct; +} + +sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type, + bool has_a_curves, + const uint8_t* grid_points, + const uint8_t* grid_16) { + const size_t b_curves_offset = 32; + sp<DataStruct> b_curves_data[kNumChannels]; + sp<DataStruct> a_curves_data[kNumChannels]; + size_t clut_offset = 0; + sp<DataStruct> clut; + size_t a_curves_offset = 0; + + // The "B" curve is required. + for (size_t i = 0; i < kNumChannels; ++i) { + b_curves_data[i] = write_trc_tag_for_linear(); + } + + // The "A" curve and CLUT are optional. + if (has_a_curves) { + clut_offset = b_curves_offset; + for (size_t i = 0; i < kNumChannels; ++i) { + clut_offset += b_curves_data[i]->getLength(); + } + clut = write_clut(grid_points, grid_16); + + a_curves_offset = clut_offset + clut->getLength(); + for (size_t i = 0; i < kNumChannels; ++i) { + a_curves_data[i] = write_trc_tag_for_linear(); + } + } + + int total_length = b_curves_offset; + for (size_t i = 0; i < kNumChannels; ++i) { + total_length += b_curves_data[i]->getLength(); + } + if (has_a_curves) { + total_length += clut->getLength(); + for (size_t i = 0; i < kNumChannels; ++i) { + total_length += a_curves_data[i]->getLength(); + } + } + sp<DataStruct> dataStruct = new DataStruct(total_length); + dataStruct->write32(Endian_SwapBE32(type)); // Type signature + dataStruct->write32(0); // Reserved + dataStruct->write8(kNumChannels); // Input channels + dataStruct->write8(kNumChannels); // Output channels + dataStruct->write16(0); // Reserved + dataStruct->write32(Endian_SwapBE32(b_curves_offset)); // B curve offset + dataStruct->write32(Endian_SwapBE32(0)); // Matrix offset (ignored) + dataStruct->write32(Endian_SwapBE32(0)); // M curve offset (ignored) + dataStruct->write32(Endian_SwapBE32(clut_offset)); // CLUT offset + dataStruct->write32(Endian_SwapBE32(a_curves_offset)); // A curve offset + for (size_t i = 0; i < kNumChannels; ++i) { + if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) { + return dataStruct; + } + } + if (has_a_curves) { + dataStruct->write(clut->getData(), clut->getLength()); + for (size_t i = 0; i < kNumChannels; ++i) { + dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength()); + } + } + return dataStruct; +} + +sp<DataStruct> IccHelper::writeIccProfile(jpegr_transfer_function tf, jpegr_color_gamut gamut) { + ICCHeader header; + + std::vector<std::pair<uint32_t, sp<DataStruct>>> tags; + + // Compute profile description tag + std::string desc = get_desc_string(tf, gamut); + + tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str())); + + Matrix3x3 toXYZD50; + switch (gamut) { + case JPEGR_COLORGAMUT_BT709: + toXYZD50 = kSRGB; + break; + case JPEGR_COLORGAMUT_P3: + toXYZD50 = kDisplayP3; + break; + case JPEGR_COLORGAMUT_BT2100: + toXYZD50 = kRec2020; + break; + default: + // Should not fall here. + return new DataStruct(0); + } + + // Compute primaries. + { + tags.emplace_back(kTAG_rXYZ, + write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0])); + tags.emplace_back(kTAG_gXYZ, + write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1])); + tags.emplace_back(kTAG_bXYZ, + write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2])); + } + + // Compute white point tag (must be D50) + tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z)); + + // Compute transfer curves. + if (tf != JPEGR_TF_PQ) { + if (tf == JPEGR_TF_HLG) { + std::vector<uint8_t> trc_table; + trc_table.resize(kTrcTableSize * 2); + for (uint32_t i = 0; i < kTrcTableSize; ++i) { + float x = i / (kTrcTableSize - 1.f); + float y = hlgOetf(x); + y *= compute_tone_map_gain(tf, y); + float_to_table16(y, &trc_table[2 * i]); + } + + tags.emplace_back(kTAG_rTRC, + write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data()))); + tags.emplace_back(kTAG_gTRC, + write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data()))); + tags.emplace_back(kTAG_bTRC, + write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data()))); + } else { + tags.emplace_back(kTAG_rTRC, write_trc_tag_for_linear()); + tags.emplace_back(kTAG_gTRC, write_trc_tag_for_linear()); + tags.emplace_back(kTAG_bTRC, write_trc_tag_for_linear()); + } + } + + // Compute CICP. + if (tf == JPEGR_TF_HLG || tf == JPEGR_TF_PQ) { + // The CICP tag is present in ICC 4.4, so update the header's version. + header.version = Endian_SwapBE32(0x04400000); + + uint32_t color_primaries = 0; + if (gamut == JPEGR_COLORGAMUT_BT709) { + color_primaries = kCICPPrimariesSRGB; + } else if (gamut == JPEGR_COLORGAMUT_P3) { + color_primaries = kCICPPrimariesP3; + } + + uint32_t transfer_characteristics = 0; + if (tf == JPEGR_TF_SRGB) { + transfer_characteristics = kCICPTrfnSRGB; + } else if (tf == JPEGR_TF_LINEAR) { + transfer_characteristics = kCICPTrfnLinear; + } else if (tf == JPEGR_TF_PQ) { + transfer_characteristics = kCICPTrfnPQ; + } else if (tf == JPEGR_TF_HLG) { + transfer_characteristics = kCICPTrfnHLG; + } + tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics)); + } + + // Compute A2B0. + if (tf == JPEGR_TF_PQ) { + std::vector<uint8_t> a2b_grid; + a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2); + size_t a2b_grid_index = 0; + for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) { + for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) { + for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) { + float rgb[3] = { + r_index / (kGridSize - 1.f), + g_index / (kGridSize - 1.f), + b_index / (kGridSize - 1.f), + }; + compute_lut_entry(toXYZD50, rgb); + float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]); + a2b_grid_index += 6; + } + } + } + const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data()); + + uint8_t grid_points[kNumChannels]; + for (size_t i = 0; i < kNumChannels; ++i) { + grid_points[i] = kGridSize; + } + + auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType, + /* has_a_curves */ true, + grid_points, + grid_16); + tags.emplace_back(kTAG_A2B0, std::move(a2b_data)); + } + + // Compute B2A0. + if (tf == JPEGR_TF_PQ) { + auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType, + /* has_a_curves */ false, + /* grid_points */ nullptr, + /* grid_16 */ nullptr); + tags.emplace_back(kTAG_B2A0, std::move(b2a_data)); + } + + // Compute copyright tag + tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022")); + + // Compute the size of the profile. + size_t tag_data_size = 0; + for (const auto& tag : tags) { + tag_data_size += tag.second->getLength(); + } + size_t tag_table_size = kICCTagTableEntrySize * tags.size(); + size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size; + + // Write the header. + header.data_color_space = Endian_SwapBE32(Signature_RGB); + header.pcs = Endian_SwapBE32(tf == JPEGR_TF_PQ ? Signature_Lab : Signature_XYZ); + header.size = Endian_SwapBE32(profile_size); + header.tag_count = Endian_SwapBE32(tags.size()); + + sp<DataStruct> dataStruct = new DataStruct(profile_size); + if (!dataStruct->write(&header, sizeof(header))) { + ALOGE("writeIccProfile(): error in header"); + return dataStruct; + } + + // Write the tag table. Track the offset and size of the previous tag to + // compute each tag's offset. An empty SkData indicates that the previous + // tag is to be reused. + uint32_t last_tag_offset = sizeof(header) + tag_table_size; + uint32_t last_tag_size = 0; + for (const auto& tag : tags) { + last_tag_offset = last_tag_offset + last_tag_size; + last_tag_size = tag.second->getLength(); + uint32_t tag_table_entry[3] = { + Endian_SwapBE32(tag.first), + Endian_SwapBE32(last_tag_offset), + Endian_SwapBE32(last_tag_size), + }; + if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) { + ALOGE("writeIccProfile(): error in writing tag table"); + return dataStruct; + } + } + + // Write the tags. + for (const auto& tag : tags) { + if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) { + ALOGE("writeIccProfile(): error in writing tags"); + return dataStruct; + } + } + + return dataStruct; +} + +} // namespace android::jpegrecoverymap
\ No newline at end of file diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h new file mode 100644 index 0000000000..a81aa62627 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/icc.h @@ -0,0 +1,234 @@ +/* + * 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_ICC_H +#define ANDROID_JPEGRECOVERYMAP_ICC_H + +#include <jpegrecoverymap/jpegr.h> +#include <jpegrecoverymap/jpegrutils.h> +#include <utils/RefBase.h> +#include <cmath> +#include <string> + +#ifdef USE_BIG_ENDIAN +#undef USE_BIG_ENDIAN +#define USE_BIG_ENDIAN true +#endif + +namespace android::jpegrecoverymap { + +typedef int32_t Fixed; +#define Fixed1 (1 << 16) +#define MaxS32FitsInFloat 2147483520 +#define MinS32FitsInFloat (-MaxS32FitsInFloat) +#define FixedToFloat(x) ((x) * 1.52587890625e-5f) + +typedef struct Matrix3x3 { + float vals[3][3]; +} Matrix3x3; + +// The D50 illuminant. +constexpr float kD50_x = 0.9642f; +constexpr float kD50_y = 1.0000f; +constexpr float kD50_z = 0.8249f; + +enum { + // data_color_space + Signature_CMYK = 0x434D594B, + Signature_Gray = 0x47524159, + Signature_RGB = 0x52474220, + + // pcs + Signature_Lab = 0x4C616220, + Signature_XYZ = 0x58595A20, +}; + + +typedef uint32_t FourByteTag; +static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) { + return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d); +} + +// This is equal to the header size according to the ICC specification (128) +// plus the size of the tag count (4). We include the tag count since we +// always require it to be present anyway. +static constexpr size_t kICCHeaderSize = 132; + +// Contains a signature (4), offset (4), and size (4). +static constexpr size_t kICCTagTableEntrySize = 12; + +static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r'); +static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' '); +static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' '); +static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p'); + +static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c'); +static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c'); +static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z'); +static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z'); +static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z'); +static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't'); +static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C'); +static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C'); +static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C'); +static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p'); +static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't'); +static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0'); +static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0'); + +static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v'); +static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' '); +static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' '); +static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a'); + + +static constexpr Matrix3x3 kSRGB = {{ + // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync. + // 0.436065674f, 0.385147095f, 0.143066406f, + // 0.222488403f, 0.716873169f, 0.060607910f, + // 0.013916016f, 0.097076416f, 0.714096069f, + { FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0) }, + { FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84) }, + { FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF) }, +}}; + +static constexpr Matrix3x3 kDisplayP3 = {{ + { 0.515102f, 0.291965f, 0.157153f }, + { 0.241182f, 0.692236f, 0.0665819f }, + { -0.00104941f, 0.0418818f, 0.784378f }, +}}; + +static constexpr Matrix3x3 kRec2020 = {{ + { 0.673459f, 0.165661f, 0.125100f }, + { 0.279033f, 0.675338f, 0.0456288f }, + { -0.00193139f, 0.0299794f, 0.797162f }, +}}; + +static constexpr uint32_t kCICPPrimariesSRGB = 1; +static constexpr uint32_t kCICPPrimariesP3 = 12; +static constexpr uint32_t kCICPPrimariesRec2020 = 9; + +static constexpr uint32_t kCICPTrfnSRGB = 1; +static constexpr uint32_t kCICPTrfnLinear = 8; +static constexpr uint32_t kCICPTrfnPQ = 16; +static constexpr uint32_t kCICPTrfnHLG = 18; + +enum ParaCurveType { + kExponential_ParaCurveType = 0, + kGAB_ParaCurveType = 1, + kGABC_ParaCurveType = 2, + kGABDE_ParaCurveType = 3, + kGABCDEF_ParaCurveType = 4, +}; + +/** + * Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN. + */ +static inline int float_saturate2int(float x) { + x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat; + x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat; + return (int)x; +} + +static Fixed float_round_to_fixed(float x) { + return float_saturate2int((float)floor((double)x * Fixed1 + 0.5)); +} + +static uint16_t float_round_to_unorm16(float x) { + x = x * 65535.f + 0.5; + if (x > 65535) return 65535; + if (x < 0) return 0; + return static_cast<uint16_t>(x); +} + +static void float_to_table16(const float f, uint8_t* table_16) { + *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f)); +} + +static bool isfinitef_(float x) { return 0 == x*0; } + +struct ICCHeader { + // Size of the profile (computed) + uint32_t size; + // Preferred CMM type (ignored) + uint32_t cmm_type = 0; + // Version 4.3 or 4.4 if CICP is included. + uint32_t version = Endian_SwapBE32(0x04300000); + // Display device profile + uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile); + // RGB input color space; + uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace); + // Profile connection space. + uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace); + // Date and time (ignored) + uint8_t creation_date_time[12] = {0}; + // Profile signature + uint32_t signature = Endian_SwapBE32(kACSP_Signature); + // Platform target (ignored) + uint32_t platform = 0; + // Flags: not embedded, can be used independently + uint32_t flags = 0x00000000; + // Device manufacturer (ignored) + uint32_t device_manufacturer = 0; + // Device model (ignored) + uint32_t device_model = 0; + // Device attributes (ignored) + uint8_t device_attributes[8] = {0}; + // Relative colorimetric rendering intent + uint32_t rendering_intent = Endian_SwapBE32(1); + // D50 standard illuminant (X, Y, Z) + uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x)); + uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y)); + uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z)); + // Profile creator (ignored) + uint32_t creator = 0; + // Profile id checksum (ignored) + uint8_t profile_id[16] = {0}; + // Reserved (ignored) + uint8_t reserved[28] = {0}; + // Technically not part of header, but required + uint32_t tag_count = 0; +}; + +class IccHelper { +private: + static constexpr uint32_t kTrcTableSize = 65; + static constexpr uint32_t kGridSize = 17; + static constexpr size_t kNumChannels = 3; + + static sp<DataStruct> write_text_tag(const char* text); + static std::string get_desc_string(const jpegr_transfer_function tf, + const jpegr_color_gamut gamut); + static sp<DataStruct> write_xyz_tag(float x, float y, float z); + static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16); + static sp<DataStruct> write_trc_tag_for_linear(); + static float compute_tone_map_gain(const jpegr_transfer_function tf, float L); + static sp<DataStruct> write_cicp_tag(uint32_t color_primaries, + uint32_t transfer_characteristics); + static sp<DataStruct> write_mAB_or_mBA_tag(uint32_t type, + bool has_a_curves, + const uint8_t* grid_points, + const uint8_t* grid_16); + static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]); + static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16); + +public: + static sp<DataStruct> writeIccProfile(const jpegr_transfer_function tf, + const jpegr_color_gamut gamut); +}; +} // namespace android::jpegrecoverymap + +#endif //ANDROID_JPEGRECOVERYMAP_ICC_H
\ No newline at end of file diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h index 5455ba61d6..9b2dde7b94 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegr.h @@ -38,6 +38,14 @@ typedef enum { JPEGR_TF_SRGB = 3, } jpegr_transfer_function; +// Target output formats for decoder +typedef enum { + JPEGR_OUTPUT_SDR, // SDR in RGBA_8888 color format + JPEGR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear) + JPEGR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function) + JPEGR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function) +} jpegr_output_format; + struct jpegr_info_struct { size_t width; size_t height; @@ -195,20 +203,17 @@ public: * @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 | + * @param output_format flag for setting output color format. if set to + * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image + * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR. + * @param recovery_map destination of the decoded recovery map. * @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); + jpegr_output_format output_format = JPEGR_OUTPUT_HDR_LINEAR, + jr_uncompressed_ptr recovery_map = nullptr); /* * Gets Info from JPEGR file without decoding it. @@ -249,12 +254,16 @@ protected: * @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 output_format flag for setting output color format. if set to + * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image + * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR. * @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, + jpegr_output_format output_format, jr_uncompressed_ptr dest); private: diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h index 581806c54e..a381743137 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/jpegrutils.h @@ -18,6 +18,7 @@ #define ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H #include <jpegrecoverymap/jpegr.h> +#include <utils/RefBase.h> #include <sstream> #include <stdint.h> @@ -26,7 +27,45 @@ namespace android::jpegrecoverymap { +static constexpr uint32_t EndianSwap32(uint32_t value) { + return ((value & 0xFF) << 24) | + ((value & 0xFF00) << 8) | + ((value & 0xFF0000) >> 8) | + (value >> 24); +} +static inline uint16_t EndianSwap16(uint16_t value) { + return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8)); +} + +#if USE_BIG_ENDIAN + #define Endian_SwapBE32(n) EndianSwap32(n) + #define Endian_SwapBE16(n) EndianSwap16(n) +#else + #define Endian_SwapBE32(n) (n) + #define Endian_SwapBE16(n) (n) +#endif + struct jpegr_metadata; +/* + * Mutable data structure. Holds information for metadata. + */ +class DataStruct : public RefBase { +private: + void* data; + int writePos; + int length; + ~DataStruct(); + +public: + DataStruct(int s); + void* getData(); + int getLength(); + int getBytesWritten(); + bool write8(uint8_t value); + bool write16(uint16_t value); + bool write32(uint32_t value); + bool write(const void* src, int size); +}; /* * Helper function used for writing data to destination. @@ -51,12 +90,10 @@ status_t Write(jr_compressed_ptr destination, const void* source, size_t length, bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata); /* - * This method generates XMP metadata. + * This method generates XMP metadata for the primary image. * * below is an example of the XMP metadata that this function generates where * secondary_image_length = 1000 - * max_content_boost = 8.0 - * min_content_boost = 0.5 * * <x:xmpmeta * xmlns:x="adobe:ns:meta/" @@ -65,8 +102,7 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> * <rdf:Description * xmlns:Container="http://ns.google.com/photos/1.0/container/" - * xmlns:Item="http://ns.google.com/photos/1.0/container/item/" - * xmlns:RecoveryMap="http://ns.google.com/photos/1.0/recoverymap/"> + * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"> * <Container:Directory> * <rdf:Seq> * <rdf:li> @@ -78,10 +114,7 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta * <Container:Item * Item:Semantic="RecoveryMap" * Item:Mime="image/jpeg" - * Item:Length="1000" - * RecoveryMap:Version="1" - * RecoveryMap:MaxContentBoost="8.0" - * RecoveryMap:MinContentBoost="0.5"/> + * Item:Length="1000"/> * </rdf:li> * </rdf:Seq> * </Container:Directory> @@ -90,10 +123,40 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta * </x:xmpmeta> * * @param secondary_image_length length of secondary image + * @return XMP metadata in type of string + */ +std::string generateXmpForPrimaryImage(int secondary_image_length); + +/* + * This method generates XMP metadata for the recovery map image. + * + * below is an example of the XMP metadata that this function generates where + * max_content_boost = 8.0 + * min_content_boost = 0.5 + * + * <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:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/" + * hdrgm:Version="1" + * hdrgm:GainMapMin="0.5" + * hdrgm:GainMapMax="8.5" + * hdrgm:Gamma="1" + * hdrgm:OffsetSDR="0" + * hdrgm:OffsetHDR="0" + * hdrgm:HDRCapacityMin="0.5" + * hdrgm:HDRCapacityMax="8.5" + * hdrgm:BaseRendition="SDR"/> + * </rdf:RDF> + * </x:xmpmeta> + * * @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); + std::string generateXmpForSecondaryImage(jpegr_metadata& metadata); } // namespace android::jpegrecoverymap #endif //ANDROID_JPEGRECOVERYMAP_JPEGRUTILS_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h new file mode 100644 index 0000000000..cf3387d9b6 --- /dev/null +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/multipictureformat.h @@ -0,0 +1,64 @@ +/* + * 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_MULTIPICTUREFORMAT_H +#define ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H + +#include <jpegrecoverymap/jpegrutils.h> + +#ifdef USE_BIG_ENDIAN +#undef USE_BIG_ENDIAN +#define USE_BIG_ENDIAN true +#endif + +namespace android::jpegrecoverymap { + +constexpr size_t kNumPictures = 2; +constexpr size_t kMpEndianSize = 4; +constexpr uint16_t kTagSerializedCount = 3; +constexpr uint32_t kTagSize = 12; + +constexpr uint16_t kTypeLong = 0x4; +constexpr uint16_t kTypeUndefined = 0x7; + +static constexpr uint8_t kMpfSig[] = {'M', 'P', 'F', '\0'}; +constexpr uint8_t kMpLittleEndian[kMpEndianSize] = {0x49, 0x49, 0x2A, 0x00}; +constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A}; + +constexpr uint16_t kVersionTag = 0xB000; +constexpr uint16_t kVersionType = kTypeUndefined; +constexpr uint32_t kVersionCount = 4; +constexpr size_t kVersionSize = 4; +constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'}; + +constexpr uint16_t kNumberOfImagesTag = 0xB001; +constexpr uint16_t kNumberOfImagesType = kTypeLong; +constexpr uint32_t kNumberOfImagesCount = 1; + +constexpr uint16_t kMPEntryTag = 0xB002; +constexpr uint16_t kMPEntryType = kTypeUndefined; +constexpr uint32_t kMPEntrySize = 16; + +constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000; +constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000; + +size_t calculateMpfSize(); +sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset, + int secondary_image_size, int secondary_image_offset); + +} // namespace android::jpegrecoverymap + +#endif //ANDROID_JPEGRECOVERYMAP_MULTIPICTUREFORMAT_H diff --git a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h index c12cee9f19..8b5318f452 100644 --- a/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h +++ b/libs/jpegrecoverymap/include/jpegrecoverymap/recoverymapmath.h @@ -115,6 +115,14 @@ inline Color operator/(const Color& lhs, const float rhs) { return temp /= rhs; } +inline uint16_t floatToHalf(float f) { + uint32_t x = *((uint32_t*)&f); + uint16_t h = ((x >> 16) & 0x8000) + | ((((x & 0x7f800000) - 0x38000000) >> 13) & 0x7c00) + | ((x >> 13) & 0x03ff); + return h; +} + constexpr size_t kRecoveryFactorPrecision = 10; constexpr size_t kRecoveryFactorNumEntries = 1 << kRecoveryFactorPrecision; struct RecoveryLUT { @@ -392,6 +400,13 @@ float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size */ uint32_t colorToRgba1010102(Color e_gamma); +/* + * Convert from Color to F16. + * + * Alpha always set to 1.0. + */ +uint64_t colorToRgbaF16(Color e_gamma); + } // namespace android::jpegrecoverymap #endif // ANDROID_JPEGRECOVERYMAP_RECOVERYMAPMATH_H diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp index 828af2d289..f8763c637c 100644 --- a/libs/jpegrecoverymap/jpegr.cpp +++ b/libs/jpegrecoverymap/jpegr.cpp @@ -19,6 +19,8 @@ #include <jpegrecoverymap/jpegdecoderhelper.h> #include <jpegrecoverymap/recoverymapmath.h> #include <jpegrecoverymap/jpegrutils.h> +#include <jpegrecoverymap/multipictureformat.h> +#include <jpegrecoverymap/icc.h> #include <image_io/jpeg/jpeg_marker.h> #include <image_io/jpeg/jpeg_info.h> @@ -26,10 +28,6 @@ #include <image_io/jpeg/jpeg_info_builder.h> #include <image_io/base/data_segment_data_source.h> #include <utils/Log.h> -#include "SkColorSpace.h" -#include "SkData.h" -#include "SkICC.h" -#include "SkRefCnt.h" #include <map> #include <memory> @@ -88,27 +86,12 @@ int GetCPUCoreCount() { return cpuCoreCount; } -static const map<jpegrecoverymap::jpegr_color_gamut, skcms_Matrix3x3> jrGamut_to_skGamut { - {JPEGR_COLORGAMUT_BT709, SkNamedGamut::kSRGB}, - {JPEGR_COLORGAMUT_P3, SkNamedGamut::kDisplayP3}, - {JPEGR_COLORGAMUT_BT2100, SkNamedGamut::kRec2020}, -}; - -static const map< - jpegrecoverymap::jpegr_transfer_function, - skcms_TransferFunction> jrTransFunc_to_skTransFunc { - {JPEGR_TF_SRGB, SkNamedTransferFn::kSRGB}, - {JPEGR_TF_LINEAR, SkNamedTransferFn::kLinear}, - {JPEGR_TF_HLG, SkNamedTransferFn::kHLG}, - {JPEGR_TF_PQ, SkNamedTransferFn::kPQ}, -}; - /* Encode API-0 */ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, - jpegr_transfer_function hdr_tf, - jr_compressed_ptr dest, - int quality, - jr_exif_ptr exif) { + 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; } @@ -145,15 +128,14 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - sk_sp<SkData> icc = SkWriteICCProfile( - jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB), - jrGamut_to_skGamut.at(uncompressed_yuv_420_image.colorGamut)); + sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB, + uncompressed_yuv_420_image.colorGamut); JpegEncoderHelper jpeg_encoder; if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image.data, uncompressed_yuv_420_image.width, uncompressed_yuv_420_image.height, quality, - icc.get()->data(), icc.get()->size())) { + icc->getData(), icc->getLength())) { return ERROR_JPEGR_ENCODE_ERROR; } jpegr_compressed_struct jpeg; @@ -167,11 +149,11 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, /* Encode API-1 */ status_t JpegR::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) { + 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) { @@ -209,15 +191,14 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, compressed_map.data = compressed_map_data.get(); JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map)); - sk_sp<SkData> icc = SkWriteICCProfile( - jrTransFunc_to_skTransFunc.at(JPEGR_TF_SRGB), - jrGamut_to_skGamut.at(uncompressed_yuv_420_image->colorGamut)); + sp<DataStruct> icc = IccHelper::writeIccProfile(JPEGR_TF_SRGB, + uncompressed_yuv_420_image->colorGamut); JpegEncoderHelper jpeg_encoder; if (!jpeg_encoder.compressImage(uncompressed_yuv_420_image->data, uncompressed_yuv_420_image->width, uncompressed_yuv_420_image->height, quality, - icc.get()->data(), icc.get()->size())) { + icc->getData(), icc->getLength())) { return ERROR_JPEGR_ENCODE_ERROR; } jpegr_compressed_struct jpeg; @@ -231,10 +212,10 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, /* Encode API-2 */ status_t JpegR::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) { + 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 @@ -276,9 +257,9 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, /* Encode API-3 */ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, - jr_compressed_ptr compressed_jpeg_image, - jpegr_transfer_function hdr_tf, - jr_compressed_ptr dest) { + 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) { @@ -327,8 +308,7 @@ status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image, return NO_ERROR; } -status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, - jr_info_ptr jpegr_info) { +status_t JpegR::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; } @@ -349,16 +329,15 @@ status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, /* Decode API */ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, - jr_uncompressed_ptr dest, - jr_exif_ptr exif, - bool request_sdr) { + jr_uncompressed_ptr dest, + jr_exif_ptr exif, + jpegr_output_format output_format, + jr_uncompressed_ptr recovery_map) { if (compressed_jpegr_image == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } - // TODO: fill EXIF data - (void) exif; - if (request_sdr) { + if (output_format == JPEGR_OUTPUT_SDR) { JpegDecoderHelper jpeg_decoder; if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length, true)) { @@ -372,21 +351,60 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, uncompressed_rgba_image.width * uncompressed_rgba_image.height * 4); dest->width = uncompressed_rgba_image.width; dest->height = uncompressed_rgba_image.height; - return NO_ERROR; + + if (recovery_map == nullptr && exif == nullptr) { + return NO_ERROR; + } + + if (exif != nullptr) { + if (exif->data == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + if (exif->length < jpeg_decoder.getEXIFSize()) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize()); + exif->length = jpeg_decoder.getEXIFSize(); + } + if (recovery_map == nullptr) { + return NO_ERROR; + } } jpegr_compressed_struct compressed_map; - jpegr_metadata metadata; JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map)); + JpegDecoderHelper recovery_map_decoder; + if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) { + return ERROR_JPEGR_DECODE_ERROR; + } + + if (recovery_map != nullptr) { + recovery_map->width = recovery_map_decoder.getDecompressedImageWidth(); + recovery_map->height = recovery_map_decoder.getDecompressedImageHeight(); + int size = recovery_map->width * recovery_map->height; + recovery_map->data = malloc(size); + memcpy(recovery_map->data, recovery_map_decoder.getDecompressedImagePtr(), size); + } + + if (output_format == JPEGR_OUTPUT_SDR) { + return NO_ERROR; + } + JpegDecoderHelper jpeg_decoder; if (!jpeg_decoder.decompressImage(compressed_jpegr_image->data, compressed_jpegr_image->length)) { return ERROR_JPEGR_DECODE_ERROR; } - JpegDecoderHelper recovery_map_decoder; - if (!recovery_map_decoder.decompressImage(compressed_map.data, compressed_map.length)) { - return ERROR_JPEGR_DECODE_ERROR; + if (exif != nullptr) { + if (exif->data == nullptr) { + return ERROR_JPEGR_INVALID_NULL_PTR; + } + if (exif->length < jpeg_decoder.getEXIFSize()) { + return ERROR_JPEGR_BUFFER_TOO_SMALL; + } + memcpy(exif->data, jpeg_decoder.getEXIFPtr(), jpeg_decoder.getEXIFSize()); + exif->length = jpeg_decoder.getEXIFSize(); } jpegr_uncompressed_struct map; @@ -399,17 +417,18 @@ status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image, 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)) { + jpegr_metadata metadata; + if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()), + recovery_map_decoder.getXMPSize(), &metadata)) { return ERROR_JPEGR_DECODE_ERROR; } - JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest)); + JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, output_format, dest)); return NO_ERROR; } status_t JpegR::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map, - jr_compressed_ptr dest) { + jr_compressed_ptr dest) { if (uncompressed_recovery_map == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } @@ -493,10 +512,10 @@ void JobQueue::reset() { } status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, - jr_uncompressed_ptr uncompressed_p010_image, - jpegr_transfer_function hdr_tf, - jr_metadata_ptr metadata, - jr_uncompressed_ptr dest) { + jr_uncompressed_ptr uncompressed_p010_image, + jpegr_transfer_function hdr_tf, + jr_metadata_ptr metadata, + jr_uncompressed_ptr dest) { if (uncompressed_yuv_420_image == nullptr || uncompressed_p010_image == nullptr || metadata == nullptr @@ -637,9 +656,10 @@ status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_ima } status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, - jr_uncompressed_ptr uncompressed_recovery_map, - jr_metadata_ptr metadata, - jr_uncompressed_ptr dest) { + jr_uncompressed_ptr uncompressed_recovery_map, + jr_metadata_ptr metadata, + jpegr_output_format output_format, + jr_uncompressed_ptr dest) { if (uncompressed_yuv_420_image == nullptr || uncompressed_recovery_map == nullptr || metadata == nullptr @@ -654,18 +674,12 @@ status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, JobQueue jobQueue; std::function<void()> applyRecMap = [uncompressed_yuv_420_image, uncompressed_recovery_map, - metadata, dest, &jobQueue, &idwTable, + metadata, dest, &jobQueue, &idwTable, output_format, &recoveryLUT]() -> void { const float hdr_ratio = metadata->maxContentBoost; size_t width = uncompressed_yuv_420_image->width; size_t height = uncompressed_yuv_420_image->height; -#if USE_HLG_OETF_LUT - ColorTransformFn hdrOetf = hlgOetfLUT; -#else - ColorTransformFn hdrOetf = hlgOetf; -#endif - size_t rowStart, rowEnd; while (jobQueue.dequeueJob(rowStart, rowEnd)) { for (size_t y = rowStart; y < rowEnd; ++y) { @@ -693,11 +707,44 @@ status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, #else Color rgb_hdr = applyRecovery(rgb_sdr, recovery, metadata); #endif - Color rgb_gamma_hdr = hdrOetf(rgb_hdr / metadata->maxContentBoost); - uint32_t rgba1010102 = colorToRgba1010102(rgb_gamma_hdr); - + rgb_hdr = rgb_hdr / metadata->maxContentBoost; size_t pixel_idx = x + y * width; - reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102; + + switch (output_format) { + case JPEGR_OUTPUT_HDR_LINEAR: + { + uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr); + reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16; + break; + } + case JPEGR_OUTPUT_HDR_HLG: + { +#if USE_HLG_OETF_LUT + ColorTransformFn hdrOetf = hlgOetfLUT; +#else + ColorTransformFn hdrOetf = hlgOetf; +#endif + Color rgb_gamma_hdr = hdrOetf(rgb_hdr); + uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr); + reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102; + break; + } + case JPEGR_OUTPUT_HDR_PQ: + { +#if USE_HLG_OETF_LUT + ColorTransformFn hdrOetf = pqOetfLUT; +#else + ColorTransformFn hdrOetf = pqOetf; +#endif + Color rgb_gamma_hdr = hdrOetf(rgb_hdr); + uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr); + reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102; + break; + } + default: + {} + // Should be impossible to hit after input validation. + } } } } @@ -721,8 +768,8 @@ status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image, } status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image, - jr_compressed_ptr primary_image, - jr_compressed_ptr recovery_map) { + jr_compressed_ptr primary_image, + jr_compressed_ptr recovery_map) { if (compressed_jpegr_image == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } @@ -771,7 +818,7 @@ status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_j status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, - jr_compressed_ptr dest) { + jr_compressed_ptr dest) { if (compressed_jpegr_image == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } @@ -790,11 +837,22 @@ status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, // (Required, XMP package) APP1 (ff e1) // 2 bytes of length (2 + 29 + length of xmp package) // name space ("http://ns.adobe.com/xap/1.0/\0") -// xmp +// XMP +// +// (Required, MPF package) APP2 (ff e2) +// 2 bytes of length +// MPF // // (Required) primary image (without the first two bytes (SOI), may have other packages) // -// (Required) secondary image (the recovery map) +// SOI (ff d8) +// +// (Required, XMP package) APP1 (ff e1) +// 2 bytes of length (2 + 29 + length of xmp package) +// name space ("http://ns.adobe.com/xap/1.0/\0") +// XMP +// +// (Required) secondary image (the recovery map, without the first two bytes (SOI)) // // Metadata versions we are using: // ECMA TR-98 for JFIF marker @@ -802,10 +860,10 @@ status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image, // Adobe XMP spec part 3 for XMP marker // ICC v4.3 spec for ICC status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, - jr_compressed_ptr compressed_recovery_map, - jr_exif_ptr exif, - jr_metadata_ptr metadata, - jr_compressed_ptr dest) { + jr_compressed_ptr compressed_recovery_map, + jr_exif_ptr exif, + jr_metadata_ptr metadata, + jr_compressed_ptr dest) { if (compressed_jpeg_image == nullptr || compressed_recovery_map == nullptr || metadata == nullptr @@ -813,8 +871,25 @@ status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return ERROR_JPEGR_INVALID_NULL_PTR; } - int pos = 0; + const string nameSpace = "http://ns.adobe.com/xap/1.0/"; + const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator + // calculate secondary image length first, because the length will be written into the primary + // image xmp + const string xmp_secondary = generateXmpForSecondaryImage(*metadata); + const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */ + + nameSpaceLength /* 29 bytes length of name space including \0 */ + + xmp_secondary.size(); /* length of xmp packet */ + const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */ + + xmp_secondary_length + + compressed_recovery_map->length; + // primary image + const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size); + // same as primary + const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size(); + + int pos = 0; + // Begin primary image // Write SOI 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)); @@ -833,13 +908,7 @@ status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, // Prepare and write XMP { - 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: representing the length of the package - // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0", - // x bytes: length of xmp packet - const int length = 2 + nameSpaceLength + xmp.size(); + const int length = xmp_primary_length; const uint8_t lengthH = ((length >> 8) & 0xff); const uint8_t lengthL = (length & 0xff); JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); @@ -847,15 +916,57 @@ status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, 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, (void*)xmp_primary.c_str(), xmp_primary.size(), pos)); + } + + // Prepare and write MPF + { + const int length = 2 + calculateMpfSize(); + const uint8_t lengthH = ((length >> 8) & 0xff); + const uint8_t lengthL = (length & 0xff); + int primary_image_size = pos + length + compressed_jpeg_image->length; + // between APP2 + package size + signature + // ff e2 00 58 4d 50 46 00 + // 2 + 2 + 4 = 8 (bytes) + // and ff d8 sign of the secondary image + int secondary_image_offset = primary_image_size - pos - 8; + sp<DataStruct> mpf = generateMpf(primary_image_size, + 0, /* primary_image_offset */ + secondary_image_size, + secondary_image_offset); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos)); + JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthH, 1, pos)); + JPEGR_CHECK(Write(dest, &lengthL, 1, pos)); + JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos)); } // Write primary image JPEGR_CHECK(Write(dest, (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos)); + // Finish primary image + + // Begin secondary image (recovery map) + // Write SOI + 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)); + + // Prepare and write XMP + { + const int length = xmp_secondary_length; + const uint8_t lengthH = ((length >> 8) & 0xff); + const uint8_t lengthL = (length & 0xff); + 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_secondary.c_str(), xmp_secondary.size(), pos)); + } // Write secondary image - JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos)); + JPEGR_CHECK(Write(dest, + (uint8_t*)compressed_recovery_map->data + 2, compressed_recovery_map->length - 2, pos)); // Set back length dest->length = pos; @@ -864,8 +975,7 @@ status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image, return NO_ERROR; } -status_t JpegR::toneMap(jr_uncompressed_ptr src, - jr_uncompressed_ptr dest) { +status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) { if (src == nullptr || dest == nullptr) { return ERROR_JPEGR_INVALID_NULL_PTR; } diff --git a/libs/jpegrecoverymap/jpegrutils.cpp b/libs/jpegrecoverymap/jpegrutils.cpp index 49526c800f..38b78ad19c 100644 --- a/libs/jpegrecoverymap/jpegrutils.cpp +++ b/libs/jpegrecoverymap/jpegrutils.cpp @@ -15,18 +15,19 @@ */ #include <jpegrecoverymap/jpegrutils.h> +#include <utils/Log.h> #include <image_io/xml/xml_reader.h> #include <image_io/xml/xml_writer.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 <cmath> using namespace photos_editing_formats::image_io; using namespace std; namespace android::jpegrecoverymap { - /* * Helper function used for generating XMP metadata. * @@ -34,12 +35,62 @@ namespace android::jpegrecoverymap { * @param suffix The suffix part of the name. * @return A name of the form "prefix:suffix". */ -string Name(const string &prefix, const string &suffix) { +static inline string Name(const string &prefix, const string &suffix) { std::stringstream ss; ss << prefix << ":" << suffix; return ss.str(); } +DataStruct::DataStruct(int s) { + data = malloc(s); + length = s; + memset(data, 0, s); + writePos = 0; +} + +DataStruct::~DataStruct() { + if (data != nullptr) { + free(data); + } +} + +void* DataStruct::getData() { + return data; +} + +int DataStruct::getLength() { + return length; +} + +int DataStruct::getBytesWritten() { + return writePos; +} + +bool DataStruct::write8(uint8_t value) { + uint8_t v = value; + return write(&v, 1); +} + +bool DataStruct::write16(uint16_t value) { + uint16_t v = value; + return write(&v, 2); +} +bool DataStruct::write32(uint32_t value) { + uint32_t v = value; + return write(&v, 4); +} + +bool DataStruct::write(const void* src, int size) { + if (writePos + size > length) { + ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d", + writePos, size, length); + return false; + } + memcpy((uint8_t*) data + writePos, src, size); + writePos += size; + return true; +} + /* * Helper function used for writing data to destination. */ @@ -58,7 +109,7 @@ class XMPXmlHandler : public XmlHandler { public: XMPXmlHandler() : XmlHandler() { - gContainerItemState = NotStrarted; + state = NotStrarted; } enum ParseState { @@ -70,11 +121,11 @@ public: virtual DataMatchResult StartElement(const XmlTokenContext& context) { string val; if (context.BuildTokenValue(&val)) { - if (!val.compare(gContainerItemName)) { - gContainerItemState = Started; + if (!val.compare(containerName)) { + state = Started; } else { - if (gContainerItemState != Done) { - gContainerItemState = NotStrarted; + if (state != Done) { + state = NotStrarted; } } } @@ -82,8 +133,8 @@ public: } virtual DataMatchResult FinishElement(const XmlTokenContext& context) { - if (gContainerItemState == Started) { - gContainerItemState = Done; + if (state == Started) { + state = Done; lastAttributeName = ""; } return context.GetResult(); @@ -91,7 +142,7 @@ public: virtual DataMatchResult AttributeName(const XmlTokenContext& context) { string val; - if (gContainerItemState == Started) { + if (state == Started) { if (context.BuildTokenValue(&val)) { if (!val.compare(maxContentBoostAttrName)) { lastAttributeName = maxContentBoostAttrName; @@ -107,7 +158,7 @@ public: virtual DataMatchResult AttributeValue(const XmlTokenContext& context) { string val; - if (gContainerItemState == Started) { + if (state == Started) { if (context.BuildTokenValue(&val, true)) { if (!lastAttributeName.compare(maxContentBoostAttrName)) { maxContentBoostStr = val; @@ -120,11 +171,11 @@ public: } bool getMaxContentBoost(float* max_content_boost) { - if (gContainerItemState == Done) { + if (state == Done) { stringstream ss(maxContentBoostStr); float val; if (ss >> val) { - *max_content_boost = val; + *max_content_boost = exp2(val); return true; } else { return false; @@ -135,11 +186,11 @@ public: } bool getMinContentBoost(float* min_content_boost) { - if (gContainerItemState == Done) { + if (state == Done) { stringstream ss(minContentBoostStr); float val; if (ss >> val) { - *min_content_boost = val; + *min_content_boost = exp2(val); return true; } else { return false; @@ -150,13 +201,13 @@ public: } private: - static const string gContainerItemName; + static const string containerName; static const string maxContentBoostAttrName; string maxContentBoostStr; static const string minContentBoostAttrName; string minContentBoostStr; string lastAttributeName; - ParseState gContainerItemState; + ParseState state; }; // GContainer XMP constants - URI and namespace prefix @@ -168,8 +219,7 @@ const string kConDirectory = Name(kContainerPrefix, "Directory"); const string kConItem = Name(kContainerPrefix, "Item"); // GContainer XMP constants - names for XMP handlers -const string XMPXmlHandler::gContainerItemName = kConItem; - +const string XMPXmlHandler::containerName = "rdf:Description"; // Item XMP constants - URI and namespace prefix const string kItemUri = "http://ns.google.com/photos/1.0/container/item/"; const string kItemPrefix = "Item"; @@ -185,17 +235,23 @@ const string kSemanticRecoveryMap = "RecoveryMap"; const string kMimeImageJpeg = "image/jpeg"; // RecoveryMap XMP constants - URI and namespace prefix -const string kRecoveryMapUri = "http://ns.google.com/photos/1.0/recoverymap/"; -const string kRecoveryMapPrefix = "RecoveryMap"; +const string kRecoveryMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/"; +const string kRecoveryMapPrefix = "hdrgm"; // RecoveryMap XMP constants - element and attribute names -const string kMapMaxContentBoost = Name(kRecoveryMapPrefix, "MaxContentBoost"); -const string kMapMinContentBoost = Name(kRecoveryMapPrefix, "MinContentBoost"); const string kMapVersion = Name(kRecoveryMapPrefix, "Version"); +const string kMapGainMapMin = Name(kRecoveryMapPrefix, "GainMapMin"); +const string kMapGainMapMax = Name(kRecoveryMapPrefix, "GainMapMax"); +const string kMapGamma = Name(kRecoveryMapPrefix, "Gamma"); +const string kMapOffsetSdr = Name(kRecoveryMapPrefix, "OffsetSDR"); +const string kMapOffsetHdr = Name(kRecoveryMapPrefix, "OffsetHDR"); +const string kMapHDRCapacityMin = Name(kRecoveryMapPrefix, "HDRCapacityMin"); +const string kMapHDRCapacityMax = Name(kRecoveryMapPrefix, "HDRCapacityMax"); +const string kMapBaseRendition = Name(kRecoveryMapPrefix, "BaseRendition"); // RecoveryMap XMP constants - names for XMP handlers -const string XMPXmlHandler::maxContentBoostAttrName = kMapMaxContentBoost; -const string XMPXmlHandler::minContentBoostAttrName = kMapMinContentBoost; +const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin; +const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax; bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* metadata) { string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; @@ -243,7 +299,7 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta return true; } -string generateXmp(int secondary_image_length, jpegr_metadata& metadata) { +string generateXmpForPrimaryImage(int secondary_image_length) { const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")}); const vector<string> kLiItem({string("rdf:li"), kConItem}); @@ -257,7 +313,6 @@ string generateXmp(int secondary_image_length, jpegr_metadata& metadata) { writer.StartWritingElement("rdf:Description"); writer.WriteXmlns(kContainerPrefix, kContainerUri); writer.WriteXmlns(kItemPrefix, kItemUri); - writer.WriteXmlns(kRecoveryMapPrefix, kRecoveryMapUri); writer.StartWritingElements(kConDirSeq); size_t item_depth = writer.StartWritingElements(kLiItem); writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary); @@ -267,9 +322,33 @@ string generateXmp(int secondary_image_length, jpegr_metadata& metadata) { writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticRecoveryMap); writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg); writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length); + writer.FinishWriting(); + + return ss.str(); +} + +string generateXmpForSecondaryImage(jpegr_metadata& metadata) { + const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")}); + const vector<string> kLiItem({string("rdf:li"), kConItem}); + + 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(kRecoveryMapPrefix, kRecoveryMapUri); writer.WriteAttributeNameAndValue(kMapVersion, metadata.version); - writer.WriteAttributeNameAndValue(kMapMaxContentBoost, metadata.maxContentBoost); - writer.WriteAttributeNameAndValue(kMapMinContentBoost, metadata.minContentBoost); + writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost)); + writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost)); + writer.WriteAttributeNameAndValue(kMapGamma, "1"); + writer.WriteAttributeNameAndValue(kMapOffsetSdr, "0"); + writer.WriteAttributeNameAndValue(kMapOffsetHdr, "0"); + writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, "0"); + writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, "2.3"); + writer.WriteAttributeNameAndValue(kMapBaseRendition, "SDR"); writer.FinishWriting(); return ss.str(); diff --git a/libs/jpegrecoverymap/multipictureformat.cpp b/libs/jpegrecoverymap/multipictureformat.cpp new file mode 100644 index 0000000000..a219aef106 --- /dev/null +++ b/libs/jpegrecoverymap/multipictureformat.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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/multipictureformat.h> +#include <jpegrecoverymap/jpegrutils.h> + +namespace android::jpegrecoverymap { +size_t calculateMpfSize() { + return sizeof(kMpfSig) + // Signature + kMpEndianSize + // Endianness + sizeof(uint32_t) + // Index IFD Offset + sizeof(uint16_t) + // Tag count + kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each + sizeof(uint32_t) + // Attribute IFD offset + kNumPictures * kMPEntrySize; // MP Entries for each image +} + +sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset, + int secondary_image_size, int secondary_image_offset) { + size_t mpf_size = calculateMpfSize(); + sp<DataStruct> dataStruct = new DataStruct(mpf_size); + + dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig)); +#if USE_BIG_ENDIAN + dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize); +#else + dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize); +#endif + + // Set the Index IFD offset be the position after the endianness value and this offset. + constexpr uint32_t indexIfdOffset = + static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig)); + dataStruct->write32(Endian_SwapBE32(indexIfdOffset)); + + // We will write 3 tags (version, number of images, MP entries). + dataStruct->write16(Endian_SwapBE16(kTagSerializedCount)); + + // Write the version tag. + dataStruct->write16(Endian_SwapBE16(kVersionTag)); + dataStruct->write16(Endian_SwapBE16(kVersionType)); + dataStruct->write32(Endian_SwapBE32(kVersionCount)); + dataStruct->write(kVersionExpected, kVersionSize); + + // Write the number of images. + dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag)); + dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType)); + dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount)); + dataStruct->write32(Endian_SwapBE32(kNumPictures)); + + // Write the MP entries. + dataStruct->write16(Endian_SwapBE16(kMPEntryTag)); + dataStruct->write16(Endian_SwapBE16(kMPEntryType)); + dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures)); + const uint32_t mpEntryOffset = + static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far + sizeof(kMpfSig) + // Excluding the MPF signature + sizeof(uint32_t) + // The 4 bytes for this offset + sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset. + dataStruct->write32(Endian_SwapBE32(mpEntryOffset)); + + // Write the attribute IFD offset (zero because we don't write it). + dataStruct->write32(0); + + // Write the MP entries for primary image + dataStruct->write32( + Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary)); + dataStruct->write32(Endian_SwapBE32(primary_image_size)); + dataStruct->write32(Endian_SwapBE32(primary_image_offset)); + dataStruct->write16(0); + dataStruct->write16(0); + + // Write the MP entries for secondary image + dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg)); + dataStruct->write32(Endian_SwapBE32(secondary_image_size)); + dataStruct->write32(Endian_SwapBE32(secondary_image_offset)); + dataStruct->write16(0); + dataStruct->write16(0); + + return dataStruct; +} + +} // namespace android::jpegrecoverymap diff --git a/libs/jpegrecoverymap/recoverymapmath.cpp b/libs/jpegrecoverymap/recoverymapmath.cpp index 7812e18b23..20c32ed888 100644 --- a/libs/jpegrecoverymap/recoverymapmath.cpp +++ b/libs/jpegrecoverymap/recoverymapmath.cpp @@ -631,4 +631,11 @@ uint32_t colorToRgba1010102(Color e_gamma) { | (0x3 << 30); // Set alpha to 1.0 } +uint64_t colorToRgbaF16(Color e_gamma) { + return (uint64_t) floatToHalf(e_gamma.r) + | (((uint64_t) floatToHalf(e_gamma.g)) << 16) + | (((uint64_t) floatToHalf(e_gamma.b)) << 32) + | (((uint64_t) floatToHalf(1.0f)) << 48); +} + } // namespace android::jpegrecoverymap diff --git a/libs/jpegrecoverymap/tests/Android.bp b/libs/jpegrecoverymap/tests/Android.bp index 5a4edb2a25..d5da7fb646 100644 --- a/libs/jpegrecoverymap/tests/Android.bp +++ b/libs/jpegrecoverymap/tests/Android.bp @@ -39,7 +39,7 @@ cc_test { "libjpegdecoder", "libjpegencoder", "libjpegrecoverymap", - "libskia", + "libutils", ], } diff --git a/libs/jpegrecoverymap/tests/jpegr_test.cpp b/libs/jpegrecoverymap/tests/jpegr_test.cpp index 7a3133d10c..0a7d20a434 100644 --- a/libs/jpegrecoverymap/tests/jpegr_test.cpp +++ b/libs/jpegrecoverymap/tests/jpegr_test.cpp @@ -152,7 +152,7 @@ void JpegRBenchmark::BenchmarkApplyRecoveryMap(jr_uncompressed_ptr yuv420Image, timerStart(&applyRecMapTime); for (auto i = 0; i < kProfileCount; i++) { - ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, dest)); + ASSERT_EQ(OK, applyRecoveryMap(yuv420Image, map, metadata, JPEGR_OUTPUT_HDR_HLG, dest)); } timerStop(&applyRecMapTime); @@ -170,18 +170,17 @@ TEST_F(JpegRTest, build) { jpegRCodec.encodeJPEGR(nullptr, nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr); jpegRCodec.encodeJPEGR(nullptr, nullptr, static_cast<jpegr_transfer_function>(0), nullptr); - jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr, false); + jpegRCodec.decodeJPEGR(nullptr, nullptr, nullptr); } TEST_F(JpegRTest, writeXmpThenRead) { jpegr_metadata metadata_expected; metadata_expected.maxContentBoost = 1.25; metadata_expected.minContentBoost = 0.75; - int length_expected = 1000; const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0"; const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator - std::string xmp = generateXmp(1000, metadata_expected); + std::string xmp = generateXmpForSecondaryImage(metadata_expected); std::vector<uint8_t> xmpData; xmpData.reserve(nameSpaceLength + xmp.size()); @@ -220,7 +219,7 @@ TEST_F(JpegRTest, encodeFromP010ThenDecode) { } if (SAVE_ENCODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::string filePath = "/sdcard/Documents/encoded_from_p010_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()); @@ -229,7 +228,7 @@ TEST_F(JpegRTest, encodeFromP010ThenDecode) { } jpegr_uncompressed_struct decodedJpegR; - int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; + int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { @@ -237,7 +236,7 @@ TEST_F(JpegRTest, encodeFromP010ThenDecode) { } if (SAVE_DECODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::string filePath = "/sdcard/Documents/decoded_from_p010_input.rgb"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); @@ -281,7 +280,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) { } if (SAVE_ENCODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_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()); @@ -290,7 +289,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) { } jpegr_uncompressed_struct decodedJpegR; - int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; + int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { @@ -298,7 +297,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrThenDecode) { } if (SAVE_DECODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_input.rgb"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); @@ -346,7 +345,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) { } if (SAVE_ENCODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::string filePath = "/sdcard/Documents/encoded_from_p010_yuv420p_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()); @@ -355,7 +354,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) { } jpegr_uncompressed_struct decodedJpegR; - int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; + int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { @@ -363,7 +362,7 @@ TEST_F(JpegRTest, encodeFromRawHdrAndSdrAndJpegThenDecode) { } if (SAVE_DECODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::string filePath = "/sdcard/Documents/decoded_from_p010_yuv420p_jpeg_input.rgb"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); @@ -427,7 +426,7 @@ TEST_F(JpegRTest, encodeFromJpegThenDecode) { } if (SAVE_ENCODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/encoded_from_jpeg_input.jpgr"; + std::string filePath = "/sdcard/Documents/encoded_from_p010_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()); @@ -436,7 +435,7 @@ TEST_F(JpegRTest, encodeFromJpegThenDecode) { } jpegr_uncompressed_struct decodedJpegR; - int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 4; + int decodedJpegRSize = TEST_IMAGE_WIDTH * TEST_IMAGE_HEIGHT * 8; decodedJpegR.data = malloc(decodedJpegRSize); ret = jpegRCodec.decodeJPEGR(&jpegR, &decodedJpegR); if (ret != OK) { @@ -444,7 +443,7 @@ TEST_F(JpegRTest, encodeFromJpegThenDecode) { } if (SAVE_DECODING_RESULT) { // Output image data to file - std::string filePath = "/sdcard/Documents/decoded_from_jpeg_input.rgb10"; + std::string filePath = "/sdcard/Documents/decoded_from_p010_jpeg_input.rgb"; std::ofstream imageFile(filePath.c_str(), std::ofstream::binary); if (!imageFile.is_open()) { ALOGE("%s: Unable to create file %s", __FUNCTION__, filePath.c_str()); diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp index b7b2926c73..5306529fcb 100644 --- a/libs/nativewindow/ANativeWindow.cpp +++ b/libs/nativewindow/ANativeWindow.cpp @@ -24,14 +24,49 @@ #include <private/android/AHardwareBufferHelpers.h> +#include <android/binder_libbinder.h> +#include <dlfcn.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; +#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__) +#error libnativewindow can only be built for system +#endif + +using android_view_Surface_writeToParcel = status_t (*)(ANativeWindow* _Nonnull window, + Parcel* _Nonnull parcel); + +using android_view_Surface_readFromParcel = + status_t (*)(const Parcel* _Nonnull parcel, ANativeWindow* _Nullable* _Nonnull outWindow); + +struct SurfaceParcelables { + android_view_Surface_writeToParcel write = nullptr; + android_view_Surface_readFromParcel read = nullptr; +}; + +const SurfaceParcelables* getSurfaceParcelFunctions() { + static SurfaceParcelables funcs = []() -> SurfaceParcelables { + SurfaceParcelables ret; + void* dl = dlopen("libgui.so", RTLD_NOW); + LOG_ALWAYS_FATAL_IF(!dl, "Failed to find libgui.so"); + ret.write = + (android_view_Surface_writeToParcel)dlsym(dl, "android_view_Surface_writeToParcel"); + LOG_ALWAYS_FATAL_IF(!ret.write, + "libgui.so missing android_view_Surface_writeToParcel; " + "loaded wrong libgui?"); + ret.read = + (android_view_Surface_readFromParcel)dlsym(dl, + "android_view_Surface_readFromParcel"); + LOG_ALWAYS_FATAL_IF(!ret.read, + "libgui.so missing android_view_Surface_readFromParcel; " + "loaded wrong libgui?"); + return ret; + }(); + return &funcs; +} + static int32_t query(ANativeWindow* window, int what) { int value; int res = window->query(window, what, &value); @@ -64,13 +99,6 @@ 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 @@ -355,38 +383,24 @@ int ANativeWindow_setAutoPrerotation(ANativeWindow* window, bool 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; + auto funcs = getSurfaceParcelFunctions(); + if (funcs->read == nullptr) { + ALOGE("Failed to load Surface_readFromParcel implementation"); + return STATUS_FAILED_TRANSACTION; } - sp<Surface> surface = sp<Surface>::make( - shimSurface->graphicBufferProducer, false, shimSurface->surfaceControlHandle); - ANativeWindow* anw = surface.get(); - ANativeWindow_acquire(anw); - *outWindow = anw; - return STATUS_OK; + const Parcel* nativeParcel = AParcel_viewPlatformParcel(parcel); + return funcs->read(nativeParcel, outWindow); } 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; + auto funcs = getSurfaceParcelFunctions(); + if (funcs->write == nullptr) { + ALOGE("Failed to load Surface_writeToParcel implementation"); + return STATUS_FAILED_TRANSACTION; } - // 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); + return funcs->write(window, nativeParcel); } /************************************************************************************************** diff --git a/libs/renderengine/ExternalTexture.cpp b/libs/renderengine/ExternalTexture.cpp index 84771c0917..210dca5429 100644 --- a/libs/renderengine/ExternalTexture.cpp +++ b/libs/renderengine/ExternalTexture.cpp @@ -39,7 +39,7 @@ ExternalTexture::ExternalTexture(const sp<GraphicBuffer>& buffer, } ExternalTexture::~ExternalTexture() { - mRenderEngine.unmapExternalTextureBuffer(mBuffer); + mRenderEngine.unmapExternalTextureBuffer(std::move(mBuffer)); } } // namespace android::renderengine::impl diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 13f766c3e1..0d7df101f4 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -800,7 +800,7 @@ status_t GLESRenderEngine::cacheExternalTextureBufferInternal(const sp<GraphicBu return NO_ERROR; } -void GLESRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { +void GLESRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { mImageManager->releaseAsync(buffer->getId(), nullptr); } @@ -1262,7 +1262,7 @@ void GLESRenderEngine::drawLayersInternal( // Do not cache protected EGLImage, protected memory is limited. if (gBuf->getUsage() & GRALLOC_USAGE_PROTECTED) { - unmapExternalTextureBuffer(gBuf); + unmapExternalTextureBuffer(std::move(gBuf)); } } diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 1b3492154b..402ff529d7 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -101,7 +101,7 @@ protected: size_t getMaxViewportDims() const override; void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) EXCLUDES(mRenderingMutex); - void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) EXCLUDES(mRenderingMutex); + void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) EXCLUDES(mRenderingMutex); bool canSkipPostRenderCleanup() const override; void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 39621cd080..0d910c9b29 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -231,7 +231,7 @@ protected: // asynchronously, but the caller can expect that map/unmap calls are performed in a manner // that's conflict serializable, i.e. unmap a buffer should never occur before binding the // buffer if the caller called mapExternalTextureBuffer before calling unmap. - virtual void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) = 0; + virtual void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) = 0; // A thread safe query to determine if any post rendering cleanup is necessary. Returning true // is a signal that calling the postRenderCleanup method would be a no-op and that callers can diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index e3ce85dd07..d3035e24a5 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -63,7 +63,7 @@ public: protected: // mock renderengine still needs to implement these, but callers should never need to call them. void mapExternalTextureBuffer(const sp<GraphicBuffer>&, bool) {} - void unmapExternalTextureBuffer(const sp<GraphicBuffer>&) {} + void unmapExternalTextureBuffer(sp<GraphicBuffer>&&) {} }; } // namespace mock diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index 413811ef99..5965d417f4 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -423,7 +423,7 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, } } -void SkiaRenderEngine::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { +void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mRenderingMutex); if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId()); diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h index 1973c7d065..dd6646ba71 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.h +++ b/libs/renderengine/skia/SkiaRenderEngine.h @@ -130,7 +130,7 @@ protected: private: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override final; - void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override final; + void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override final; bool canSkipPostRenderCleanup() const override final; void initCanvas(SkCanvas* canvas, const DisplaySettings& display); diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 8d99f3d320..936e31679f 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -444,8 +444,11 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { 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, GetDeviceQueue2); + const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr, + deviceQueueCreateFlags, + (uint32_t)graphicsQueueIndex, 0}; + vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue); VK_GET_DEV_PROC(device, DeviceWaitIdle); VK_GET_DEV_PROC(device, DestroyDevice); diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp index 8aa41b3e50..6a1561abcd 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.cpp +++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp @@ -230,16 +230,17 @@ void RenderEngineThreaded::mapExternalTextureBuffer(const sp<GraphicBuffer>& buf mCondition.notify_one(); } -void RenderEngineThreaded::unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) { +void RenderEngineThreaded::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { ATRACE_CALL(); // This function is designed so it can run asynchronously, so we do not need to wait // for the futures. { std::lock_guard lock(mThreadMutex); - mFunctionCalls.push([=](renderengine::RenderEngine& instance) { - ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); - instance.unmapExternalTextureBuffer(buffer); - }); + mFunctionCalls.push( + [=, buffer = std::move(buffer)](renderengine::RenderEngine& instance) mutable { + ATRACE_NAME("REThreaded::unmapExternalTextureBuffer"); + instance.unmapExternalTextureBuffer(std::move(buffer)); + }); } mCondition.notify_one(); } diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h index 168e2d2b06..6eb108e064 100644 --- a/libs/renderengine/threaded/RenderEngineThreaded.h +++ b/libs/renderengine/threaded/RenderEngineThreaded.h @@ -69,7 +69,7 @@ public: protected: void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, bool isRenderable) override; - void unmapExternalTextureBuffer(const sp<GraphicBuffer>& buffer) override; + void unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) override; bool canSkipPostRenderCleanup() const override; void drawLayersInternal(const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, const DisplaySettings& display, diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp index 2278d391b5..019d6cb070 100644 --- a/libs/sensor/ISensorServer.cpp +++ b/libs/sensor/ISensorServer.cpp @@ -67,7 +67,11 @@ public: v.setCapacity(n); while (n) { n--; - reply.read(s); + if(reply.read(s) != OK) { + ALOGE("Failed to read reply from getSensorList"); + v.clear(); + break; + } v.add(s); } return v; @@ -85,7 +89,11 @@ public: v.setCapacity(n); while (n) { n--; - reply.read(s); + if(reply.read(s) != OK) { + ALOGE("Failed to read reply from getDynamicSensorList"); + v.clear(); + break; + } v.add(s); } return v; @@ -131,10 +139,12 @@ public: } virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, - uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) { + int deviceId, uint32_t size, int32_t type, int32_t format, + const native_handle_t *resource) { Parcel data, reply; data.writeInterfaceToken(ISensorServer::getInterfaceDescriptor()); data.writeString16(opPackageName); + data.writeInt32(deviceId); data.writeUint32(size); data.writeInt32(type); data.writeInt32(format); @@ -229,6 +239,7 @@ status_t BnSensorServer::onTransact( case CREATE_SENSOR_DIRECT_CONNECTION: { CHECK_INTERFACE(ISensorServer, data, reply); const String16& opPackageName = data.readString16(); + const int deviceId = data.readInt32(); uint32_t size = data.readUint32(); int32_t type = data.readInt32(); int32_t format = data.readInt32(); @@ -238,8 +249,8 @@ status_t BnSensorServer::onTransact( return BAD_VALUE; } native_handle_set_fdsan_tag(resource); - sp<ISensorEventConnection> ch = - createSensorDirectConnection(opPackageName, size, type, format, resource); + sp<ISensorEventConnection> ch = createSensorDirectConnection( + opPackageName, deviceId, size, type, format, resource); native_handle_close_with_tag(resource); native_handle_delete(resource); reply->writeStrongBinder(IInterface::asBinder(ch)); diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp index fb895f59b9..b6ea77deb5 100644 --- a/libs/sensor/Sensor.cpp +++ b/libs/sensor/Sensor.cpp @@ -628,7 +628,13 @@ bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& output return false; } outputString8.setTo(static_cast<char const*>(buffer), len); + + if (size < FlattenableUtils::align<4>(len)) { + ALOGE("Malformed Sensor String8 field. Should be in a 4-byte aligned buffer but is not."); + return false; + } FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len)); + return true; } diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index 27482768f2..ba190e0855 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -92,6 +92,16 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName) return *sensorManager; } +void SensorManager::removeInstanceForPackage(const String16& packageName) { + Mutex::Autolock _l(sLock); + auto iterator = sPackageInstances.find(packageName); + if (iterator != sPackageInstances.end()) { + SensorManager* sensorManager = iterator->second; + delete sensorManager; + sPackageInstances.erase(iterator); + } +} + SensorManager::SensorManager(const String16& opPackageName) : mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) { Mutex::Autolock _l(mLock); @@ -166,6 +176,11 @@ status_t SensorManager::assertStateLocked() { mSensors = mSensorServer->getSensorList(mOpPackageName); size_t count = mSensors.size(); + if (count == 0) { + ALOGE("Failed to get Sensor list"); + mSensorServer.clear(); + return UNKNOWN_ERROR; + } mSensorList = static_cast<Sensor const**>(malloc(count * sizeof(Sensor*))); LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL"); @@ -300,6 +315,12 @@ bool SensorManager::isDataInjectionEnabled() { int SensorManager::createDirectChannel( size_t size, int channelType, const native_handle_t *resourceHandle) { + static constexpr int DEFAULT_DEVICE_ID = 0; + return createDirectChannel(DEFAULT_DEVICE_ID, size, channelType, resourceHandle); +} + +int SensorManager::createDirectChannel( + int deviceId, size_t size, int channelType, const native_handle_t *resourceHandle) { Mutex::Autolock _l(mLock); if (assertStateLocked() != NO_ERROR) { return NO_INIT; @@ -312,7 +333,7 @@ int SensorManager::createDirectChannel( } sp<ISensorEventConnection> conn = - mSensorServer->createSensorDirectConnection(mOpPackageName, + mSensorServer->createSensorDirectConnection(mOpPackageName, deviceId, static_cast<uint32_t>(size), static_cast<int32_t>(channelType), SENSOR_DIRECT_FMT_SENSORS_EVENT, resourceHandle); diff --git a/libs/sensor/include/sensor/ISensorServer.h b/libs/sensor/include/sensor/ISensorServer.h index 3295196ac4..58157280b1 100644 --- a/libs/sensor/include/sensor/ISensorServer.h +++ b/libs/sensor/include/sensor/ISensorServer.h @@ -50,7 +50,8 @@ public: virtual int32_t isDataInjectionEnabled() = 0; virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, - uint32_t size, int32_t type, int32_t format, const native_handle_t *resource) = 0; + int deviceId, uint32_t size, int32_t type, int32_t format, + const native_handle_t *resource) = 0; virtual int setOperationParameter( int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) = 0; diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h index 0798da292a..bb44cb8869 100644 --- a/libs/sensor/include/sensor/SensorManager.h +++ b/libs/sensor/include/sensor/SensorManager.h @@ -54,6 +54,7 @@ class SensorManager : public ASensorManager { public: static SensorManager& getInstanceForPackage(const String16& packageName); + static void removeInstanceForPackage(const String16& packageName); ~SensorManager(); ssize_t getSensorList(Sensor const* const** list); @@ -65,6 +66,8 @@ public: String8 packageName = String8(""), int mode = 0, String16 attributionTag = String16("")); bool isDataInjectionEnabled(); int createDirectChannel(size_t size, int channelType, const native_handle_t *channelData); + int createDirectChannel( + int deviceId, size_t size, int channelType, const native_handle_t *channelData); void destroyDirectChannel(int channelNativeHandle); int configureDirectChannel(int channelNativeHandle, int sensorHandle, int rateLevel); int setOperationParameter(int handle, int type, const Vector<float> &floats, const Vector<int32_t> &ints); diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp index 7459466d31..c3af996b13 100644 --- a/libs/ui/Gralloc4.cpp +++ b/libs/ui/Gralloc4.cpp @@ -22,6 +22,8 @@ #include <aidlcommonsupport/NativeHandle.h> #include <android/binder_enums.h> #include <android/binder_manager.h> +#include <cutils/android_filesystem_config.h> +#include <cutils/multiuser.h> #include <gralloctypes/Gralloc4.h> #include <hidl/ServiceManagement.h> #include <hwbinder/IPCThreadState.h> @@ -1195,8 +1197,15 @@ Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(map mAllocator = IAllocator::getService(); if (__builtin_available(android 31, *)) { if (hasIAllocatorAidl()) { - mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( - AServiceManager_waitForService(kAidlAllocatorServiceName.c_str()))); + // TODO(b/269517338): Perform the isolated checking for this in service manager instead. + uid_t aid = multiuser_get_app_id(getuid()); + if (aid >= AID_ISOLATED_START && aid <= AID_ISOLATED_END) { + mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( + AServiceManager_getService(kAidlAllocatorServiceName.c_str()))); + } else { + mAidlAllocator = AidlIAllocator::fromBinder(ndk::SpAIBinder( + AServiceManager_waitForService(kAidlAllocatorServiceName.c_str()))); + } ALOGE_IF(!mAidlAllocator, "AIDL IAllocator declared but failed to get service"); } } diff --git a/opengl/OWNERS b/opengl/OWNERS index 379f7638f0..3d60a1dad6 100644 --- a/opengl/OWNERS +++ b/opengl/OWNERS @@ -1,11 +1,6 @@ -abdolrashidi@google.com -cclao@google.com chrisforbes@google.com cnorthrop@google.com ianelliott@google.com jessehall@google.com -lfy@google.com lpy@google.com -romanl@google.com vantablack@google.com -yuxinhu@google.com diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index 86c788d3b3..aecfc6b077 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -231,7 +231,7 @@ int BlobCache::flatten(void* buffer, size_t size) const { int BlobCache::unflatten(void const* buffer, size_t size) { // All errors should result in the BlobCache being in an empty state. - mCacheEntries.clear(); + clear(); // Read the cache header if (size < sizeof(Header)) { @@ -258,7 +258,7 @@ int BlobCache::unflatten(void const* buffer, size_t size) { size_t numEntries = header->mNumEntries; for (size_t i = 0; i < numEntries; i++) { if (byteOffset + sizeof(EntryHeader) > size) { - mCacheEntries.clear(); + clear(); ALOGE("unflatten: not enough room for cache entry headers"); return -EINVAL; } @@ -270,7 +270,7 @@ int BlobCache::unflatten(void const* buffer, size_t size) { size_t totalSize = align4(entrySize); if (byteOffset + totalSize > size) { - mCacheEntries.clear(); + clear(); ALOGE("unflatten: not enough room for cache entry headers"); return -EINVAL; } diff --git a/opengl/libs/EGL/BlobCache.h b/opengl/libs/EGL/BlobCache.h index ff03d30099..52078ff5fd 100644 --- a/opengl/libs/EGL/BlobCache.h +++ b/opengl/libs/EGL/BlobCache.h @@ -117,7 +117,10 @@ public: // clear flushes out all contents of the cache then the BlobCache, leaving // it in an empty state. - void clear() { mCacheEntries.clear(); } + void clear() { + mCacheEntries.clear(); + mTotalSize = 0; + } protected: // mMaxTotalSize is the maximum size that all cache entries can occupy. This diff --git a/opengl/libs/EGL/BlobCache_test.cpp b/opengl/libs/EGL/BlobCache_test.cpp index ceea0fb979..450c12837b 100644 --- a/opengl/libs/EGL/BlobCache_test.cpp +++ b/opengl/libs/EGL/BlobCache_test.cpp @@ -466,4 +466,31 @@ TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) { ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4)); } +// Test for a divide by zero bug (b/239862516). Before the fix, unflatten() would not reset +// mTotalSize when it encountered an error, which would trigger division by 0 in clean() in the +// right conditions. +TEST_F(BlobCacheFlattenTest, SetAfterFailedUnflatten) { + // isCleanable() must be true, so mTotalSize must be > mMaxTotalSize / 2 after unflattening + // after one entry is lost. To make this the case, MaxTotalSize is 30 and three 10 sized + // entries are used. One of those entries is lost, resulting in mTotalSize=20 + const size_t kMaxKeySize = 10; + const size_t kMaxValueSize = 10; + const size_t kMaxTotalSize = 30; + mBC.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize)); + mBC2.reset(new BlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize)); + mBC->set("aaaaa", 5, "aaaaa", 5); + mBC->set("bbbbb", 5, "bbbbb", 5); + mBC->set("ccccc", 5, "ccccc", 5); + + size_t size = mBC->getFlattenedSize(); + uint8_t* flat = new uint8_t[size]; + ASSERT_EQ(OK, mBC->flatten(flat, size)); + + ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size - 10)); + delete[] flat; + + // This line will trigger clean() which caused a crash. + mBC2->set("dddddddddd", 10, "dddddddddd", 10); +} + } // namespace android diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index b00ee33374..3dc93ee0b7 100644 --- a/opengl/libs/EGL/egl_cache.cpp +++ b/opengl/libs/EGL/egl_cache.cpp @@ -110,38 +110,6 @@ void egl_cache_t::initialize(egl_display_t* display) { } } - // Check the device config to decide whether multifile should be used - if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) { - mMultifileMode = true; - ALOGV("Using multifile EGL blobcache"); - } - - // Allow forcing the mode for debug purposes - std::string mode = base::GetProperty("debug.egl.blobcache.multifile", ""); - if (mode == "true") { - ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str()); - mMultifileMode = true; - } else if (mode == "false") { - ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str()); - mMultifileMode = false; - } - - if (mMultifileMode) { - mCacheByteLimit = static_cast<size_t>( - base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit", - kMultifileCacheByteLimit)); - - // Check for a debug value - int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1); - if (debugCacheSize >= 0) { - ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit", - mCacheByteLimit, debugCacheSize); - mCacheByteLimit = debugCacheSize; - } - - ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit); - } - mInitialized = true; } @@ -167,6 +135,8 @@ void egl_cache_t::setBlob(const void* key, EGLsizeiANDROID keySize, const void* return; } + updateMode(); + if (mInitialized) { if (mMultifileMode) { MultifileBlobCache* mbc = getMultifileBlobCacheLocked(); @@ -200,6 +170,8 @@ EGLsizeiANDROID egl_cache_t::getBlob(const void* key, EGLsizeiANDROID keySize, v return 0; } + updateMode(); + if (mInitialized) { if (mMultifileMode) { MultifileBlobCache* mbc = getMultifileBlobCacheLocked(); @@ -247,6 +219,51 @@ size_t egl_cache_t::getCacheSize() { return 0; } +void egl_cache_t::updateMode() { + // We don't set the mode in the constructor because these checks have + // a non-trivial cost, and not all processes that instantiate egl_cache_t + // will use it. + + // If we've already set the mode, skip these checks + static bool checked = false; + if (checked) { + return; + } + checked = true; + + // Check the device config to decide whether multifile should be used + if (base::GetBoolProperty("ro.egl.blobcache.multifile", false)) { + mMultifileMode = true; + ALOGV("Using multifile EGL blobcache"); + } + + // Allow forcing the mode for debug purposes + std::string mode = base::GetProperty("debug.egl.blobcache.multifile", ""); + if (mode == "true") { + ALOGV("Forcing multifile cache due to debug.egl.blobcache.multifile == %s", mode.c_str()); + mMultifileMode = true; + } else if (mode == "false") { + ALOGV("Forcing monolithic cache due to debug.egl.blobcache.multifile == %s", mode.c_str()); + mMultifileMode = false; + } + + if (mMultifileMode) { + mCacheByteLimit = static_cast<size_t>( + base::GetUintProperty<uint32_t>("ro.egl.blobcache.multifile_limit", + kMultifileCacheByteLimit)); + + // Check for a debug value + int debugCacheSize = base::GetIntProperty("debug.egl.blobcache.multifile_limit", -1); + if (debugCacheSize >= 0) { + ALOGV("Overriding cache limit %zu with %i from debug.egl.blobcache.multifile_limit", + mCacheByteLimit, debugCacheSize); + mCacheByteLimit = debugCacheSize; + } + + ALOGV("Using multifile EGL blobcache limit of %zu bytes", mCacheByteLimit); + } +} + BlobCache* egl_cache_t::getBlobCacheLocked() { if (mBlobCache == nullptr) { mBlobCache.reset(new FileBlobCache(kMaxMonolithicKeySize, kMaxMonolithicValueSize, diff --git a/opengl/libs/EGL/egl_cache.h b/opengl/libs/EGL/egl_cache.h index 1399368dd8..ae6d38146e 100644 --- a/opengl/libs/EGL/egl_cache.h +++ b/opengl/libs/EGL/egl_cache.h @@ -88,6 +88,9 @@ private: egl_cache_t(const egl_cache_t&); // not implemented void operator=(const egl_cache_t&); // not implemented + // Check system properties to determine which blobcache mode should be used + void updateMode(); + // getBlobCacheLocked returns the BlobCache object being used to store the // key/value blob pairs. If the BlobCache object has not yet been created, // this will do so, loading the serialized cache contents from disk if diff --git a/services/batteryservice/include/batteryservice/BatteryService.h b/services/batteryservice/include/batteryservice/BatteryService.h index a2e4115bd6..bf6189d7af 100644 --- a/services/batteryservice/include/batteryservice/BatteryService.h +++ b/services/batteryservice/include/batteryservice/BatteryService.h @@ -37,6 +37,7 @@ enum { BATTERY_PROP_CHARGING_POLICY = 7, // equals BATTERY_PROPERTY_CHARGING_POLICY BATTERY_PROP_MANUFACTURING_DATE = 8, // equals BATTERY_PROPERTY_MANUFACTURING_DATE BATTERY_PROP_FIRST_USAGE_DATE = 9, // equals BATTERY_PROPERTY_FIRST_USAGE_DATE + BATTERY_PROP_STATE_OF_HEALTH = 10, // equals BATTERY_PROPERTY_STATE_OF_HEALTH }; struct BatteryProperties { diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index ab5c5ef99c..da4e42f4fe 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -34,6 +34,7 @@ filegroup { srcs: [ "AnrTracker.cpp", "Connection.cpp", + "DebugConfig.cpp", "DragState.cpp", "Entry.cpp", "FocusResolver.cpp", diff --git a/services/inputflinger/dispatcher/DebugConfig.cpp b/services/inputflinger/dispatcher/DebugConfig.cpp new file mode 100644 index 0000000000..764194d3d0 --- /dev/null +++ b/services/inputflinger/dispatcher/DebugConfig.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "DebugConfig.h" + +#include <android-base/properties.h> + +namespace android::inputdispatcher { + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +bool debugInboundEventDetails() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_INBOUND_EVENT_DETAILS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", + ANDROID_LOG_INFO); + return DEBUG_INBOUND_EVENT_DETAILS; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index d2ad40754a..0e260a7a03 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -22,12 +22,20 @@ #include <log/log_event_list.h> namespace android::inputdispatcher { + +/** + * Signals whether this is a debuggable Android build. + * This is populated by reading the value of the "ro.debuggable" property. + */ +extern const bool IS_DEBUGGABLE_BUILD; + /** * Log detailed debug messages about each inbound event notification to the dispatcher. - * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG" (requires restart) + * Enable this via "adb shell setprop log.tag.InputDispatcherInboundEvent DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). */ -const bool DEBUG_INBOUND_EVENT_DETAILS = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundEvent", ANDROID_LOG_INFO); +bool debugInboundEventDetails(); /** * Log detailed debug messages about each outbound event processed by the dispatcher. @@ -90,4 +98,5 @@ 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 diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index ce7c882f7d..b625a1b95b 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -14,16 +14,17 @@ * limitations under the License. */ +#define LOG_TAG "InputDispatcher" + #include "Entry.h" #include "Connection.h" +#include "DebugConfig.h" -#include <android-base/properties.h> #include <android-base/stringprintf.h> #include <cutils/atomic.h> #include <inttypes.h> -using android::base::GetBoolProperty; using android::base::StringPrintf; namespace android::inputdispatcher { @@ -172,7 +173,7 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou KeyEntry::~KeyEntry() {} std::string KeyEntry::getDescription() const { - if (!GetBoolProperty("ro.debuggable", false)) { + if (!IS_DEBUGGABLE_BUILD) { return "KeyEvent"; } return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32 @@ -242,7 +243,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 MotionEntry::~MotionEntry() {} std::string MotionEntry::getDescription() const { - if (!GetBoolProperty("ro.debuggable", false)) { + if (!IS_DEBUGGABLE_BUILD) { return "MotionEvent"; } std::string msg; @@ -292,7 +293,7 @@ std::string SensorEntry::getDescription() const { deviceId, inputEventSourceToString(source).c_str(), ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp); - if (!GetBoolProperty("ro.debuggable", false)) { + if (IS_DEBUGGABLE_BUILD) { for (size_t i = 0; i < values.size(); i++) { if (i > 0) { msg += ", "; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9d5bbbdd11..cd427f03cf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -110,6 +110,8 @@ constexpr int LOGTAG_INPUT_INTERACTION = 62000; constexpr int LOGTAG_INPUT_FOCUS = 62001; constexpr int LOGTAG_INPUT_CANCEL = 62003; +const ui::Transform kIdentityTransform; + inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } @@ -475,8 +477,8 @@ bool isUserActivityEvent(const EventEntry& eventEntry) { } // Returns true if the given window can accept pointer events at the given display location. -bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y, - bool isStylus) { +bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y, + bool isStylus, const ui::Transform& displayTransform) { const auto inputConfig = windowInfo.inputConfig; if (windowInfo.displayId != displayId || inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) { @@ -486,7 +488,17 @@ bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32 if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) { return false; } - if (!windowInfo.touchableRegionContainsPoint(x, y)) { + + // Window Manager works in the logical display coordinate space. When it specifies bounds for a + // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside + // the window. Points on the right and bottom edges should not be inside the window, so we need + // to be careful about performing a hit test when the display is rotated, since the "right" and + // "bottom" of the window will be different in the display (un-rotated) space compared to in the + // logical display in which WM determined the bounds. Perform the hit test in the logical + // display space to ensure these edges are considered correctly in all orientations. + const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion); + const auto p = displayTransform.transform(x, y); + if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) { return false; } return true; @@ -540,19 +552,16 @@ std::optional<std::string> verifyTargetedInjection(const sp<WindowInfoHandle>& w return {}; } -Point resolveTouchedPosition(const MotionEntry& entry) { +std::pair<float, float> 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)); + return {entry.xCursorPosition, 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))); + return {entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X), + entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y)}; } std::optional<nsecs_t> getDownTime(const EventEntry& eventEntry) { @@ -1159,7 +1168,7 @@ void InputDispatcher::addRecentEventLocked(std::shared_ptr<EventEntry> entry) { } std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>> -InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus, +InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus, bool ignoreDragWindow) const { // Traverse windows from front to back to find touched window. std::vector<InputTarget> outsideTargets; @@ -1170,7 +1179,8 @@ InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t } const WindowInfo& info = *windowHandle->getInfo(); - if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { + if (!info.isSpy() && + windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) { return {windowHandle, outsideTargets}; } @@ -1184,14 +1194,14 @@ InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t } std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked( - int32_t displayId, int32_t x, int32_t y, bool isStylus) const { + int32_t displayId, float x, float y, bool isStylus) const { // Traverse windows from front to back and gather the touched spy windows. std::vector<sp<WindowInfoHandle>> spyWindows; const auto& windowHandles = getWindowHandlesLocked(displayId); for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { const WindowInfo& info = *windowHandle->getInfo(); - if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus)) { + if (!windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) { continue; } if (!info.isSpy()) { @@ -1207,7 +1217,7 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason const char* reason; switch (dropReason) { case DropReason::POLICY: - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("Dropped event because policy consumed it."); } reason = "inbound event was dropped because the policy consumed it"; @@ -1596,7 +1606,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<Key } else if (entry->action == AKEY_EVENT_ACTION_UP && mKeyRepeatState.lastKeyEntry && mKeyRepeatState.lastKeyEntry->deviceId != entry->deviceId) { // The key on device 'deviceId' is still down, do not stop key repeat - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("deviceId=%d got KEY_UP as stale", entry->deviceId); } } else if (!entry->syntheticRepeat) { @@ -2184,18 +2194,20 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction; const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE); + // If pointers are already down, let's finish the current gesture and ignore the new events + // from another device. However, if the new event is a down event, let's cancel the current + // touch and let the new one take over. + if (switchedDevice && wasDown && !isDown) { + LOG(INFO) << "Dropping event because a pointer for device " << oldState->deviceId + << " is already down in display " << displayId << ": " << entry.getDescription(); + // TODO(b/211379801): test multiple simultaneous input streams. + outInjectionResult = InputEventInjectionResult::FAILED; + return {}; // wrong device + } + if (newGesture) { - // If pointers are already down, let's finish the current gesture and ignore the new events - // from another device. - if (switchedDevice && wasDown) { - ALOGI("Dropping event because a pointer for a different device is already down " - "in display %" PRId32, - displayId); - // TODO: test multiple simultaneous input streams. - outInjectionResult = InputEventInjectionResult::FAILED; - return {}; // wrong device - } - tempTouchState.clearWindowsWithoutPointers(); + // If a new gesture is starting, clear the touch state completely. + tempTouchState.reset(); tempTouchState.deviceId = entry.deviceId; tempTouchState.source = entry.source; isSplit = false; @@ -2203,7 +2215,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( ALOGI("Dropping move event because a pointer for a different device is already active " "in display %" PRId32, displayId); - // TODO: test multiple simultaneous input streams. + // TODO(b/211379801): test multiple simultaneous input streams. outInjectionResult = InputEventInjectionResult::FAILED; return {}; // wrong device } @@ -2229,8 +2241,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } // Handle the case where we did not find a window. if (newTouchedWindowHandle == nullptr) { - ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y, - displayId); + ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId); // Try to assign the pointer to the first foreground window we find, if there is one. newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(); } @@ -2268,7 +2279,8 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } if (newTouchedWindows.empty()) { - ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.", + ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display " + "%d.", x, y, displayId); outInjectionResult = InputEventInjectionResult::FAILED; return {}; @@ -2317,6 +2329,10 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN || maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN; + // TODO(b/211379801): Currently, even if pointerIds are empty (hover case), we would + // still add a window to the touch state. We should avoid doing that, but some of the + // later checks ("at least one foreground window") rely on this in order to dispatch + // the event properly, so that needs to be updated, possibly by looking at InputTargets. tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds, isDownOrPointerDown ? std::make_optional(entry.eventTime) @@ -2369,10 +2385,9 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // If the pointer is not currently down, then ignore the event. 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()); + LOG(INFO) << "Dropping event because the pointer is not down or we previously " + "dropped the pointer down event in display " + << displayId << ": " << entry.getDescription(); outInjectionResult = InputEventInjectionResult::FAILED; return {}; } @@ -2530,7 +2545,6 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } // Success! Output targets from the touch state. - tempTouchState.clearWindowsWithoutPointers(); for (const TouchedWindow& touchedWindow : tempTouchState.windows) { if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) { // Windows with hovering pointers are getting persisted inside TouchState. @@ -2570,14 +2584,13 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( } else if (maskedAction == AMOTION_EVENT_ACTION_UP) { // Pointer went up. tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id); - tempTouchState.clearWindowsWithoutPointers(); } else if (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."); + if (oldState && (oldState->isDown() || oldState->hasHoveringPointers())) { + ALOGD("Conflicting pointer actions: Down received while already down or hovering."); *outConflictingPointerActions = true; } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { @@ -2600,6 +2613,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // state was only valid for this one action. if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) { if (displayId >= 0) { + tempTouchState.clearWindowsWithoutPointers(); mTouchStatesByDisplay[displayId] = tempTouchState; } else { mTouchStatesByDisplay.erase(displayId); @@ -2741,7 +2755,8 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa if (displayInfoIt != mDisplayInfos.end()) { inputTarget.displayTransform = displayInfoIt->second.transform; } else { - ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId); + // DisplayInfo not found for this window on display windowInfo->displayId. + // TODO(b/198444055): Make this an error message after 'setInputWindows' API is removed. } inputTargets.push_back(inputTarget); it = inputTargets.end() - 1; @@ -3048,9 +3063,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, 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"); + if (!inputTarget.firstDownTimeInTarget.has_value()) { + logDispatchStateLocked(); + LOG(FATAL) << "Splitting motion events requires a down time to be set for the " + "target on connection " + << connection->getInputChannelName() << " for " + << originalMotionEntry.getDescription(); + } std::unique_ptr<MotionEntry> splitMotionEntry = splitMotionEvent(originalMotionEntry, inputTarget.pointerIds, inputTarget.firstDownTimeInTarget.value()); @@ -3931,8 +3950,8 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( // in this way. ALOGW("Dropping split motion event because the pointer count is %d but " "we expected there to be %zu pointers. This probably means we received " - "a broken sequence of pointer ids from the input device.", - splitPointerCount, pointerIds.count()); + "a broken sequence of pointer ids from the input device: %s", + splitPointerCount, pointerIds.count(), originalMotionEntry.getDescription().c_str()); return nullptr; } @@ -4007,7 +4026,7 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( } void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifyConfigurationChanged - eventTime=%" PRId64, args->eventTime); } @@ -4027,18 +4046,20 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange /** * If one of the meta shortcuts is detected, process them here: - * Meta + Backspace -> generate BACK - * Meta + Enter -> generate HOME - * This will potentially overwrite keyCode and metaState. + * Meta + Backspace; Meta + Grave; Meta + Left arrow -> generate BACK + * Most System shortcuts are handled in PhoneWindowManager.java except 'Back' shortcuts. Unlike + * Back, other shortcuts DO NOT need to be sent to applications and are fully handled by the system. + * But for Back key and Back shortcuts, we need to send KEYCODE_BACK to applications which can + * potentially handle the back key presses. + * Note: We don't send any Meta based KeyEvents to applications, so we need to convert to a KeyEvent + * where meta modifier is off before sending. Currently only use case is 'Back'. */ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode, int32_t& metaState) { if (metaState & AMETA_META_ON && action == AKEY_EVENT_ACTION_DOWN) { int32_t newKeyCode = AKEYCODE_UNKNOWN; - if (keyCode == AKEYCODE_DEL) { + if (keyCode == AKEYCODE_DEL || keyCode == AKEYCODE_GRAVE || keyCode == AKEYCODE_DPAD_LEFT) { newKeyCode = AKEYCODE_BACK; - } else if (keyCode == AKEYCODE_ENTER) { - newKeyCode = AKEYCODE_HOME; } if (newKeyCode != AKEYCODE_UNKNOWN) { std::scoped_lock _l(mLock); @@ -4063,14 +4084,15 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 } void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { - ALOGD("notifyKey - eventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 - "policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%" PRId64, - args->eventTime, args->deviceId, args->source, args->displayId, args->policyFlags, - args->action, args->flags, args->keyCode, args->scanCode, args->metaState, - args->downTime); - } + ALOGD_IF(debugInboundEventDetails(), + "notifyKey - id=%" PRIx32 ", eventTime=%" PRId64 + ", deviceId=%d, source=%s, displayId=%" PRId32 + "policyFlags=0x%x, action=%s, flags=0x%x, keyCode=%s, scanCode=0x%x, metaState=0x%x, " + "downTime=%" PRId64, + args->id, args->eventTime, args->deviceId, + inputEventSourceToString(args->source).c_str(), args->displayId, args->policyFlags, + KeyEvent::actionToString(args->action), args->flags, KeyEvent::getLabel(args->keyCode), + args->scanCode, args->metaState, args->downTime); if (!validateKeyEvent(args->action)) { return; } @@ -4141,23 +4163,22 @@ bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args } void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { - ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " + if (debugInboundEventDetails()) { + ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" 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, xCursorPosition=%f, " "yCursorPosition=%f, downTime=%" PRId64, - args->id, args->eventTime, args->deviceId, args->source, args->displayId, - 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); + args->id, args->eventTime, args->deviceId, + inputEventSourceToString(args->source).c_str(), args->displayId, 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, " - "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, " - "orientation=%f", - i, args->pointerProperties[i].id, args->pointerProperties[i].toolType, + ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, " + "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f", + i, args->pointerProperties[i].id, + motionToolTypeToString(args->pointerProperties[i].toolType), args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), @@ -4169,9 +4190,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } } - LOG_ALWAYS_FATAL_IF(!validateMotionEvent(args->action, args->actionButton, args->pointerCount, - args->pointerProperties), - "Invalid event: %s", args->dump().c_str()); + + if (!validateMotionEvent(args->action, args->actionButton, args->pointerCount, + args->pointerProperties)) { + LOG(ERROR) << "Invalid event: " << args->dump(); + return; + } uint32_t policyFlags = args->policyFlags; policyFlags |= POLICY_FLAG_TRUSTED; @@ -4252,7 +4276,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { } void InputDispatcher::notifySensor(const NotifySensorArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " " sensorType=%s", args->id, args->eventTime, args->deviceId, args->source, @@ -4280,7 +4304,7 @@ void InputDispatcher::notifySensor(const NotifySensorArgs* args) { } void InputDispatcher::notifyVibratorState(const NotifyVibratorStateArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifyVibratorState - eventTime=%" PRId64 ", device=%d, isOn=%d", args->eventTime, args->deviceId, args->isOn); } @@ -4292,7 +4316,7 @@ bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs } void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifySwitch - eventTime=%" PRId64 ", policyFlags=0x%x, switchValues=0x%08x, " "switchMask=0x%08x", args->eventTime, args->policyFlags, args->switchValues, args->switchMask); @@ -4304,7 +4328,7 @@ void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args->eventTime, args->deviceId); } @@ -4324,7 +4348,7 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { } void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args->eventTime, args->request.enable ? "true" : "false"); } @@ -4347,7 +4371,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev InputEventInjectionSync syncMode, std::chrono::milliseconds timeout, uint32_t policyFlags) { - if (DEBUG_INBOUND_EVENT_DETAILS) { + if (debugInboundEventDetails()) { ALOGD("injectInputEvent - eventType=%d, targetUid=%s, syncMode=%d, timeout=%lld, " "policyFlags=0x%08x", event->getType(), targetUid ? std::to_string(*targetUid).c_str() : "none", syncMode, @@ -4750,6 +4774,12 @@ sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId return getWindowHandleLocked(focusedToken, displayId); } +ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const { + auto displayInfoIt = mDisplayInfos.find(displayId); + return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform + : kIdentityTransform; +} + bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& window, const MotionEntry& motionEntry) const { const WindowInfo& info = *window->getInfo(); @@ -4787,7 +4817,7 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w 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); + ALOGD("Stack of obscuring windows during untrusted touch (%.1f, %.1f):", x, y); for (const auto& log : occlusionInfo.debugInfo) { ALOGD("%s", log.c_str()); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index b94858b6d0..2246d47e48 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -239,11 +239,11 @@ private: std::shared_ptr<EventEntry> mNextUnblockedEvent GUARDED_BY(mLock); std::pair<sp<android::gui::WindowInfoHandle>, std::vector<InputTarget>> - findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, bool isStylus = false, + findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus = 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); + int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock); sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const REQUIRES(mLock); @@ -374,6 +374,7 @@ private: int32_t displayId) const REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getWindowHandleLocked( const sp<IBinder>& windowHandleToken) const REQUIRES(mLock); + ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock); // Same function as above, but faster. Since displayId is provided, this avoids the need // to loop through all displays. diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index ad5a7fde07..94f38131b6 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -28,10 +28,6 @@ InputState::InputState(const IdGenerator& idGenerator) : mIdGenerator(idGenerato InputState::~InputState() {} -bool InputState::isNeutral() const { - return mKeyMementos.empty() && mMotionMementos.empty(); -} - bool InputState::isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const { for (const MotionMemento& memento : mMotionMementos) { if (memento.deviceId == deviceId && memento.source == source && @@ -251,10 +247,19 @@ void InputState::addMotionMemento(const MotionEntry& entry, int32_t flags, bool } void InputState::MotionMemento::setPointers(const MotionEntry& entry) { - pointerCount = entry.pointerCount; + pointerCount = 0; for (uint32_t i = 0; i < entry.pointerCount; i++) { - pointerProperties[i].copyFrom(entry.pointerProperties[i]); - pointerCoords[i].copyFrom(entry.pointerCoords[i]); + if (MotionEvent::getActionMasked(entry.action) == AMOTION_EVENT_ACTION_POINTER_UP) { + // In POINTER_UP events, the pointer is leaving. Since the action is not stored, + // this departing pointer should not be recorded. + const uint8_t actionIndex = MotionEvent::getActionIndex(entry.action); + if (i == actionIndex) { + continue; + } + } + pointerProperties[pointerCount].copyFrom(entry.pointerProperties[i]); + pointerCoords[pointerCount].copyFrom(entry.pointerCoords[i]); + pointerCount++; } } diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h index 42d8cc6af3..d788e47429 100644 --- a/services/inputflinger/dispatcher/InputState.h +++ b/services/inputflinger/dispatcher/InputState.h @@ -34,9 +34,6 @@ public: explicit InputState(const IdGenerator& idGenerator); ~InputState(); - // Returns true if there is no state to be canceled. - bool isNeutral() const; - // Returns true if the specified source is known to have received a hover enter // motion event. bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 425847183e..9c443f14cf 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -35,6 +35,7 @@ void TouchState::removeTouchedPointer(int32_t pointerId) { for (TouchedWindow& touchedWindow : windows) { touchedWindow.removeTouchingPointer(pointerId); } + clearWindowsWithoutPointers(); } void TouchState::removeTouchedPointerFromWindow( @@ -42,6 +43,7 @@ void TouchState::removeTouchedPointerFromWindow( for (TouchedWindow& touchedWindow : windows) { if (touchedWindow.windowHandle == windowHandle) { touchedWindow.removeTouchingPointer(pointerId); + clearWindowsWithoutPointers(); return; } } @@ -51,6 +53,7 @@ void TouchState::clearHoveringPointers() { for (TouchedWindow& touchedWindow : windows) { touchedWindow.clearHoveringPointers(); } + clearWindowsWithoutPointers(); } void TouchState::clearWindowsWithoutPointers() { @@ -135,7 +138,7 @@ void TouchState::cancelPointersForWindowsExcept(std::bitset<MAX_POINTER_ID + 1> w.pointerIds &= ~pointerIds; } }); - std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); }); + clearWindowsWithoutPointers(); } /** @@ -164,7 +167,7 @@ void TouchState::cancelPointersForNonPilferingWindows() { w.pilferedPointerIds ^ allPilferedPointerIds; w.pointerIds &= ~pilferedByOtherWindows; }); - std::erase_if(windows, [](const TouchedWindow& w) { return w.pointerIds.none(); }); + clearWindowsWithoutPointers(); } sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const { @@ -216,6 +219,11 @@ bool TouchState::isDown() const { [](const TouchedWindow& window) { return window.pointerIds.any(); }); } +bool TouchState::hasHoveringPointers() const { + return std::any_of(windows.begin(), windows.end(), + [](const TouchedWindow& window) { return window.hasHoveringPointers(); }); +} + std::set<sp<WindowInfoHandle>> TouchState::getWindowsWithHoveringPointer(int32_t hoveringDeviceId, int32_t pointerId) const { std::set<sp<WindowInfoHandle>> out; @@ -231,9 +239,7 @@ void TouchState::removeHoveringPointer(int32_t hoveringDeviceId, int32_t hoverin for (TouchedWindow& window : windows) { window.removeHoveringPointer(hoveringDeviceId, hoveringPointerId); } - std::erase_if(windows, [](const TouchedWindow& w) { - return w.pointerIds.none() && !w.hasHoveringPointers(); - }); + clearWindowsWithoutPointers(); } std::string TouchState::dump() const { diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 6e965d8c96..a20080f534 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -71,6 +71,7 @@ struct TouchState { const sp<android::gui::WindowInfoHandle>& windowHandle) const; // Whether any of the windows are currently being touched bool isDown() const; + bool hasHoveringPointers() const; std::set<sp<android::gui::WindowInfoHandle>> getWindowsWithHoveringPointer( int32_t deviceId, int32_t pointerId) const; diff --git a/services/inputflinger/docs/input_coordinates.md b/services/inputflinger/docs/input_coordinates.md new file mode 100644 index 0000000000..7795710936 --- /dev/null +++ b/services/inputflinger/docs/input_coordinates.md @@ -0,0 +1,113 @@ +# Input Coordinate Processing in InputFlinger + +This document aims to illustrate why we need to take care when converting +between the discrete and continuous coordinate spaces, especially when +performing rotations. + +The Linux evdev protocol works over **discrete integral** values. The same is +true for displays, which output discrete pixels. WindowManager also tracks +window bounds in pixels in the rotated logical display. + +However, our `MotionEvent` APIs +report **floating point** axis values in a **continuous space**. This disparity +is important to note when working in InputFlinger, which has to make sure the +discrete raw coordinates are converted to the continuous space correctly in all +scenarios. + +## Disparity between continuous and discrete coordinates during rotation + +Let's consider an example of device that has a 3 x 4 screen. + +### Natural orientation: No rotation + +If the user interacts with the highlighted pixel, the touchscreen would report +the discreet coordinates (0, 2). + +``` + ┌─────┬─────┬─────┐ + │ 0,0 │ 1,0 │ 2,0 │ + ├─────┼─────┼─────┤ + │ 0,1 │ 1,1 │ 2,1 │ + ├─────┼─────┼─────┤ + │█0,2█│ 1,2 │ 2,2 │ + ├─────┼─────┼─────┤ + │ 0,3 │ 1,3 │ 2,3 │ + └─────┴─────┴─────┘ +``` + +When converted to the continuous space, the point (0, 2) corresponds to the +location shown below. + +``` + 0 1 2 3 + 0 ┌─────┬─────┬─────┐ + │ │ │ │ + 1 ├─────┼─────┼─────┤ + │ │ │ │ + 2 █─────┼─────┼─────┤ + │ │ │ │ + 3 ├─────┼─────┼─────┤ + │ │ │ │ + 4 └─────┴─────┴─────┘ +``` + +### Rotated orientation: 90-degree counter-clockwise rotation + +When the device is rotated and the same place on the touchscreen is touched, the +input device will still report the same coordinates of (0, 2). + +In the rotated display, that now corresponds to the pixel (2, 2). + +``` + ┌─────┬─────┬─────┬─────┐ + │ 0,0 │ 1,0 │ 2,0 │ 3,0 │ + ├─────┼─────┼─────┼─────┤ + │ 0,1 │ 1,1 │ 2,1 │ 3,1 │ + ├─────┼─────┼─────┼─────┤ + │ 0,2 │ 1,2 │█2,2█│ 3,2 │ + └─────┴─────┴─────┴─────┘ +``` + +*It is important to note that rotating the device 90 degrees is NOT equivalent +to rotating the continuous coordinate space by 90 degrees.* + +The point (2, 2) now corresponds to a different location in the continuous space +than before, even though the user was interacting at the same place on the +touchscreen. + +``` + 0 1 2 3 4 + 0 ┌─────┬─────┬─────┬─────┐ + │ │ │ │ │ + 1 ├─────┼─────┼─────┼─────┤ + │ │ │ │ │ + 2 ├─────┼─────█─────┼─────┤ + │ │ │ │ │ + 3 └─────┴─────┴─────┴─────┘ +``` + +If we were to simply (incorrectly) rotate the continuous space from before by +90 degrees, the touched point would correspond to the location (2, 3), shown +below. This new point is outside the bounds of the display, since it does not +correspond to any pixel at that location. + +It should be impossible for a touchscreen to generate points outside the bounds +of the display, because we assume that the area of the touchscreen maps directly +to the area of the display. Therefore, that point is an invalid coordinate that +cannot be generated by an input device. + +``` + 0 1 2 3 4 + 0 ┌─────┬─────┬─────┬─────┐ + │ │ │ │ ╏ + 1 ├─────┼─────┼─────┼─────┤ + │ │ │ │ ╏ + 2 ├─────┼─────┼─────┼─────┤ + │ │ │ │ ╏ + 3 └-----┴-----█-----┴-----┘ +``` + +The same logic applies to windows as well. When performing hit tests to +determine if a point in the continuous space falls inside a window's bounds, +hit test must be performed in the correct orientation, since points on the right +and bottom edges of the window do not fall within the window bounds. diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index d29692cf05..132c3a1004 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -39,6 +39,7 @@ filegroup { "EventHub.cpp", "InputDevice.cpp", "InputReader.cpp", + "Macros.cpp", "TouchVideoDevice.cpp", "controller/PeripheralController.cpp", "mapper/CursorInputMapper.cpp", diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index d3d8021645..3d3a8ea42d 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -882,14 +882,13 @@ int32_t EventHub::getDeviceControllerNumber(int32_t deviceId) const { return device != nullptr ? device->controllerNumber : 0; } -void EventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { +std::optional<PropertyMap> EventHub::getConfiguration(int32_t deviceId) const { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->configuration) { - *outConfiguration = *device->configuration; - } else { - outConfiguration->clear(); + if (device == nullptr || device->configuration == nullptr) { + return {}; } + return *device->configuration; } status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index c598c0a700..0d2030e0c4 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -277,12 +277,18 @@ std::list<NotifyArgs> InputDevice::configure(nsecs_t when, const InputReaderConf mHasMic = mClasses.test(InputDeviceClass::MIC); if (!isIgnored()) { - if (!changes) { // first time only + // Full configuration should happen the first time configure is called + // and when the device type is changed. Changing a device type can + // affect various other parameters so should result in a + // reconfiguration. + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) { mConfiguration.clear(); for_each_subdevice([this](InputDeviceContext& context) { - PropertyMap configuration; - context.getConfiguration(&configuration); - mConfiguration.addAll(&configuration); + std::optional<PropertyMap> configuration = + getEventHub()->getConfiguration(context.getEventHubId()); + if (configuration) { + mConfiguration.addAll(&(*configuration)); + } }); mAssociatedDeviceType = @@ -412,22 +418,21 @@ std::list<NotifyArgs> InputDevice::process(const RawEvent* rawEvents, size_t cou // 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, - rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, - rawEvent->when); + if (debugRawEvents()) { + const auto [type, code, value] = + InputEventLookup::getLinuxEvdevLabel(rawEvent->type, rawEvent->code, + rawEvent->value); + ALOGD("Input event: eventHubDevice=%d type=%s code=%s value=%s when=%" PRId64, + rawEvent->deviceId, type.c_str(), code.c_str(), value.c_str(), rawEvent->when); } if (mDropUntilNextSync) { if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { mDropUntilNextSync = false; - if (DEBUG_RAW_EVENTS) { - ALOGD("Recovered from input event buffer overrun."); - } + ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun."); } else { - if (DEBUG_RAW_EVENTS) { - ALOGD("Dropped input event while waiting for next input sync."); - } + ALOGD_IF(debugRawEvents(), + "Dropped input event while waiting for next input sync."); } } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { ALOGI("Detected input event buffer overrun for device %s.", getName().c_str()); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 57f679c022..9080cc1d26 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -146,7 +146,7 @@ void InputReader::loopOnce() { if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); if (now >= mNextTimeout) { - if (DEBUG_RAW_EVENTS) { + if (debugRawEvents()) { ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); } mNextTimeout = LLONG_MAX; @@ -199,7 +199,7 @@ std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents } batchSize += 1; } - if (DEBUG_RAW_EVENTS) { + if (debugRawEvents()) { ALOGD("BatchSize: %zu Count: %zu", batchSize, count); } out += processEventsForDeviceLocked(deviceId, rawEvent, batchSize); diff --git a/services/inputflinger/reader/Macros.cpp b/services/inputflinger/reader/Macros.cpp new file mode 100644 index 0000000000..8841d0f14a --- /dev/null +++ b/services/inputflinger/reader/Macros.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <android-base/properties.h> + +namespace { + +const bool IS_DEBUGGABLE_BUILD = +#if defined(__ANDROID__) + android::base::GetBoolProperty("ro.debuggable", false); +#else + true; +#endif + +} // namespace + +namespace android { + +bool debugRawEvents() { + if (!IS_DEBUGGABLE_BUILD) { + static const bool DEBUG_RAW_EVENTS = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO); + return DEBUG_RAW_EVENTS; + } + return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO); +} + +} // namespace android diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h index d2a7ced864..2bce215b3c 100644 --- a/services/inputflinger/reader/Macros.h +++ b/services/inputflinger/reader/Macros.h @@ -25,12 +25,14 @@ #include <unordered_map> 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) + * Enable this via "adb shell setprop log.tag.InputReaderRawEvents DEBUG". + * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately + * on debuggable builds (e.g. userdebug). */ -const bool DEBUG_RAW_EVENTS = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "RawEvents", ANDROID_LOG_INFO); +bool debugRawEvents(); /** * Log debug messages about virtual key processing. @@ -52,6 +54,7 @@ const bool DEBUG_POINTERS = */ 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) @@ -79,6 +82,7 @@ const bool DEBUG_STYLUS_FUSION = */ const bool DEBUG_LIGHT_DETAILS = __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "LightDetails", ANDROID_LOG_INFO); + } // namespace android #define INDENT " " diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 86acadb428..0b15efed12 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -263,7 +263,13 @@ public: virtual int32_t getDeviceControllerNumber(int32_t deviceId) const = 0; - virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; + /** + * Get the PropertyMap for the provided EventHub device, if available. + * This acquires the device lock, so a copy is returned rather than the raw pointer + * to the device's PropertyMap. A std::nullopt may be returned if the device could + * not be found, or if it doesn't have any configuration. + */ + virtual std::optional<PropertyMap> getConfiguration(int32_t deviceId) const = 0; virtual status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const = 0; @@ -464,7 +470,7 @@ public: int32_t getDeviceControllerNumber(int32_t deviceId) const override final; - void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override final; + std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override final; status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const override final; diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 7867029ab7..4ae06fe77b 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -269,9 +269,6 @@ public: inline int32_t getDeviceControllerNumber() const { return mEventHub->getDeviceControllerNumber(mId); } - inline void getConfiguration(PropertyMap* outConfiguration) const { - return mEventHub->getConfiguration(mId, outConfiguration); - } inline status_t getAbsoluteAxisInfo(int32_t code, RawAbsoluteAxisInfo* axisInfo) const { return mEventHub->getAbsoluteAxisInfo(mId, code, axisInfo); } diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 0361bc670a..dc0454dc04 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -61,36 +61,6 @@ static bool isSupportedScanCode(int32_t scanCode) { scanCode >= BTN_WHEEL; } -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 --- KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext, uint32_t source, @@ -295,14 +265,12 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read keyMetaState = mMetaState; } - // Key down on external an keyboard should wake the device. + // Any key down on an external 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 // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each // wake key individually. - // TODO: Use the input device configuration to control this behavior more finely. - if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault && - !isMediaKey(keyCode)) { + if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault) { policyFlags |= POLICY_FLAG_WAKE; } diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index f797bc9c0b..3d60bfdfb8 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -218,7 +218,7 @@ SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); int32_t reportingMode = 0; - if (!tryGetProperty(prefix + ".reportingMode", reportingMode)) { + if (tryGetProperty(prefix + ".reportingMode", reportingMode)) { sensorInfo.flags |= (reportingMode & REPORTING_MODE_MASK) << REPORTING_MODE_SHIFT; } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 31fdac9ffc..96c163dc3c 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -293,7 +293,10 @@ std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when, mConfig = *config; - if (!changes) { // first time only + // Full configuration should happen the first time configure is called and + // when the device type is changed. Changing a device type can affect + // various other parameters so should result in a reconfiguration. + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_TYPE)) { // Configure basic parameters. configureParameters(); @@ -328,7 +331,8 @@ std::list<NotifyArgs> TouchInputMapper::configure(nsecs_t when, InputReaderConfiguration::CHANGE_POINTER_CAPTURE | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT | InputReaderConfiguration::CHANGE_SHOW_TOUCHES | - InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) { + InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE | + InputReaderConfiguration::CHANGE_DEVICE_TYPE))) { // Configure device sources, display dimensions, orientation and // scaling factors. configureInputDevice(when, &resetNeeded); @@ -801,75 +805,96 @@ void TouchInputMapper::initializeOrientedRanges() { }; } - // 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. - // TODO(b/20508709): Calculate the oriented ranges using the input device's raw frame. - switch (mInputDeviceOrientation) { - case ui::ROTATION_90: - case ui::ROTATION_270: - mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayBounds.height - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY(); - - mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayBounds.width - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX(); - break; - - default: - mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = mDisplayBounds.width - 1; - mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = 0; - mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mRawToDisplay.getScaleX(); - - mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = mDisplayBounds.height - 1; - mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = 0; - mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mRawToDisplay.getScaleY(); - break; - } + // Oriented X/Y range (in the rotated display's orientation) + const FloatRect rawFrame = Rect{mRawPointerAxes.x.minValue, mRawPointerAxes.y.minValue, + mRawPointerAxes.x.maxValue, mRawPointerAxes.y.maxValue} + .toFloatRect(); + const auto orientedRangeRect = mRawToRotatedDisplay.transform(rawFrame); + mOrientedRanges.x.min = orientedRangeRect.left; + mOrientedRanges.y.min = orientedRangeRect.top; + mOrientedRanges.x.max = orientedRangeRect.right; + mOrientedRanges.y.max = orientedRangeRect.bottom; + + // Oriented flat (in the rotated display's orientation) + const auto orientedFlat = + transformWithoutTranslation(mRawToRotatedDisplay, + {static_cast<float>(mRawPointerAxes.x.flat), + static_cast<float>(mRawPointerAxes.y.flat)}); + mOrientedRanges.x.flat = std::abs(orientedFlat.x); + mOrientedRanges.y.flat = std::abs(orientedFlat.y); + + // Oriented fuzz (in the rotated display's orientation) + const auto orientedFuzz = + transformWithoutTranslation(mRawToRotatedDisplay, + {static_cast<float>(mRawPointerAxes.x.fuzz), + static_cast<float>(mRawPointerAxes.y.fuzz)}); + mOrientedRanges.x.fuzz = std::abs(orientedFuzz.x); + mOrientedRanges.y.fuzz = std::abs(orientedFuzz.y); + + // Oriented resolution (in the rotated display's orientation) + const auto orientedRes = + transformWithoutTranslation(mRawToRotatedDisplay, + {static_cast<float>(mRawPointerAxes.x.resolution), + static_cast<float>(mRawPointerAxes.y.resolution)}); + mOrientedRanges.x.resolution = std::abs(orientedRes.x); + mOrientedRanges.y.resolution = std::abs(orientedRes.y); } void TouchInputMapper::computeInputTransforms() { - const ui::Size rawSize{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()}; + constexpr auto isRotated = [](const ui::Transform::RotationFlags& rotation) { + return rotation == ui::Transform::ROT_90 || rotation == ui::Transform::ROT_270; + }; - ui::Size rotatedRawSize = rawSize; - if (mInputDeviceOrientation == ui::ROTATION_270 || mInputDeviceOrientation == ui::ROTATION_90) { - std::swap(rotatedRawSize.width, rotatedRawSize.height); - } - const auto rotationFlags = ui::Transform::toRotationFlags(-mInputDeviceOrientation); - mRawRotation = ui::Transform{rotationFlags}; + // See notes about input coordinates in the inputflinger docs: + // //frameworks/native/services/inputflinger/docs/input_coordinates.md // Step 1: Undo the raw offset so that the raw coordinate space now starts at (0, 0). - ui::Transform undoRawOffset; - undoRawOffset.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue); - - // Step 2: Rotate the raw coordinates to the expected orientation. - ui::Transform rotate; - // When rotating raw coordinates, the raw size will be used as an offset. - // Account for the extra unit added to the raw range when the raw size was calculated. - rotate.set(rotationFlags, rotatedRawSize.width - 1, rotatedRawSize.height - 1); - - // Step 3: Scale the raw coordinates to the display space. - ui::Transform scaleToDisplay; - const float xScale = static_cast<float>(mDisplayBounds.width) / rotatedRawSize.width; - const float yScale = static_cast<float>(mDisplayBounds.height) / rotatedRawSize.height; - scaleToDisplay.set(xScale, 0, 0, yScale); - - mRawToDisplay = (scaleToDisplay * (rotate * undoRawOffset)); - - // Calculate the transform that takes raw coordinates to the rotated display space. - ui::Transform displayToRotatedDisplay; - displayToRotatedDisplay.set(ui::Transform::toRotationFlags(-mViewport.orientation), - mViewport.deviceWidth, mViewport.deviceHeight); - mRawToRotatedDisplay = displayToRotatedDisplay * mRawToDisplay; + ui::Transform undoOffsetInRaw; + undoOffsetInRaw.set(-mRawPointerAxes.x.minValue, -mRawPointerAxes.y.minValue); + + // Step 2: Rotate the raw coordinates to account for input device orientation. The coordinates + // will now be in the same orientation as the display in ROTATION_0. + // Note: Negating an ui::Rotation value will give its inverse rotation. + const auto inputDeviceOrientation = ui::Transform::toRotationFlags(-mParameters.orientation); + const ui::Size orientedRawSize = isRotated(inputDeviceOrientation) + ? ui::Size{mRawPointerAxes.getRawHeight(), mRawPointerAxes.getRawWidth()} + : ui::Size{mRawPointerAxes.getRawWidth(), mRawPointerAxes.getRawHeight()}; + // When rotating raw values, account for the extra unit added when calculating the raw range. + const auto orientInRaw = ui::Transform(inputDeviceOrientation, orientedRawSize.width - 1, + orientedRawSize.height - 1); + + // Step 3: Rotate the raw coordinates to account for the display rotation. The coordinates will + // now be in the same orientation as the rotated display. There is no need to rotate the + // coordinates to the display rotation if the device is not orientation-aware. + const auto viewportRotation = ui::Transform::toRotationFlags(-mViewport.orientation); + const auto rotatedRawSize = mParameters.orientationAware && isRotated(viewportRotation) + ? ui::Size{orientedRawSize.height, orientedRawSize.width} + : orientedRawSize; + // When rotating raw values, account for the extra unit added when calculating the raw range. + const auto rotateInRaw = mParameters.orientationAware + ? ui::Transform(viewportRotation, rotatedRawSize.width - 1, rotatedRawSize.height - 1) + : ui::Transform(); + + // Step 4: Scale the raw coordinates to the display space. + // - Here, we assume that the raw surface of the touch device maps perfectly to the surface + // of the display panel. This is usually true for touchscreens. + // - From this point onward, we are no longer in the discrete space of the raw coordinates but + // are in the continuous space of the logical display. + ui::Transform scaleRawToDisplay; + const float xScale = static_cast<float>(mViewport.deviceWidth) / rotatedRawSize.width; + const float yScale = static_cast<float>(mViewport.deviceHeight) / rotatedRawSize.height; + scaleRawToDisplay.set(xScale, 0, 0, yScale); + + // Step 5: Undo the display rotation to bring us back to the un-rotated display coordinate space + // that InputReader uses. + const auto undoRotateInDisplay = + ui::Transform(viewportRotation, mViewport.deviceWidth, mViewport.deviceHeight) + .inverse(); + + // Now put it all together! + mRawToRotatedDisplay = (scaleRawToDisplay * (rotateInRaw * (orientInRaw * undoOffsetInRaw))); + mRawToDisplay = (undoRotateInDisplay * mRawToRotatedDisplay); + mRawRotation = ui::Transform{mRawToDisplay.getOrientation()}; } void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) { @@ -945,6 +970,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mPhysicalFrameInRotatedDisplay = {mViewport.physicalLeft, mViewport.physicalTop, mViewport.physicalRight, mViewport.physicalBottom}; + // TODO(b/257118693): Remove the dependence on the old orientation/rotation logic that + // uses mInputDeviceOrientation. The new logic uses the transforms calculated in + // computeInputTransforms(). // 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 // orientation-aware, then we need to apply the inverse rotation of the display so that @@ -1446,7 +1474,7 @@ std::list<NotifyArgs> TouchInputMapper::sync(nsecs_t when, nsecs_t readTime) { assignPointerIds(last, next); } - ALOGD_IF(DEBUG_RAW_EVENTS, + ALOGD_IF(debugRawEvents(), "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, @@ -1650,7 +1678,8 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->setButtonState(mCurrentRawState.buttonState); mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(), mCurrentCookedState.cookedPointerData.idToIndex.cbegin(), - mCurrentCookedState.cookedPointerData.touchingIdBits, + mCurrentCookedState.cookedPointerData.touchingIdBits | + mCurrentCookedState.cookedPointerData.hoveringIdBits, mViewport.displayId); } diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 9f32311e14..d3af402153 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -16,6 +16,7 @@ #include "../Macros.h" +#include <limits> #include <optional> #include <android/input.h> @@ -30,6 +31,85 @@ namespace android { namespace { +/** + * Log details of each gesture output by the gestures library. + * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires + * restarting the shell) + */ +const bool DEBUG_TOUCHPAD_GESTURES = + __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures", + ANDROID_LOG_INFO); + +// Describes a segment of the acceleration curve. +struct CurveSegment { + // The maximum pointer speed which this segment should apply. The last segment in a curve should + // always set this to infinity. + double maxPointerSpeedMmPerS; + double slope; + double intercept; +}; + +const std::vector<CurveSegment> segments = { + {10.922, 3.19, 0}, + {31.750, 4.79, -17.526}, + {98.044, 7.28, -96.52}, + {std::numeric_limits<double>::infinity(), 15.04, -857.758}, +}; + +const std::vector<double> sensitivityFactors = {1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 16, 18, 20}; + +std::vector<double> createAccelerationCurveForSensitivity(int32_t sensitivity, + size_t propertySize) { + LOG_ALWAYS_FATAL_IF(propertySize < 4 * segments.size()); + std::vector<double> output(propertySize, 0); + + // The Gestures library uses functions of the following form to define curve segments, where a, + // b, and c can be specified by us: + // output_speed(input_speed_mm) = a * input_speed_mm ^ 2 + b * input_speed_mm + c + // + // (a, b, and c are also called sqr_, mul_, and int_ in the Gestures library code.) + // + // We are trying to implement the following function, where slope and intercept are the + // parameters specified in the `segments` array above: + // gain(input_speed_mm) = + // 0.64 * (sensitivityFactor / 10) * (slope + intercept / input_speed_mm) + // Where "gain" is a multiplier applied to the input speed to produce the output speed: + // output_speed(input_speed_mm) = input_speed_mm * gain(input_speed_mm) + // + // To put our function in the library's form, we substitute it into the function above: + // output_speed(input_speed_mm) = + // input_speed_mm * (0.64 * (sensitivityFactor / 10) * + // (slope + 25.4 * intercept / input_speed_mm)) + // then expand the brackets so that input_speed_mm cancels out for the intercept term: + // gain(input_speed_mm) = + // 0.64 * (sensitivityFactor / 10) * slope * input_speed_mm + + // 0.64 * (sensitivityFactor / 10) * intercept + // + // This gives us the following parameters for the Gestures library function form: + // a = 0 + // b = 0.64 * (sensitivityFactor / 10) * slope + // c = 0.64 * (sensitivityFactor / 10) * intercept + + double commonFactor = 0.64 * sensitivityFactors[sensitivity + 7] / 10; + + size_t i = 0; + for (CurveSegment seg : segments) { + // The library's curve format consists of four doubles per segment: + // * maximum pointer speed for the segment (mm/s) + // * multiplier for the x² term (a.k.a. "a" or "sqr") + // * multiplier for the x term (a.k.a. "b" or "mul") + // * the intercept (a.k.a. "c" or "int") + // (see struct CurveSegment in the library's AccelFilterInterpreter) + output[i + 0] = seg.maxPointerSpeedMmPerS; + output[i + 1] = 0; + output[i + 2] = commonFactor * seg.slope; + output[i + 3] = commonFactor * seg.intercept; + i += 4; + } + + return output; +} + short getMaxTouchCount(const InputDeviceContext& context) { if (context.hasScanCode(BTN_TOOL_QUINTTAP)) return 5; if (context.hasScanCode(BTN_TOOL_QUADTAP)) return 4; @@ -147,10 +227,12 @@ std::list<NotifyArgs> TouchpadInputMapper::configure(nsecs_t when, mGestureConverter.setOrientation(orientation); } if (!changes || (changes & InputReaderConfiguration::CHANGE_TOUCHPAD_SETTINGS)) { - // TODO(b/265798483): load an Android-specific acceleration curve instead of mapping to one - // of five ChromeOS curves. - const int pointerSensitivity = (config->touchpadPointerSpeed + 7) / 3 + 1; - mPropertyProvider.getProperty("Pointer Sensitivity").setIntValues({pointerSensitivity}); + mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve") + .setBoolValues({true}); + GesturesProp accelCurveProp = mPropertyProvider.getProperty("Pointer Accel Curve"); + accelCurveProp.setRealValues( + createAccelerationCurveForSensitivity(config->touchpadPointerSpeed, + accelCurveProp.getCount())); mPropertyProvider.getProperty("Invert Scrolling") .setBoolValues({config->touchpadNaturalScrollingEnabled}); mPropertyProvider.getProperty("Tap Enable") @@ -178,6 +260,7 @@ std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) { std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs_t readTime, SelfContainedHardwareState schs) { + ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "New hardware state: %s", schs.state.String().c_str()); mProcessing = true; mGestureInterpreter->PushHardwareState(&schs.state); mProcessing = false; @@ -186,7 +269,7 @@ std::list<NotifyArgs> TouchpadInputMapper::sendHardwareState(nsecs_t when, nsecs } void TouchpadInputMapper::consumeGesture(const Gesture* gesture) { - ALOGD("Gesture ready: %s", gesture->String().c_str()); + ALOGD_IF(DEBUG_TOUCHPAD_GESTURES, "Gesture ready: %s", gesture->String().c_str()); if (!mProcessing) { ALOGE("Received gesture outside of the normal processing flow; ignoring it."); return; diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp index 2e175b8fae..c091a51386 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp @@ -80,18 +80,32 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t schs.fingers.clear(); for (size_t i = 0; i < mMotionAccumulator.getSlotCount(); i++) { MultiTouchMotionAccumulator::Slot slot = mMotionAccumulator.getSlot(i); - if (slot.isInUse()) { - FingerState& fingerState = schs.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(); + if (!slot.isInUse()) { + continue; + } + // Some touchpads continue to report contacts even after they've identified them as palms. + // We want to exclude these contacts from the HardwareStates, but still need to report a + // tracking ID of -1 if a finger turns into a palm. + const bool isPalm = slot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM; + if (isPalm && mFingerSlots.find(i) == mFingerSlots.end()) { + continue; + } + + FingerState& fingerState = schs.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 = isPalm ? -1 : slot.getTrackingId(); + if (fingerState.tracking_id == -1) { + mFingerSlots.erase(i); + } else { + mFingerSlots.insert(i); } } schs.state.fingers = schs.fingers.data(); @@ -103,6 +117,7 @@ SelfContainedHardwareState HardwareStateConverter::produceHardwareState(nsecs_t void HardwareStateConverter::reset() { mCursorButtonAccumulator.reset(mDeviceContext); mTouchButtonAccumulator.reset(); + mFingerSlots.clear(); mMscTimestamp = 0; } diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h index 88312993a0..d6787b7e3f 100644 --- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h +++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h @@ -17,6 +17,7 @@ #pragma once #include <optional> +#include <set> #include <utils/Timers.h> @@ -53,6 +54,7 @@ private: MultiTouchMotionAccumulator mMotionAccumulator; TouchButtonAccumulator mTouchButtonAccumulator; int32_t mMscTimestamp = 0; + std::set<size_t> mFingerSlots; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp index cd18cd3e35..089f45a4e6 100644 --- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp +++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp @@ -68,11 +68,11 @@ const GesturesPropProvider gesturePropProvider = { .free_fn = freeProperty, }; -bool PropertyProvider::hasProperty(const std::string name) const { +bool PropertyProvider::hasProperty(const std::string& name) const { return mProperties.find(name) != mProperties.end(); } -GesturesProp& PropertyProvider::getProperty(const std::string name) { +GesturesProp& PropertyProvider::getProperty(const std::string& name) { return mProperties.at(name); } @@ -84,7 +84,7 @@ std::string PropertyProvider::dump() const { return dump; } -GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, int* loc, +GesturesProp* PropertyProvider::createIntArrayProperty(const std::string& name, int* loc, size_t count, const int* init) { const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)}); @@ -92,7 +92,7 @@ GesturesProp* PropertyProvider::createIntArrayProperty(const std::string name, i return &it->second; } -GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name, +GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string& name, GesturesPropBool* loc, size_t count, const GesturesPropBool* init) { const auto [it, inserted] = @@ -101,7 +101,7 @@ GesturesProp* PropertyProvider::createBoolArrayProperty(const std::string name, return &it->second; } -GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, double* loc, +GesturesProp* PropertyProvider::createRealArrayProperty(const std::string& name, double* loc, size_t count, const double* init) { const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, count, init)}); @@ -109,7 +109,7 @@ GesturesProp* PropertyProvider::createRealArrayProperty(const std::string name, return &it->second; } -GesturesProp* PropertyProvider::createStringProperty(const std::string name, const char** loc, +GesturesProp* PropertyProvider::createStringProperty(const std::string& name, const char** loc, const char* const init) { const auto [it, inserted] = mProperties.insert(std::pair{name, GesturesProp(name, loc, init)}); LOG_ALWAYS_FATAL_IF(!inserted, "Gesture property \"%s\" already exists.", name.c_str()); diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h index c21260f6c2..50451a3929 100644 --- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.h +++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.h @@ -31,18 +31,18 @@ extern const GesturesPropProvider gesturePropProvider; // Implementation of a gestures library property provider, which provides configuration parameters. class PropertyProvider { public: - bool hasProperty(const std::string name) const; - GesturesProp& getProperty(const std::string name); + bool hasProperty(const std::string& name) const; + GesturesProp& getProperty(const std::string& name); std::string dump() const; // Methods to be called by the gestures library: - GesturesProp* createIntArrayProperty(const std::string name, int* loc, size_t count, + GesturesProp* createIntArrayProperty(const std::string& name, int* loc, size_t count, const int* init); - GesturesProp* createBoolArrayProperty(const std::string name, GesturesPropBool* loc, + GesturesProp* createBoolArrayProperty(const std::string& name, GesturesPropBool* loc, size_t count, const GesturesPropBool* init); - GesturesProp* createRealArrayProperty(const std::string name, double* loc, size_t count, + GesturesProp* createRealArrayProperty(const std::string& name, double* loc, size_t count, const double* init); - GesturesProp* createStringProperty(const std::string name, const char** loc, + GesturesProp* createStringProperty(const std::string& name, const char** loc, const char* const init); void freeProperty(GesturesProp* prop); diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index af40fed51d..97138c73c0 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -86,6 +86,13 @@ cc_test { ], }, }, + sanitize: { + undefined: true, + all_undefined: true, + diag: { + undefined: true, + }, + }, static_libs: [ "libc++fs", "libgmock", diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp index 6ac0bfb541..ff6d584007 100644 --- a/services/inputflinger/tests/FakeEventHub.cpp +++ b/services/inputflinger/tests/FakeEventHub.cpp @@ -255,11 +255,12 @@ int32_t FakeEventHub::getDeviceControllerNumber(int32_t) const { return 0; } -void FakeEventHub::getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const { +std::optional<PropertyMap> FakeEventHub::getConfiguration(int32_t deviceId) const { Device* device = getDevice(deviceId); - if (device) { - *outConfiguration = device->configuration; + if (device == nullptr) { + return {}; } + return device->configuration; } status_t FakeEventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h index 72f8ac03a5..e0a3f9eee1 100644 --- a/services/inputflinger/tests/FakeEventHub.h +++ b/services/inputflinger/tests/FakeEventHub.h @@ -159,7 +159,7 @@ private: 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; + std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override; status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const override; bool hasRelativeAxis(int32_t deviceId, int axis) const override; diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp index 79218816bd..36b9bab14e 100644 --- a/services/inputflinger/tests/HardwareStateConverter_test.cpp +++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp @@ -191,6 +191,68 @@ TEST_F(HardwareStateConverterTest, TwoFingers) { EXPECT_EQ(0u, finger2.flags); } +TEST_F(HardwareStateConverterTest, OnePalm) { + const nsecs_t time = ARBITRARY_TIME; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + HardwareStateConverter conv(deviceContext); + + processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0); + processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); + processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100); + + processAxis(conv, time, EV_KEY, BTN_TOUCH, 1); + std::optional<SelfContainedHardwareState> schs = processSync(conv, time); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(0, schs->state.finger_cnt); +} + +TEST_F(HardwareStateConverterTest, OneFingerTurningIntoAPalm) { + const nsecs_t time = ARBITRARY_TIME; + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + HardwareStateConverter conv(deviceContext); + + processAxis(conv, time, EV_ABS, ABS_MT_SLOT, 0); + processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + processAxis(conv, time, EV_ABS, ABS_MT_TRACKING_ID, 123); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 50); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 100); + + processAxis(conv, time, EV_KEY, BTN_TOUCH, 1); + + std::optional<SelfContainedHardwareState> schs = processSync(conv, time); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(1, schs->state.finger_cnt); + + processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_PALM); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 51); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 99); + + schs = processSync(conv, time); + ASSERT_TRUE(schs.has_value()); + ASSERT_EQ(1, schs->state.finger_cnt); + EXPECT_EQ(-1, schs->state.fingers[0].tracking_id); + + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 53); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 97); + + schs = processSync(conv, time); + ASSERT_TRUE(schs.has_value()); + EXPECT_EQ(0, schs->state.finger_cnt); + + processAxis(conv, time, EV_ABS, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_X, 55); + processAxis(conv, time, EV_ABS, ABS_MT_POSITION_Y, 95); + schs = processSync(conv, time); + ASSERT_TRUE(schs.has_value()); + ASSERT_EQ(1, schs->state.finger_cnt); + const FingerState& newFinger = schs->state.fingers[0]; + EXPECT_EQ(123, newFinger.tracking_id); + EXPECT_NEAR(55, newFinger.position_x, EPSILON); + EXPECT_NEAR(95, newFinger.position_y, EPSILON); +} + TEST_F(HardwareStateConverterTest, ButtonPressed) { const nsecs_t time = ARBITRARY_TIME; InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index e71cdce498..e2996437bf 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -58,8 +58,23 @@ static constexpr int32_t SECOND_DEVICE_ID = 2; static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; static constexpr int32_t SECOND_DISPLAY_ID = 1; +static constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN; +static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE; +static constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP; +static constexpr int32_t ACTION_HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER; +static constexpr int32_t ACTION_HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT; static constexpr int32_t ACTION_OUTSIDE = AMOTION_EVENT_ACTION_OUTSIDE; static constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL; +/** + * The POINTER_DOWN(0) is an unusual, but valid, action. It just means that the new pointer in the + * MotionEvent is at the index 0 rather than 1 (or later). That is, the pointer id=0 (which is at + * index 0) is the new pointer going down. The same pointer could have been placed at a different + * index, and the action would become POINTER_1_DOWN, 2, etc..; these would all be valid. In + * general, we try to place pointer id = 0 at the index 0. Of course, this is not possible if + * pointer id=0 leaves but the pointer id=1 remains. + */ +static constexpr int32_t POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); 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 = @@ -90,6 +105,8 @@ static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms; static constexpr int expectedWallpaperFlags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; +using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID; + struct PointF { float x; float y; @@ -145,6 +162,10 @@ MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { return arg.getDisplayId() == displayId; } +MATCHER_P(WithDeviceId, deviceId, "InputEvent with specified deviceId") { + return arg.getDeviceId() == deviceId; +} + MATCHER_P(WithSource, source, "InputEvent with specified source") { *result_listener << "expected source " << inputEventSourceToString(source) << ", but got " << inputEventSourceToString(arg.getSource()); @@ -163,6 +184,10 @@ MATCHER_P2(WithCoords, x, y, "MotionEvent with specified coordinates") { return arg.getX(/*pointerIndex=*/0) == x && arg.getY(/*pointerIndex=*/0) == y; } +MATCHER_P(WithPointerCount, pointerCount, "MotionEvent with specified number of pointers") { + return arg.getPointerCount() == pointerCount; +} + MATCHER_P(WithPointers, pointers, "MotionEvent with specified pointers") { // Build a map for the received pointers, by pointer id std::map<int32_t /*pointerId*/, PointF> actualPointers; @@ -205,10 +230,10 @@ public: const auto& motionEvent = static_cast<const MotionEvent&>(event); EXPECT_EQ(motionEvent.getEventTime(), args.eventTime); EXPECT_EQ(motionEvent.getAction(), args.action); - EXPECT_EQ(motionEvent.getX(0), point.x); - EXPECT_EQ(motionEvent.getY(0), point.y); - EXPECT_EQ(motionEvent.getRawX(0), point.x); - EXPECT_EQ(motionEvent.getRawY(0), point.y); + EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION); + EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION); }); } @@ -1922,6 +1947,48 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance } /** + * Two fingers down on the window, and lift off the first finger. + * Next, cancel the gesture to the window by removing the window. Make sure that the CANCEL event + * contains a single pointer. + */ +TEST_F(InputDispatcherTest, CancelAfterPointer0Up) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + NotifyMotionArgs args; + // First touch pointer down on right window + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .build())); + // Second touch pointer down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100)) + .build())); + // First touch pointer lifts. The second one remains down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(110).y(100)) + .build())); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); + window->consumeMotionEvent(WithMotionAction(POINTER_0_UP)); + + // Remove the window. The gesture should be canceled + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}}); + const std::map<int32_t, PointF> expectedPointers{{1, PointF{110, 100}}}; + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithPointers(expectedPointers))); +} + +/** * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above, * with the following differences: * After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to @@ -2029,8 +2096,17 @@ TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) { wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {100, 100})) + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, + AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 1, + AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .build(), + INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); @@ -2365,6 +2441,205 @@ TEST_F(InputDispatcherTest, HoverFromLeftToRightAndTap) { } /** + * Two windows: a window on the left and a window on the right. + * Mouse is clicked on the left window and remains down. Touch is touched on the right and remains + * down. Then, on the left window, also place second touch pointer down. + * This test tries to reproduce a crash. + * In the buggy implementation, second pointer down on the left window would cause a crash. + */ +TEST_F(InputDispatcherTest, MultiDeviceSplitTouch) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> leftWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 200, 200)); + + sp<FakeWindowHandle> rightWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + rightWindow->setFrame(Rect(200, 0, 400, 200)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow}}}); + + const int32_t touchDeviceId = 4; + const int32_t mouseDeviceId = 6; + NotifyMotionArgs args; + + // Start hovering over the left window + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId))); + + // Mouse down on left window + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId))); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId))); + + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + + // First touch pointer down on right window + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .build())); + leftWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + + rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Second touch pointer down on left window + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .build())); + leftWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); + // This MOVE event is not necessary (doesn't carry any new information), but it's there in the + // current implementation. + const std::map<int32_t, PointF> expectedPointers{{0, PointF{100, 100}}}; + rightWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers))); + + leftWindow->assertNoEvents(); + rightWindow->assertNoEvents(); +} + +/** + * On a single window, use two different devices: mouse and touch. + * Touch happens first, with two pointers going down, and then the first pointer leaving. + * Mouse is clicked next, which causes the touch stream to be aborted with ACTION_CANCEL. + * Finally, a second touch pointer goes down again. Ensure the second touch pointer is ignored, + * because the mouse is currently down, and a POINTER_DOWN event from the touchscreen does not + * represent a new gesture. + */ +TEST_F(InputDispatcherTest, MixedTouchAndMouseWithPointerDown) { + 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, 400, 400)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + const int32_t touchDeviceId = 4; + const int32_t mouseDeviceId = 6; + NotifyMotionArgs args; + + // First touch pointer down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .build())); + // Second touch pointer down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100)) + .build())); + // First touch pointer lifts. The second one remains down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100)) + .build())); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); + window->consumeMotionEvent(WithMotionAction(POINTER_0_UP)); + + // Mouse down. The touch should be canceled + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(320).y(100)) + .build())); + + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId), + WithPointerCount(1u))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId))); + + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(320).y(100)) + .build())); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + + // Second touch pointer down. + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_0_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(350).y(100)) + .build())); + // The pointer_down event should be ignored + window->assertNoEvents(); +} + +/** + * Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels + * the injected event. + */ +TEST_F(InputDispatcherTest, UnfinishedInjectedEvent) { + 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, 400, 400)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + const int32_t touchDeviceId = 4; + NotifyMotionArgs args; + // Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after + // completion. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(50) + .y(50)) + .build())); + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID))); + + // Now a real touch comes. Rather than crashing or dropping the real event, the injected pointer + // should be canceled and the new gesture should take over. + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(300).y(100)) + .build())); + + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(VIRTUAL_KEYBOARD_ID))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** * This test is similar to the test above, but the sequence of injected events is different. * * Two windows: a window on the left and a window on the right. @@ -2541,6 +2816,182 @@ TEST_F(InputDispatcherTest, StylusHoverAndTouchTap) { } /** + * A spy window above a window with no input channel. + * Start hovering with a stylus device, and then tap with it. + * Ensure spy window receives the entire sequence. + */ +TEST_F(InputDispatcherTest, StylusHoverAndDownNoInputChannel) { + 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, 200, 200)); + spyWindow->setTrustedOverlay(true); + spyWindow->setSpy(true); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setNoInputChannel(true); + window->setFrame(Rect(0, 0, 200, 200)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}}); + + NotifyMotionArgs args; + + // Start hovering with stylus + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + // Stop hovering + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + + // Stylus touches down + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Stylus goes up + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_UP)); + + // Again hover + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + // Stop hovering + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + + // No more events + spyWindow->assertNoEvents(); + window->assertNoEvents(); +} + +/** + * Start hovering with a mouse, and then tap with a touch device. Pilfer the touch stream. + * Next, click with the mouse device. Both windows (spy and regular) should receive the new mouse + * ACTION_DOWN event because that's a new gesture, and pilfering should no longer be active. + * While the mouse is down, new move events from the touch device should be ignored. + */ +TEST_F(InputDispatcherTest, TouchPilferAndMouseMove) { + 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, 200, 200)); + spyWindow->setTrustedOverlay(true); + spyWindow->setSpy(true); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 200, 200)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, window}}}); + + const int32_t mouseDeviceId = 7; + const int32_t touchDeviceId = 4; + NotifyMotionArgs args; + + // Hover a bit with mouse first + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + spyWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId))); + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId))); + + // Start touching + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(55).y(55)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + // Pilfer the stream + EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindow->getToken())); + window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(60).y(60)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + // Mouse down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + + spyWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId))); + spyWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId))); + + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(100).y(100)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + + // Mouse move! + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(110).y(110)) + .build())); + spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + window->consumeMotionEvent(WithMotionAction(ACTION_MOVE)); + + // Touch move! + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(65).y(65)) + .build())); + + // No more events + spyWindow->assertNoEvents(); + window->assertNoEvents(); +} + +/** * On the display, have a single window, and also an area where there's no window. * First pointer touches the "no window" area of the screen. Second pointer touches the window. * Make sure that the window receives the second pointer, and first pointer is simply ignored. @@ -2649,8 +3100,8 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) { window1->assertNoEvents(); // Now move the pointer on the first window - mDispatcher->notifyMotion( - &(args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}}))); + mDispatcher->notifyMotion(&( + args = generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}}))); mDispatcher->waitForIdle(); window1->consumeMotionEvent(WithDownTime(downTimeForWindow1)); @@ -2705,7 +3156,8 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(300) .y(400)) .build())); - windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT); + windowLeft->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + windowLeft->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2750,7 +3202,6 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { .x(900) .y(400)) .build())); - windowLeft->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)); windowRight->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)); // No more events @@ -2758,6 +3209,70 @@ TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { windowRight->assertNoEvents(); } +/** + * Put two fingers down (and don't release them) and click the mouse button. + * The clicking of mouse is a new ACTION_DOWN event. Since it's from a different device, the + * currently active gesture should be canceled, and the new one should proceed. + */ +TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) { + 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, 600, 800)); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + + const int32_t touchDeviceId = 4; + const int32_t mouseDeviceId = 6; + NotifyMotionArgs args; + + // Two pointers down + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .build())); + + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(100).y(100)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(120).y(120)) + .build())); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); + + // Inject a series of mouse events for a mouse click + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(300).y(400)) + .build())); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId), + WithPointerCount(2u))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId))); + + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(300).y(400)) + .build())); + window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); + + // Try to send more touch events while the mouse is down. Since it's a continuation of an + // already canceled gesture, it should be ignored. + mDispatcher->notifyMotion(&( + args = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(101).y(101)) + .pointer(PointerBuilder(1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(121).y(121)) + .build())); + window->assertNoEvents(); +} + TEST_F(InputDispatcherTest, HoverWithSpyWindows) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); @@ -2940,7 +3455,8 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + window->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(mDispatcher, @@ -2984,7 +3500,7 @@ TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) { .x(300) .y(400)) .build())); - window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)); + window->assertNoEvents(); } /** @@ -3017,6 +3533,42 @@ TEST_F(InputDispatcherTest, HoverExitIsSentToRemovedWindow) { } /** + * If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT. + */ +TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) { + 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}}}); + + const int32_t mouseDeviceId = 7; + const int32_t touchDeviceId = 4; + NotifyMotionArgs args; + + // Start hovering with the mouse + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE) + .deviceId(mouseDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_MOUSE).x(10).y(10)) + .build())); + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId))); + + // Touch goes down + mDispatcher->notifyMotion( + &(args = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .deviceId(touchDeviceId) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) + .build())); + + window->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId))); + window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId))); +} + +/** * Inject a mouse hover event followed by a tap from touchscreen. * The tap causes a HOVER_EXIT event to be generated because the current event * stream's source has been switched. @@ -3386,8 +3938,7 @@ class InputDispatcherDisplayProjectionTest : public InputDispatcherTest { public: void SetUp() override { InputDispatcherTest::SetUp(); - mDisplayInfos.clear(); - mWindowInfos.clear(); + removeAllWindowsAndDisplays(); } void addDisplayInfo(int displayId, const ui::Transform& transform) { @@ -3403,6 +3954,11 @@ public: mDispatcher->onWindowInfosChanged(mWindowInfos, mDisplayInfos); } + void removeAllWindowsAndDisplays() { + mDisplayInfos.clear(); + mWindowInfos.clear(); + } + // Set up a test scenario where the display has a scaled projection and there are two windows // on the display. std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupScaledDisplayScenario() { @@ -3435,11 +3991,11 @@ private: std::vector<gui::WindowInfo> mWindowInfos; }; -TEST_F(InputDispatcherDisplayProjectionTest, HitTestsInDisplaySpace) { +TEST_F(InputDispatcherDisplayProjectionTest, HitTestCoordinateSpaceConsistency) { auto [firstWindow, secondWindow] = setupScaledDisplayScenario(); // Send down to the first window. The point is represented in the display space. The point is - // selected so that if the hit test was done with the transform applied to it, then it would - // end up in the incorrect window. + // selected so that if the hit test was performed with the point and the bounds being in + // different coordinate spaces, the event would end up in the incorrect window. NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {PointF{75, 55}}); @@ -3514,6 +4070,81 @@ TEST_F(InputDispatcherDisplayProjectionTest, WindowGetsEventsInCorrectCoordinate EXPECT_EQ(80, event->getY(0)); } +/** Ensure consistent behavior of InputDispatcher in all orientations. */ +class InputDispatcherDisplayOrientationFixture + : public InputDispatcherDisplayProjectionTest, + public ::testing::WithParamInterface<ui::Rotation> {}; + +// This test verifies the touchable region of a window for all rotations of the display by tapping +// in different locations on the display, specifically points close to the four corners of a +// window. +TEST_P(InputDispatcherDisplayOrientationFixture, HitTestInDifferentOrientations) { + constexpr static int32_t displayWidth = 400; + constexpr static int32_t displayHeight = 800; + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + const auto rotation = GetParam(); + + // Set up the display with the specified rotation. + const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270; + const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth; + const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight; + const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation), + logicalDisplayWidth, logicalDisplayHeight); + addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform); + + // Create a window with its bounds determined in the logical display. + const Rect frameInLogicalDisplay(100, 100, 200, 300); + const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(frameInDisplay, displayTransform); + addWindow(window); + + // The following points in logical display space should be inside the window. + static const std::array<vec2, 4> insidePoints{ + {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}}; + for (const auto pointInsideWindow : insidePoints) { + const vec2 p = displayTransform.inverse().transform(pointInsideWindow); + const PointF pointInDisplaySpace{p.x, p.y}; + const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInDisplaySpace}); + mDispatcher->notifyMotion(&down); + window->consumeMotionDown(); + + const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInDisplaySpace}); + mDispatcher->notifyMotion(&up); + window->consumeMotionUp(); + } + + // The following points in logical display space should be outside the window. + static const std::array<vec2, 5> outsidePoints{ + {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}}; + for (const auto pointOutsideWindow : outsidePoints) { + const vec2 p = displayTransform.inverse().transform(pointOutsideWindow); + const PointF pointInDisplaySpace{p.x, p.y}; + const auto down = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInDisplaySpace}); + mDispatcher->notifyMotion(&down); + + const auto up = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {pointInDisplaySpace}); + mDispatcher->notifyMotion(&up); + } + window->assertNoEvents(); +} + +// Run the precision tests for all rotations. +INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests, + InputDispatcherDisplayOrientationFixture, + ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, + ui::ROTATION_270), + [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) { + return ftl::enum_string(testParamInfo.param); + }); + using TransferFunction = std::function<bool(const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder>, sp<IBinder>)>; @@ -4903,6 +5534,7 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_StopsKeyRepeatAfterDisableInp } TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFromInputDispatcher) { + GTEST_SKIP() << "Flaky test (b/270393106)"; sendAndConsumeKeyDown(/*deviceId=*/1); for (int32_t repeatCount = 1; repeatCount <= 10; ++repeatCount) { InputEvent* repeatEvent = mWindow->consume(); @@ -4913,6 +5545,7 @@ TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseEventIdFrom } TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_RepeatKeyEventsUseUniqueEventId) { + GTEST_SKIP() << "Flaky test (b/270393106)"; sendAndConsumeKeyDown(/*deviceId=*/1); std::unordered_set<int32_t> idSet; @@ -5042,6 +5675,13 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) { windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID); + // Lift up the touch from the second display + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + windowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID); + monitorInSecondary.consumeMotionUp(SECOND_DISPLAY_ID); + // Test inject a non-pointer motion event. // If specific a display, it will dispatch to the focused window of particular display, // or it will dispatch to the focused window of focused display. diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index a02ef0548e..ae300066d2 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -54,7 +54,8 @@ std::list<NotifyArgs> InputMapperTest::configureDevice(uint32_t changes) { if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO | - InputReaderConfiguration::CHANGE_POINTER_CAPTURE))) { + InputReaderConfiguration::CHANGE_POINTER_CAPTURE | + InputReaderConfiguration::CHANGE_DEVICE_TYPE))) { mReader->requestRefreshConfiguration(changes); mReader->loopOnce(); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index fe7af80b92..711cfbf922 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -3658,8 +3658,8 @@ protected: }; TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { - // For external devices, non-media keys will trigger wake on key down. Media keys need to be - // marked as WAKE in the keylayout file to trigger wake. + // For external devices, keys will trigger wake on key down. Media keys should also trigger + // wake if triggered from external devices. mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, 0); mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0); @@ -3681,7 +3681,7 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) { process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); - ASSERT_EQ(uint32_t(0), args.policyFlags); + ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args)); @@ -6724,6 +6724,29 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirec ASSERT_FALSE(fakePointerController->isPointerShown()); } +TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) { + // Initialize the device without setting device source to touch navigation. + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(ui::ROTATION_0); + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + // Ensure that the device is created as a touchscreen, not touch navigation. + ASSERT_EQ(AINPUT_SOURCE_TOUCHSCREEN, mapper.getSources()); + + // Add device type association after the device was created. + mFakePolicy->addDeviceTypeAssociation(DEVICE_LOCATION, "touchNavigation"); + + // Send update to the mapper. + std::list<NotifyArgs> unused2 = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + InputReaderConfiguration::CHANGE_DEVICE_TYPE /*changes*/); + + // Check whether device type update was successful. + ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mDevice->getSources()); +} + // --- TouchDisplayProjectionTest --- class TouchDisplayProjectionTest : public SingleTouchInputMapperTest { @@ -6731,28 +6754,30 @@ 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(ui::Rotation orientation, Rect naturalPhysicalDisplay) { + void configurePhysicalDisplay(ui::Rotation orientation, Rect naturalPhysicalDisplay, + int32_t naturalDisplayWidth = DISPLAY_WIDTH, + int32_t naturalDisplayHeight = DISPLAY_HEIGHT) { uint32_t inverseRotationFlags; - auto width = DISPLAY_WIDTH; - auto height = DISPLAY_HEIGHT; + auto rotatedWidth = naturalDisplayWidth; + auto rotatedHeight = naturalDisplayHeight; switch (orientation) { case ui::ROTATION_90: inverseRotationFlags = ui::Transform::ROT_270; - std::swap(width, height); + std::swap(rotatedWidth, rotatedHeight); break; case ui::ROTATION_180: inverseRotationFlags = ui::Transform::ROT_180; break; case ui::ROTATION_270: inverseRotationFlags = ui::Transform::ROT_90; - std::swap(width, height); + std::swap(rotatedWidth, rotatedHeight); break; case ui::ROTATION_0: inverseRotationFlags = ui::Transform::ROT_0; break; } - const ui::Transform rotation(inverseRotationFlags, width, height); + const ui::Transform rotation(inverseRotationFlags, rotatedWidth, rotatedHeight); const Rect rotatedPhysicalDisplay = rotation.transform(naturalPhysicalDisplay); std::optional<DisplayViewport> internalViewport = @@ -6771,8 +6796,8 @@ public: v.physicalRight = rotatedPhysicalDisplay.right; v.physicalBottom = rotatedPhysicalDisplay.bottom; - v.deviceWidth = width; - v.deviceHeight = height; + v.deviceWidth = rotatedWidth; + v.deviceHeight = rotatedHeight; v.isActive = true; v.uniqueId = UNIQUE_ID; @@ -6886,6 +6911,243 @@ TEST_F(TouchDisplayProjectionTest, EmitsTouchDownAfterEnteringPhysicalDisplay) { } } +// --- TouchscreenPrecisionTests --- + +// This test suite is used to ensure that touchscreen devices are scaled and configured correctly +// in various orientations and with different display rotations. We configure the touchscreen to +// have a higher resolution than that of the display by an integer scale factor in each axis so that +// we can enforce that coordinates match precisely as expected. +class TouchscreenPrecisionTestsFixture : public TouchDisplayProjectionTest, + public ::testing::WithParamInterface<ui::Rotation> { +public: + void SetUp() override { + SingleTouchInputMapperTest::SetUp(); + + // Prepare the raw axes to have twice the resolution of the display in the X axis and + // four times the resolution of the display in the Y axis. + prepareButtons(); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_X, PRECISION_RAW_X_MIN, PRECISION_RAW_X_MAX, + PRECISION_RAW_X_FLAT, PRECISION_RAW_X_FUZZ, + PRECISION_RAW_X_RES); + mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_Y, PRECISION_RAW_Y_MIN, PRECISION_RAW_Y_MAX, + PRECISION_RAW_Y_FLAT, PRECISION_RAW_Y_FUZZ, + PRECISION_RAW_Y_RES); + } + + static const int32_t PRECISION_RAW_X_MIN = TouchInputMapperTest::RAW_X_MIN; + static const int32_t PRECISION_RAW_X_MAX = PRECISION_RAW_X_MIN + DISPLAY_WIDTH * 2 - 1; + static const int32_t PRECISION_RAW_Y_MIN = TouchInputMapperTest::RAW_Y_MIN; + static const int32_t PRECISION_RAW_Y_MAX = PRECISION_RAW_Y_MIN + DISPLAY_HEIGHT * 4 - 1; + + static const int32_t PRECISION_RAW_X_RES = 50; // units per millimeter + static const int32_t PRECISION_RAW_Y_RES = 100; // units per millimeter + + static const int32_t PRECISION_RAW_X_FLAT = 16; + static const int32_t PRECISION_RAW_Y_FLAT = 32; + + static const int32_t PRECISION_RAW_X_FUZZ = 4; + static const int32_t PRECISION_RAW_Y_FUZZ = 8; + + static const std::array<Point, 4> kRawCorners; +}; + +const std::array<Point, 4> TouchscreenPrecisionTestsFixture::kRawCorners = {{ + {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MIN}, // left-top + {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MIN}, // right-top + {PRECISION_RAW_X_MAX, PRECISION_RAW_Y_MAX}, // right-bottom + {PRECISION_RAW_X_MIN, PRECISION_RAW_Y_MAX}, // left-bottom +}}; + +// Tests for how the touchscreen is oriented relative to the natural orientation of the display. +// For example, if a touchscreen is configured with an orientation of 90 degrees, it is a portrait +// touchscreen panel that is used on a device whose natural display orientation is in landscape. +TEST_P(TouchscreenPrecisionTestsFixture, OrientationPrecision) { + enum class Orientation { + ORIENTATION_0 = ui::toRotationInt(ui::ROTATION_0), + ORIENTATION_90 = ui::toRotationInt(ui::ROTATION_90), + ORIENTATION_180 = ui::toRotationInt(ui::ROTATION_180), + ORIENTATION_270 = ui::toRotationInt(ui::ROTATION_270), + ftl_last = ORIENTATION_270, + }; + using Orientation::ORIENTATION_0, Orientation::ORIENTATION_90, Orientation::ORIENTATION_180, + Orientation::ORIENTATION_270; + static const std::map<Orientation, std::array<vec2, 4> /*mappedCorners*/> kMappedCorners = { + {ORIENTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}}, + {ORIENTATION_90, {{{0, 479.5}, {0, 0}, {799.75, 0}, {799.75, 479.5}}}}, + {ORIENTATION_180, {{{479.5, 799.75}, {0, 799.75}, {0, 0}, {479.5, 0}}}}, + {ORIENTATION_270, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}}, + }; + + const auto touchscreenOrientation = static_cast<Orientation>(ui::toRotationInt(GetParam())); + + // Configure the touchscreen as being installed in the one of the four different orientations + // relative to the display. + addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.orientation", ftl::enum_string(touchscreenOrientation).c_str()); + prepareDisplay(ui::ROTATION_0); + + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + // If the touchscreen is installed in a rotated orientation relative to the display (i.e. in + // orientations of either 90 or 270) this means the display's natural resolution will be + // flipped. + const bool displayRotated = + touchscreenOrientation == ORIENTATION_90 || touchscreenOrientation == ORIENTATION_270; + const int32_t width = displayRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + const int32_t height = displayRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + const Rect physicalFrame{0, 0, width, height}; + configurePhysicalDisplay(ui::ROTATION_0, physicalFrame, width, height); + + const auto& expectedPoints = kMappedCorners.at(touchscreenOrientation); + const float expectedPrecisionX = displayRotated ? 4 : 2; + const float expectedPrecisionY = displayRotated ? 2 : 4; + + // Test all four corners. + for (int i = 0; i < 4; i++) { + const auto& raw = kRawCorners[i]; + processDown(mapper, raw.x, raw.y); + processSync(mapper); + const auto& expected = expectedPoints[i]; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(expected.x, expected.y), + WithPrecision(expectedPrecisionX, expectedPrecisionY)))) + << "Failed to process raw point (" << raw.x << ", " << raw.y << ") " + << "with touchscreen orientation " + << ftl::enum_string(touchscreenOrientation).c_str() << ", expected point (" + << expected.x << ", " << expected.y << ")."; + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(expected.x, expected.y)))); + } +} + +TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionWhenOrientationAware) { + static const std::map<ui::Rotation /*rotation*/, std::array<vec2, 4> /*mappedCorners*/> + kMappedCorners = { + {ui::ROTATION_0, {{{0, 0}, {479.5, 0}, {479.5, 799.75}, {0, 799.75}}}}, + {ui::ROTATION_90, {{{0.5, 0}, {480, 0}, {480, 799.75}, {0.5, 799.75}}}}, + {ui::ROTATION_180, {{{0.5, 0.25}, {480, 0.25}, {480, 800}, {0.5, 800}}}}, + {ui::ROTATION_270, {{{0, 0.25}, {479.5, 0.25}, {479.5, 800}, {0, 800}}}}, + }; + + const ui::Rotation displayRotation = GetParam(); + + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(displayRotation); + + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + const auto& expectedPoints = kMappedCorners.at(displayRotation); + + // Test all four corners. + for (int i = 0; i < 4; i++) { + const auto& expected = expectedPoints[i]; + const auto& raw = kRawCorners[i]; + processDown(mapper, raw.x, raw.y); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(expected.x, expected.y), WithPrecision(2, 4)))) + << "Failed to process raw point (" << raw.x << ", " << raw.y << ") " + << "with display rotation " << ui::toCString(displayRotation) + << ", expected point (" << expected.x << ", " << expected.y << ")."; + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(expected.x, expected.y)))); + } +} + +TEST_P(TouchscreenPrecisionTestsFixture, RotationPrecisionOrientationAwareInOri270) { + static const std::map<ui::Rotation /*orientation*/, std::array<vec2, 4> /*mappedCorners*/> + kMappedCorners = { + {ui::ROTATION_0, {{{799.75, 0}, {799.75, 479.5}, {0, 479.5}, {0, 0}}}}, + {ui::ROTATION_90, {{{800, 0}, {800, 479.5}, {0.25, 479.5}, {0.25, 0}}}}, + {ui::ROTATION_180, {{{800, 0.5}, {800, 480}, {0.25, 480}, {0.25, 0.5}}}}, + {ui::ROTATION_270, {{{799.75, 0.5}, {799.75, 480}, {0, 480}, {0, 0.5}}}}, + }; + + const ui::Rotation displayRotation = GetParam(); + + addConfigurationProperty("touch.deviceType", "touchScreen"); + addConfigurationProperty("touch.orientation", "ORIENTATION_270"); + + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + + // Ori 270, so width and height swapped + const Rect physicalFrame{0, 0, DISPLAY_HEIGHT, DISPLAY_WIDTH}; + prepareDisplay(displayRotation); + configurePhysicalDisplay(displayRotation, physicalFrame, DISPLAY_HEIGHT, DISPLAY_WIDTH); + + const auto& expectedPoints = kMappedCorners.at(displayRotation); + + // Test all four corners. + for (int i = 0; i < 4; i++) { + const auto& expected = expectedPoints[i]; + const auto& raw = kRawCorners[i]; + processDown(mapper, raw.x, raw.y); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), + WithCoords(expected.x, expected.y), WithPrecision(4, 2)))) + << "Failed to process raw point (" << raw.x << ", " << raw.y << ") " + << "with display rotation " << ui::toCString(displayRotation) + << ", expected point (" << expected.x << ", " << expected.y << ")."; + processUp(mapper); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(expected.x, expected.y)))); + } +} + +TEST_P(TouchscreenPrecisionTestsFixture, MotionRangesAreOrientedInRotatedDisplay) { + const ui::Rotation displayRotation = GetParam(); + + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(displayRotation); + + __attribute__((unused)) SingleTouchInputMapper& mapper = + addMapperAndConfigure<SingleTouchInputMapper>(); + + const InputDeviceInfo deviceInfo = mDevice->getDeviceInfo(); + // MotionRanges use display pixels as their units + const auto* xRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN); + const auto* yRange = deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN); + + // The MotionRanges should be oriented in the rotated display's coordinate space + const bool displayRotated = + displayRotation == ui::ROTATION_90 || displayRotation == ui::ROTATION_270; + + constexpr float MAX_X = 479.5; + constexpr float MAX_Y = 799.75; + EXPECT_EQ(xRange->min, 0.f); + EXPECT_EQ(yRange->min, 0.f); + EXPECT_EQ(xRange->max, displayRotated ? MAX_Y : MAX_X); + EXPECT_EQ(yRange->max, displayRotated ? MAX_X : MAX_Y); + + EXPECT_EQ(xRange->flat, 8.f); + EXPECT_EQ(yRange->flat, 8.f); + + EXPECT_EQ(xRange->fuzz, 2.f); + EXPECT_EQ(yRange->fuzz, 2.f); + + EXPECT_EQ(xRange->resolution, 25.f); // pixels per millimeter + EXPECT_EQ(yRange->resolution, 25.f); // pixels per millimeter +} + +// Run the precision tests for all rotations. +INSTANTIATE_TEST_SUITE_P(TouchscreenPrecisionTests, TouchscreenPrecisionTestsFixture, + ::testing::Values(ui::ROTATION_0, ui::ROTATION_90, ui::ROTATION_180, + ui::ROTATION_270), + [](const testing::TestParamInfo<ui::Rotation>& testParamInfo) { + return ftl::enum_string(testParamInfo.param); + }); + // --- ExternalStylusFusionTest --- class ExternalStylusFusionTest : public SingleTouchInputMapperTest { diff --git a/services/inputflinger/tests/TestInputListenerMatchers.h b/services/inputflinger/tests/TestInputListenerMatchers.h index b9d96076f1..edd14f82e7 100644 --- a/services/inputflinger/tests/TestInputListenerMatchers.h +++ b/services/inputflinger/tests/TestInputListenerMatchers.h @@ -176,4 +176,10 @@ MATCHER_P(WithDownTime, downTime, "InputEvent with specified downTime") { return arg.downTime == downTime; } +MATCHER_P2(WithPrecision, xPrecision, yPrecision, "MotionEvent with specified precision") { + *result_listener << "expected x-precision " << xPrecision << " and y-precision " << yPrecision + << ", but got " << arg.xPrecision << " and " << arg.yPrecision; + return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision; +} + } // namespace android diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index 7c9be5c8d4..546121d6ac 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -86,8 +86,8 @@ public: int32_t getDeviceControllerNumber(int32_t deviceId) const override { return mFdp->ConsumeIntegral<int32_t>(); } - void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const override { - *outConfiguration = mFuzzConfig; + std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override { + return mFuzzConfig; } status_t getAbsoluteAxisInfo(int32_t deviceId, int axis, RawAbsoluteAxisInfo* outAxisInfo) const override { diff --git a/services/memtrackproxy/MemtrackProxy.cpp b/services/memtrackproxy/MemtrackProxy.cpp index 467616724a..9e41a9333e 100644 --- a/services/memtrackproxy/MemtrackProxy.cpp +++ b/services/memtrackproxy/MemtrackProxy.cpp @@ -97,9 +97,14 @@ bool MemtrackProxy::CheckPid(pid_t calling_pid, pid_t request_pid) { return calling_pid == request_pid; } -MemtrackProxy::MemtrackProxy() - : memtrack_hidl_instance_(MemtrackProxy::MemtrackHidlInstance()), - memtrack_aidl_instance_(MemtrackProxy::MemtrackAidlInstance()) {} +MemtrackProxy::MemtrackProxy() { + memtrack_aidl_instance_ = MemtrackProxy::MemtrackAidlInstance(); + + // Only check for a HIDL implementation if we failed to get the AIDL service + if (!memtrack_aidl_instance_) { + memtrack_hidl_instance_ = MemtrackProxy::MemtrackHidlInstance(); + } +} ndk::ScopedAStatus MemtrackProxy::getMemory(int pid, MemtrackType type, std::vector<MemtrackRecord>* _aidl_return) { diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 4ac9651a7b..4fff8bb1b4 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -28,10 +28,10 @@ using util::ProtoOutputStream; SensorService::SensorDirectConnection::SensorDirectConnection(const sp<SensorService>& service, uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName) + const String16& opPackageName, int deviceId) : mService(service), mUid(uid), mMem(*mem), mHalChannelHandle(halChannelHandle), - mOpPackageName(opPackageName), mDestroyed(false) { + mOpPackageName(opPackageName), mDeviceId(deviceId), mDestroyed(false) { mUserId = multiuser_get_user_id(mUid); ALOGD_IF(DEBUG_CONNECTIONS, "Created SensorDirectConnection"); } @@ -180,8 +180,7 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int }; Mutex::Autolock _l(mConnectionLock); - SensorDevice& dev(SensorDevice::getInstance()); - int ret = dev.configureDirectChannel(handle, getHalChannelHandle(), &config); + int ret = configure(handle, &config); if (rateLevel == SENSOR_DIRECT_RATE_STOP) { if (ret == NO_ERROR) { @@ -224,7 +223,6 @@ void SensorService::SensorDirectConnection::capRates() { std::unordered_map<int, int>& existingConnections = (!temporarilyStopped) ? mActivated : mActivatedBackup; - SensorDevice& dev(SensorDevice::getInstance()); for (auto &i : existingConnections) { int handle = i.first; int rateLevel = i.second; @@ -239,8 +237,8 @@ void SensorService::SensorDirectConnection::capRates() { // Only reconfigure the channel if it's ongoing if (!temporarilyStopped) { // Stopping before reconfiguring is the well-tested path in CTS - dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig); - dev.configureDirectChannel(handle, getHalChannelHandle(), &capConfig); + configure(handle, &stopConfig); + configure(handle, &capConfig); } } } @@ -258,7 +256,6 @@ void SensorService::SensorDirectConnection::uncapRates() { const struct sensors_direct_cfg_t stopConfig = { .rate_level = SENSOR_DIRECT_RATE_STOP }; - SensorDevice& dev(SensorDevice::getInstance()); for (auto &i : mMicRateBackup) { int handle = i.first; int rateLevel = i.second; @@ -273,13 +270,23 @@ void SensorService::SensorDirectConnection::uncapRates() { // Only reconfigure the channel if it's ongoing if (!temporarilyStopped) { // Stopping before reconfiguring is the well-tested path in CTS - dev.configureDirectChannel(handle, getHalChannelHandle(), &stopConfig); - dev.configureDirectChannel(handle, getHalChannelHandle(), &config); + configure(handle, &stopConfig); + configure(handle, &config); } } mMicRateBackup.clear(); } +int SensorService::SensorDirectConnection::configure( + int handle, const sensors_direct_cfg_t* config) { + if (mDeviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { + SensorDevice& dev(SensorDevice::getInstance()); + return dev.configureDirectChannel(handle, getHalChannelHandle(), config); + } else { + return mService->configureRuntimeSensorDirectChannel(handle, this, config); + } +} + void SensorService::SensorDirectConnection::stopAll(bool backupRecord) { Mutex::Autolock _l(mConnectionLock); stopAllLocked(backupRecord); @@ -290,9 +297,8 @@ void SensorService::SensorDirectConnection::stopAllLocked(bool backupRecord) { .rate_level = SENSOR_DIRECT_RATE_STOP }; - SensorDevice& dev(SensorDevice::getInstance()); for (auto &i : mActivated) { - dev.configureDirectChannel(i.first, getHalChannelHandle(), &config); + configure(i.first, &config); } if (backupRecord && mActivatedBackup.empty()) { @@ -306,8 +312,6 @@ void SensorService::SensorDirectConnection::recoverAll() { if (!mActivatedBackup.empty()) { stopAllLocked(false); - SensorDevice& dev(SensorDevice::getInstance()); - // recover list of report from backup ALOG_ASSERT(mActivated.empty(), "mActivated must be empty if mActivatedBackup was non-empty"); @@ -319,7 +323,7 @@ void SensorService::SensorDirectConnection::recoverAll() { struct sensors_direct_cfg_t config = { .rate_level = i.second }; - dev.configureDirectChannel(i.first, getHalChannelHandle(), &config); + configure(i.first, &config); } } } diff --git a/services/sensorservice/SensorDirectConnection.h b/services/sensorservice/SensorDirectConnection.h index d39a073f98..bfaf811330 100644 --- a/services/sensorservice/SensorDirectConnection.h +++ b/services/sensorservice/SensorDirectConnection.h @@ -39,7 +39,7 @@ class SensorService::SensorDirectConnection: public BnSensorEventConnection { public: SensorDirectConnection(const sp<SensorService>& service, uid_t uid, const sensors_direct_mem_t *mem, int32_t halChannelHandle, - const String16& opPackageName); + const String16& opPackageName, int deviceId); void dump(String8& result) const; void dump(util::ProtoOutputStream* proto) const; uid_t getUid() const { return mUid; } @@ -53,6 +53,7 @@ public: void onSensorAccessChanged(bool hasAccess); void onMicSensorAccessChanged(bool isMicToggleOn); userid_t getUserId() const { return mUserId; } + int getDeviceId() const { return mDeviceId; } protected: virtual ~SensorDirectConnection(); @@ -68,6 +69,9 @@ protected: private: bool hasSensorAccess() const; + // Sends the configuration to the relevant sensor device. + int configure(int handle, const sensors_direct_cfg_t* config); + // Stops all active sensor direct report requests. // // If backupRecord is true, stopped requests can be recovered @@ -95,6 +99,7 @@ private: const sensors_direct_mem_t mMem; const int32_t mHalChannelHandle; const String16 mOpPackageName; + const int mDeviceId; mutable Mutex mConnectionLock; std::unordered_map<int, int> mActivated; diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 3a0329cd09..0fb3cadc63 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -181,7 +181,7 @@ int SensorService::registerRuntimeSensor( handle, sensor.type, sensor.name); sp<RuntimeSensor::SensorCallback> runtimeSensorCallback( - new RuntimeSensorCallbackProxy(std::move(callback))); + new RuntimeSensorCallbackProxy(callback)); sensor_t runtimeSensor = sensor; // force the handle to be consistent runtimeSensor.handle = handle; @@ -193,11 +193,15 @@ int SensorService::registerRuntimeSensor( return mSensors.getNonSensor().getHandle(); } + if (mRuntimeSensorCallbacks.find(deviceId) == mRuntimeSensorCallbacks.end()) { + mRuntimeSensorCallbacks.emplace(deviceId, callback); + } return handle; } status_t SensorService::unregisterRuntimeSensor(int handle) { ALOGI("Unregistering runtime sensor handle 0x%x disconnected", handle); + int deviceId = getDeviceIdFromHandle(handle); { Mutex::Autolock _l(mLock); if (!unregisterDynamicSensorLocked(handle)) { @@ -210,6 +214,20 @@ status_t SensorService::unregisterRuntimeSensor(int handle) { for (const sp<SensorEventConnection>& connection : connLock.getActiveConnections()) { connection->removeSensor(handle); } + + // If this was the last sensor for this device, remove its callback. + bool deviceHasSensors = false; + mSensors.forEachEntry( + [&deviceId, &deviceHasSensors] (const SensorServiceUtil::SensorList::Entry& e) -> bool { + if (e.deviceId == deviceId) { + deviceHasSensors = true; + return false; // stop iterating + } + return true; + }); + if (!deviceHasSensors) { + mRuntimeSensorCallbacks.erase(deviceId); + } return OK; } @@ -1517,7 +1535,7 @@ int SensorService::isDataInjectionEnabled() { } sp<ISensorEventConnection> SensorService::createSensorDirectConnection( - const String16& opPackageName, uint32_t size, int32_t type, int32_t format, + const String16& opPackageName, int deviceId, uint32_t size, int32_t type, int32_t format, const native_handle *resource) { resetTargetSdkVersionCache(opPackageName); ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); @@ -1593,14 +1611,25 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( native_handle_set_fdsan_tag(clone); sp<SensorDirectConnection> conn; - SensorDevice& dev(SensorDevice::getInstance()); - int channelHandle = dev.registerDirectChannel(&mem); + int channelHandle = 0; + if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { + SensorDevice& dev(SensorDevice::getInstance()); + channelHandle = dev.registerDirectChannel(&mem); + } else { + auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId); + if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) { + ALOGE("Runtime sensor callback for deviceId %d not found", deviceId); + } else { + int fd = dup(clone->data[0]); + channelHandle = runtimeSensorCallback->second->onDirectChannelCreated(fd); + } + } if (channelHandle <= 0) { ALOGE("SensorDevice::registerDirectChannel returns %d", channelHandle); } else { mem.handle = clone; - conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName); + conn = new SensorDirectConnection(this, uid, &mem, channelHandle, opPackageName, deviceId); } if (conn == nullptr) { @@ -1614,6 +1643,24 @@ sp<ISensorEventConnection> SensorService::createSensorDirectConnection( return conn; } +int SensorService::configureRuntimeSensorDirectChannel( + int sensorHandle, const SensorDirectConnection* c, const sensors_direct_cfg_t* config) { + int deviceId = c->getDeviceId(); + int sensorDeviceId = getDeviceIdFromHandle(sensorHandle); + if (sensorDeviceId != c->getDeviceId()) { + ALOGE("Cannot configure direct channel created for device %d with a sensor that belongs" + "to device %d", c->getDeviceId(), sensorDeviceId); + return BAD_VALUE; + } + auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId); + if (runtimeSensorCallback == mRuntimeSensorCallbacks.end()) { + ALOGE("Runtime sensor callback for deviceId %d not found", deviceId); + return BAD_VALUE; + } + return runtimeSensorCallback->second->onDirectChannelConfigured( + c->getHalChannelHandle(), sensorHandle, config->rate_level); +} + int SensorService::setOperationParameter( int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints) { @@ -1769,8 +1816,18 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { void SensorService::cleanupConnection(SensorDirectConnection* c) { Mutex::Autolock _l(mLock); - SensorDevice& dev(SensorDevice::getInstance()); - dev.unregisterDirectChannel(c->getHalChannelHandle()); + int deviceId = c->getDeviceId(); + if (deviceId == RuntimeSensor::DEFAULT_DEVICE_ID) { + SensorDevice& dev(SensorDevice::getInstance()); + dev.unregisterDirectChannel(c->getHalChannelHandle()); + } else { + auto runtimeSensorCallback = mRuntimeSensorCallbacks.find(deviceId); + if (runtimeSensorCallback != mRuntimeSensorCallbacks.end()) { + runtimeSensorCallback->second->onDirectChannelDestroyed(c->getHalChannelHandle()); + } else { + ALOGE("Runtime sensor callback for deviceId %d not found", deviceId); + } + } mConnectionHolder.removeDirectConnection(c); } @@ -1848,6 +1905,19 @@ std::shared_ptr<SensorInterface> SensorService::getSensorInterfaceFromHandle(int return mSensors.getInterface(handle); } +int SensorService::getDeviceIdFromHandle(int handle) const { + int deviceId = RuntimeSensor::DEFAULT_DEVICE_ID; + mSensors.forEachEntry( + [&deviceId, handle] (const SensorServiceUtil::SensorList::Entry& e) -> bool { + if (e.si->getSensor().getHandle() == handle) { + deviceId = e.deviceId; + return false; // stop iterating + } + return true; + }); + return deviceId; +} + status_t SensorService::enable(const sp<SensorEventConnection>& connection, int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags, const String16& opPackageName) { diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 3f6a895b52..fe72a69f15 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -154,6 +154,10 @@ public: virtual status_t onConfigurationChanged(int handle, bool enabled, int64_t samplingPeriodNanos, int64_t batchReportLatencyNanos) = 0; + virtual int onDirectChannelCreated(int fd) = 0; + virtual void onDirectChannelDestroyed(int channelHandle) = 0; + virtual int onDirectChannelConfigured(int channelHandle, int sensorHandle, + int rateLevel) = 0; }; static char const* getServiceName() ANDROID_API { return "sensorservice"; } @@ -187,6 +191,9 @@ public: status_t unregisterRuntimeSensor(int handle) ANDROID_API; status_t sendRuntimeSensorEvent(const sensors_event_t& event) ANDROID_API; + int configureRuntimeSensorDirectChannel(int sensorHandle, const SensorDirectConnection* c, + const sensors_direct_cfg_t* config); + // Returns true if a sensor should be throttled according to our rate-throttling rules. static bool isSensorInCappedSet(int sensorType); @@ -370,7 +377,8 @@ private: int requestedMode, const String16& opPackageName, const String16& attributionTag); virtual int isDataInjectionEnabled(); virtual sp<ISensorEventConnection> createSensorDirectConnection(const String16& opPackageName, - uint32_t size, int32_t type, int32_t format, const native_handle *resource); + int deviceId, uint32_t size, int32_t type, int32_t format, + const native_handle *resource); virtual int setOperationParameter( int32_t handle, int32_t type, const Vector<float> &floats, const Vector<int32_t> &ints); virtual status_t dump(int fd, const Vector<String16>& args); @@ -380,6 +388,7 @@ private: String8 getSensorStringType(int handle) const; bool isVirtualSensor(int handle) const; std::shared_ptr<SensorInterface> getSensorInterfaceFromHandle(int handle) const; + int getDeviceIdFromHandle(int handle) const; 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); @@ -517,6 +526,7 @@ private: std::unordered_map<int, SensorServiceUtil::RecentEventLogger*> mRecentEvent; Mode mCurrentOperatingMode; std::queue<sensors_event_t> mRuntimeSensorEventQueue; + std::unordered_map</*deviceId*/int, sp<RuntimeSensorCallback>> mRuntimeSensorCallbacks; // 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/aidl/SensorManager.cpp b/services/sensorservice/aidl/SensorManager.cpp index b7aecdf1bd..08e00b4401 100644 --- a/services/sensorservice/aidl/SensorManager.cpp +++ b/services/sensorservice/aidl/SensorManager.cpp @@ -59,6 +59,9 @@ SensorManagerAidl::~SensorManagerAidl() { if (mPollThread.joinable()) { mPollThread.join(); } + + ::android::SensorManager::removeInstanceForPackage( + String16(ISensorManager::descriptor)); } ndk::ScopedAStatus createDirectChannel(::android::SensorManager& manager, size_t size, int type, diff --git a/services/sensorservice/aidl/fuzzer/fuzzer.cpp b/services/sensorservice/aidl/fuzzer/fuzzer.cpp index 1b63d76953..ee8ceb354c 100644 --- a/services/sensorservice/aidl/fuzzer/fuzzer.cpp +++ b/services/sensorservice/aidl/fuzzer/fuzzer.cpp @@ -16,7 +16,7 @@ #include <fuzzbinder/libbinder_ndk_driver.h> #include <fuzzer/FuzzedDataProvider.h> -#include <ServiceManager.h> +#include <fakeservicemanager/FakeServiceManager.h> #include <android-base/logging.h> #include <android/binder_interface_utils.h> #include <fuzzbinder/random_binder.h> @@ -29,7 +29,7 @@ using ndk::SharedRefBase; [[clang::no_destroy]] static std::once_flag gSmOnce; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - static android::sp<android::ServiceManager> fakeServiceManager = new android::ServiceManager(); + static android::sp<android::FakeServiceManager> fakeServiceManager = new android::FakeServiceManager(); std::call_once(gSmOnce, [&] { setDefaultServiceManager(fakeServiceManager); }); fakeServiceManager->clear(); diff --git a/services/sensorservice/hidl/SensorManager.cpp b/services/sensorservice/hidl/SensorManager.cpp index f04712c534..3d148e103a 100644 --- a/services/sensorservice/hidl/SensorManager.cpp +++ b/services/sensorservice/hidl/SensorManager.cpp @@ -60,6 +60,9 @@ SensorManager::~SensorManager() { if (mPollThread.joinable()) { mPollThread.join(); } + + ::android::SensorManager::removeInstanceForPackage( + String16(ISensorManager::descriptor)); } // Methods from ::android::frameworks::sensorservice::V1_0::ISensorManager follow. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 32684fc3b7..eae5871477 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -18,6 +18,7 @@ #include <cstdint> +#include <android/gui/CachingHint.h> #include <gui/HdrMetadata.h> #include <math/mat4.h> #include <ui/BlurRegion.h> @@ -212,8 +213,7 @@ struct LayerFECompositionState { float currentSdrHdrRatio = 1.f; float desiredSdrHdrRatio = 1.f; - bool isInternalDisplayOverlay = false; - + gui::CachingHint cachingHint = gui::CachingHint::Enabled; virtual ~LayerFECompositionState(); // Debugging diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index 24a7744542..d26ca9dd00 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -149,6 +149,9 @@ public: bool hasSolidColorLayers() const; + // True if any layer in this cached set has CachingHint::Disabled + bool cachingHintExcludesLayers() const; + private: const NonBufferHash mFingerprint; std::chrono::steady_clock::time_point mLastUpdate = std::chrono::steady_clock::now(); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index e309442220..d5c488e40e 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -73,6 +73,7 @@ enum class LayerStateField : uint32_t { BackgroundBlurRadius = 1u << 17, BlurRegions = 1u << 18, HasProtectedContent = 1u << 19, + CachingHint = 1u << 20, }; // clang-format on @@ -250,6 +251,8 @@ public: bool isProtected() const { return mIsProtected.get(); } + gui::CachingHint getCachingHint() const { return mCachingHint.get(); } + bool hasSolidColorCompositionType() const { return getOutputLayer()->getLayerFE().getCompositionState()->compositionType == aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; @@ -487,7 +490,15 @@ private: return layer->getLayerFE().getCompositionState()->hasProtectedContent; }}; - static const constexpr size_t kNumNonUniqueFields = 18; + OutputLayerState<gui::CachingHint, LayerStateField::CachingHint> + mCachingHint{[](auto layer) { + return layer->getLayerFE().getCompositionState()->cachingHint; + }, + [](const gui::CachingHint& cachingHint) { + return std::vector<std::string>{toString(cachingHint)}; + }}; + + static const constexpr size_t kNumNonUniqueFields = 19; std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() { std::array<const StateInterface*, kNumNonUniqueFields> constFields = @@ -501,13 +512,11 @@ private: } std::array<const StateInterface*, kNumNonUniqueFields> getNonUniqueFields() const { - return { - &mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode, + return {&mDisplayFrame, &mSourceCrop, &mBufferTransform, &mBlendMode, &mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace, &mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream, &mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions, - &mFrameNumber, &mIsProtected, - }; + &mFrameNumber, &mIsProtected, &mCachingHint}; } }; diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp index 531b65963d..615d04b460 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp @@ -126,6 +126,7 @@ void LayerFECompositionState::dump(std::string& out) const { dumpVal(out, "desired sdr/hdr ratio", desiredSdrHdrRatio); } dumpVal(out, "colorTransform", colorTransform); + dumpVal(out, "caching hint", toString(cachingHint)); out.append("\n"); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 7d9431605d..175dd1d825 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -538,8 +538,8 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, return; } - bool computeAboveCoveredExcludingOverlays = - coverage.aboveCoveredLayersExcludingOverlays && !layerFEState->isInternalDisplayOverlay; + bool computeAboveCoveredExcludingOverlays = coverage.aboveCoveredLayersExcludingOverlays && + !layerFEState->outputFilter.toInternalDisplay; /* * opaqueRegion: area of a surface that is fully opaque. diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index ed9a88d3c4..a00ce57e29 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -393,6 +393,18 @@ bool CachedSet::hasSolidColorLayers() const { }); } +bool CachedSet::cachingHintExcludesLayers() const { + const bool shouldExcludeLayers = + std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { + return layer.getState()->getCachingHint() == gui::CachingHint::Disabled; + }); + + LOG_ALWAYS_FATAL_IF(shouldExcludeLayers && getLayerCount() > 1, + "CachedSet is invalid: should be excluded but contains %zu layers", + getLayerCount()); + return shouldExcludeLayers; +} + void CachedSet::dump(std::string& result) const { const auto now = std::chrono::steady_clock::now(); diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 9175dd01a1..13b6307aea 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -413,6 +413,7 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) { bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout; const bool layerHasBlur = currentSet->hasBlurBehind(); + const bool layerDeniedFromCaching = currentSet->cachingHintExcludesLayers(); // Layers should also be considered inactive whenever their framerate is lower than 1fps. if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) { @@ -424,7 +425,8 @@ std::vector<Flattener::Run> Flattener::findCandidateRuns(time_point now) const { } } - if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) && + if (!layerDeniedFromCaching && layerIsInactive && + (firstLayer || runHasFirstLayer || !layerHasBlur) && !currentSet->hasUnsupportedDataspace()) { if (isPartOfRun) { builder.increment(); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 8555fd6d87..1a56ab751f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -147,6 +147,7 @@ public: MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); MOCK_METHOD(const aidl::android::hardware::graphics::composer3::OverlayProperties&, getOverlaySupport, (), (const, override)); + MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 9ad2edb983..aa83883e95 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -1312,6 +1312,18 @@ TEST_F(OutputLayerWriteStateToHWCTest, setBlockingRegion) { false); } +TEST_F(OutputLayerWriteStateToHWCTest, setCompositionTypeRefreshRateIndicator) { + mLayerFEState.compositionType = Composition::REFRESH_RATE_INDICATOR; + + expectGeometryCommonCalls(); + expectPerFrameCommonCalls(); + expectSetHdrMetadataAndBufferCalls(); + expectSetCompositionTypeCall(Composition::REFRESH_RATE_INDICATOR); + + mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0, + /*zIsOverridden*/ false, /*isPeekingThrough*/ false); +} + /* * OutputLayer::uncacheBuffers */ diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index d5d688e705..ca5ba69e8e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -577,6 +577,20 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { cachedSet.append(CachedSet(layer3)); } +TEST_F(CachedSetTest, cachingHintIncludesLayersByDefault) { + CachedSet cachedSet(*mTestLayers[0]->cachedSetLayer.get()); + EXPECT_FALSE(cachedSet.cachingHintExcludesLayers()); +} + +TEST_F(CachedSetTest, cachingHintExcludesLayersWhenDisabled) { + CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); + mTestLayers[0]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled; + mTestLayers[0]->layerState->update(&mTestLayers[0]->outputLayer); + + CachedSet cachedSet(layer1); + EXPECT_TRUE(cachedSet.cachingHintExcludesLayers()); +} + TEST_F(CachedSetTest, holePunch_requiresBuffer) { CachedSet::Layer& layer1 = *mTestLayers[0]->cachedSetLayer.get(); auto& layerFECompositionState = mTestLayers[0]->layerFECompositionState; diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 86cfee6f0a..778a0a8c93 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -1112,6 +1112,55 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM true); } +TEST_F(FlattenerTest, flattenLayers_skipsLayersDisabledFromCaching) { + auto& layerState1 = mTestLayers[0]->layerState; + const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; + + auto& layerState2 = mTestLayers[1]->layerState; + const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer; + + // The third layer has a CachingHint that prevents caching from running + auto& layerState3 = mTestLayers[2]->layerState; + const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer; + mTestLayers[2]->layerFECompositionState.cachingHint = gui::CachingHint::Disabled; + mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer); + + const std::vector<const LayerState*> layers = { + layerState1.get(), + layerState2.get(), + layerState3.get(), + }; + + initializeFlattener(layers); + + mTime += 200ms; + initializeOverrideBuffer(layers); + EXPECT_EQ(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + + // This will render a CachedSet. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) + .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. + EXPECT_EQ(nullptr, overrideBuffer1); + EXPECT_EQ(nullptr, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); + + // This time we merge the CachedSet in, so we have a new hash, and we should + // only have two sets. + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); + initializeOverrideBuffer(layers); + EXPECT_NE(getNonBufferHash(layers), + mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); + + EXPECT_NE(nullptr, overrideBuffer1); + EXPECT_EQ(overrideBuffer1, overrideBuffer2); + EXPECT_EQ(nullptr, overrideBuffer3); +} + TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { auto& layerState1 = mTestLayers[0]->layerState; const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer; diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp index 47b682059f..044917ead9 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp @@ -994,6 +994,45 @@ TEST_F(LayerStateTest, hasBlurBehind_withBlurRegion_returnsTrue) { EXPECT_TRUE(mLayerState->hasBlurBehind()); } +TEST_F(LayerStateTest, updateCachingHint) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.cachingHint = gui::CachingHint::Enabled; + 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.cachingHint = gui::CachingHint::Disabled; + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer); + EXPECT_EQ(ftl::Flags<LayerStateField>(LayerStateField::CachingHint), updates); +} + +TEST_F(LayerStateTest, compareCachingHint) { + OutputLayerCompositionState outputLayerCompositionState; + LayerFECompositionState layerFECompositionState; + layerFECompositionState.cachingHint = gui::CachingHint::Enabled; + 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.cachingHint = gui::CachingHint::Disabled; + setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState, + layerFECompositionStateTwo); + auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer); + + verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::CachingHint); + + EXPECT_TRUE(mLayerState->compare(*otherLayerState)); + EXPECT_TRUE(otherLayerState->compare(*mLayerState)); +} + TEST_F(LayerStateTest, dumpDoesNotCrash) { OutputLayerCompositionState outputLayerCompositionState; LayerFECompositionState layerFECompositionState; diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 5f73fbc1a6..3cdb3d5c76 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -408,8 +408,8 @@ HdrCapabilities DisplayDevice::getHdrCapabilities() const { capabilities.getDesiredMinLuminance()); } -void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, - bool showInMiddle) { +void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, + bool showRenderRate, bool showInMiddle) { if (!enable) { mRefreshRateOverlay.reset(); return; @@ -428,11 +428,22 @@ void DisplayDevice::enableRefreshRateOverlay(bool enable, bool showSpinner, bool features |= RefreshRateOverlay::Features::ShowInMiddle; } + if (setByHwc) { + features |= RefreshRateOverlay::Features::SetByHwc; + } + const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange(); mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(fpsRange, features); mRefreshRateOverlay->setLayerStack(getLayerStack()); mRefreshRateOverlay->setViewport(getSize()); - mRefreshRateOverlay->changeRefreshRate(getActiveMode().modePtr->getFps(), getActiveMode().fps); + updateRefreshRateOverlayRate(getActiveMode().modePtr->getFps(), getActiveMode().fps); +} + +void DisplayDevice::updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc) { + ATRACE_CALL(); + if (mRefreshRateOverlay && (!mRefreshRateOverlay->isSetByHwc() || setByHwc)) { + mRefreshRateOverlay->changeRefreshRate(displayFps, renderFps); + } } bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredModeId, @@ -441,7 +452,7 @@ bool DisplayDevice::onKernelTimerChanged(std::optional<DisplayModeId> desiredMod const auto newMode = mRefreshRateSelector->onKernelTimerChanged(desiredModeId, timerExpired); if (newMode) { - mRefreshRateOverlay->changeRefreshRate(newMode->modePtr->getFps(), newMode->fps); + updateRefreshRateOverlayRate(newMode->modePtr->getFps(), newMode->fps); return true; } } @@ -510,21 +521,21 @@ void DisplayDevice::clearDesiredActiveModeState() { mDesiredActiveModeChanged = false; } -void DisplayDevice::adjustRefreshRate(Fps leaderDisplayRefreshRate) { +void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) { using fps_approx_ops::operator==; if (mRequestedRefreshRate == 0_Hz) { return; } using fps_approx_ops::operator>; - if (mRequestedRefreshRate > leaderDisplayRefreshRate) { - mAdjustedRefreshRate = leaderDisplayRefreshRate; + if (mRequestedRefreshRate > pacesetterDisplayRefreshRate) { + mAdjustedRefreshRate = pacesetterDisplayRefreshRate; return; } unsigned divisor = static_cast<unsigned>( - std::round(leaderDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue())); - mAdjustedRefreshRate = leaderDisplayRefreshRate / divisor; + std::round(pacesetterDisplayRefreshRate.getValue() / mRequestedRefreshRate.getValue())); + mAdjustedRefreshRate = pacesetterDisplayRefreshRate / divisor; } std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index b86d9bec39..d9c3e1c825 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -237,8 +237,9 @@ public: } // Enables an overlay to be displayed with the current refresh rate - void enableRefreshRateOverlay(bool enable, bool showSpinner, bool showRenderRate, + void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate, bool showInMiddle) REQUIRES(kMainThreadContext); + void updateRefreshRateOverlayRate(Fps displayFps, Fps renderFps, bool setByHwc = false); bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; } bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired); void animateRefreshRateOverlay(); @@ -247,9 +248,9 @@ public: Fps getAdjustedRefreshRate() const { return mAdjustedRefreshRate; } - // Round the requested refresh rate to match a divisor of the leader + // Round the requested refresh rate to match a divisor of the pacesetter // display's refresh rate. Only supported for virtual displays. - void adjustRefreshRate(Fps leaderDisplayRefreshRate); + void adjustRefreshRate(Fps pacesetterDisplayRefreshRate); // release HWC resources (if any) for removable displays void disconnect(); @@ -290,7 +291,7 @@ private: // for virtual displays to match this requested refresh rate. const Fps mRequestedRefreshRate; - // Adjusted refresh rate, rounded to match a divisor of the leader + // Adjusted refresh rate, rounded to match a divisor of the pacesetter // display's refresh rate. Only supported for virtual displays. Fps mAdjustedRefreshRate = 0_Hz; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 4194a7e4f6..bd2680fbb5 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -1428,6 +1428,19 @@ Error AidlComposer::setHdrConversionStrategy(AidlHdrConversionStrategy hdrConver return Error::NONE; } +Error AidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display displayId, bool enabled) { + const auto status = + mAidlComposerClient->setRefreshRateChangedCallbackDebugEnabled(translate<int64_t>( + displayId), + enabled); + if (!status.isOk()) { + ALOGE("setRefreshRateChangedCallbackDebugEnabled failed %s", + status.getDescription().c_str()); + return static_cast<Error>(status.getServiceSpecificError()); + } + return Error::NONE; +} + Error AidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { Error error = Error::NONE; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h index d163ff2d4a..8313c09e58 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h @@ -239,6 +239,7 @@ public: void onHotplugDisconnect(Display) override; Error getHdrConversionCapabilities(std::vector<HdrConversionCapability>*) override; Error setHdrConversionStrategy(HdrConversionStrategy, Hdr*) override; + Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; private: // Many public functions above simply write a command into the command diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 9b9b7fdbff..c65c572920 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -294,6 +294,7 @@ public: std::vector<::aidl::android::hardware::graphics::common::HdrConversionCapability>*) = 0; virtual Error setHdrConversionStrategy( ::aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) = 0; + virtual Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) = 0; }; } // namespace Hwc2 diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 470bf76792..28148ac1dc 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -808,6 +808,21 @@ status_t HWComposer::setHdrConversionStrategy( return NO_ERROR; } +status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId displayId, + bool enabled) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = + mComposer->setRefreshRateChangedCallbackDebugEnabled(mDisplayData[displayId] + .hwcDisplay->getId(), + enabled); + if (error != hal::Error::NONE) { + ALOGE("Error in setting refresh refresh rate change callback debug enabled %s", + to_string(error).c_str()); + return INVALID_OPERATION; + } + return NO_ERROR; +} + status_t HWComposer::getDisplayDecorationSupport( PhysicalDisplayId displayId, std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>* diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 95568eba4c..7a3f41cc1c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -297,6 +297,7 @@ public: virtual status_t setHdrConversionStrategy( aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; + virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -453,6 +454,7 @@ public: status_t setHdrConversionStrategy( aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; + status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index 9bc62b6b04..23de4fa42e 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1358,6 +1358,10 @@ Error HidlComposer::setHdrConversionStrategy(HdrConversionStrategy, Hdr*) { return Error::UNSUPPORTED; } +Error HidlComposer::setRefreshRateChangedCallbackDebugEnabled(Display, bool) { + return Error::UNSUPPORTED; +} + Error HidlComposer::getClientTargetProperty( Display display, ClientTargetPropertyWithBrightness* outClientTargetProperty) { IComposerClient::ClientTargetProperty property; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h index 2bab1fed1f..d04652bf21 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h @@ -348,6 +348,7 @@ public: override; Error setHdrConversionStrategy(aidl::android::hardware::graphics::common::HdrConversionStrategy, Hdr*) override; + Error setRefreshRateChangedCallbackDebugEnabled(Display, bool) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp index 6d492c0f4a..5efa3944dd 100644 --- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp +++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.cpp @@ -24,9 +24,13 @@ namespace android::surfaceflinger { std::atomic<uint32_t> LayerCreationArgs::sSequence{1}; +uint32_t LayerCreationArgs::getInternalLayerId(uint32_t id) { + return id | INTERNAL_LAYER_PREFIX; +} + LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name, uint32_t flags, gui::LayerMetadata metadataArg, - std::optional<uint32_t> id) + std::optional<uint32_t> id, bool internalLayer) : flinger(flinger), client(std::move(client)), name(std::move(name)), @@ -46,16 +50,25 @@ LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, if (id) { sequence = *id; - sSequence = *id + 1; + if (internalLayer) { + sequence = getInternalLayerId(*id); + } else { + sSequence = *id + 1; + } } else { sequence = sSequence++; - if (sequence == UNASSIGNED_LAYER_ID) { + if (sequence >= INTERNAL_LAYER_PREFIX) { + sSequence = 1; ALOGW("Layer sequence id rolled over."); sequence = sSequence++; } } } +LayerCreationArgs::LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer) + : LayerCreationArgs(nullptr, nullptr, /*name=*/"", /*flags=*/0, /*metadata=*/{}, id, + internalLayer) {} + LayerCreationArgs::LayerCreationArgs(const LayerCreationArgs& args) : LayerCreationArgs(args.flinger, args.client, args.name, args.flags, args.metadata) {} diff --git a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h index 9d2aaab23d..8341e1dfc5 100644 --- a/services/surfaceflinger/FrontEnd/LayerCreationArgs.h +++ b/services/surfaceflinger/FrontEnd/LayerCreationArgs.h @@ -25,6 +25,7 @@ #include <optional> constexpr uint32_t UNASSIGNED_LAYER_ID = std::numeric_limits<uint32_t>::max(); +constexpr uint32_t INTERNAL_LAYER_PREFIX = 1u << 31; namespace android { class SurfaceFlinger; @@ -35,10 +36,13 @@ namespace android::surfaceflinger { struct LayerCreationArgs { static std::atomic<uint32_t> sSequence; + static uint32_t getInternalLayerId(uint32_t id); LayerCreationArgs(android::SurfaceFlinger*, sp<android::Client>, std::string name, - uint32_t flags, gui::LayerMetadata, - std::optional<uint32_t> id = std::nullopt); + uint32_t flags, gui::LayerMetadata, std::optional<uint32_t> id = std::nullopt, + bool internalLayer = false); + LayerCreationArgs(std::optional<uint32_t> id, bool internalLayer = false); + LayerCreationArgs(const LayerCreationArgs&); android::SurfaceFlinger* flinger; @@ -54,6 +58,8 @@ struct LayerCreationArgs { wp<IBinder> parentHandle = nullptr; wp<IBinder> mirrorLayerHandle = nullptr; ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK; + uint32_t parentId = UNASSIGNED_LAYER_ID; + uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID; }; } // namespace android::surfaceflinger diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp index afe557e440..c30465fbd4 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp @@ -130,6 +130,14 @@ const RequestedLayerState* LayerHierarchy::getLayer() const { return mLayer; } +const LayerHierarchy* LayerHierarchy::getRelativeParent() const { + return mRelativeParent; +} + +const LayerHierarchy* LayerHierarchy::getParent() const { + return mParent; +} + std::string LayerHierarchy::getDebugStringShort() const { std::string debug = "LayerHierarchy{"; debug += ((mLayer) ? mLayer->getDebugString() : "root") + " "; @@ -449,7 +457,7 @@ LayerHierarchy::ScopedAddToTraversalPath::ScopedAddToTraversalPath(TraversalPath traversalPath.id = layerId; traversalPath.variant = variant; if (variant == LayerHierarchy::Variant::Mirror) { - traversalPath.mirrorRootId = layerId; + traversalPath.mirrorRootId = mParentPath.id; } else if (variant == LayerHierarchy::Variant::Relative) { if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(), layerId) != traversalPath.relativeRootIds.end()) { diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h index 2ab897b35d..3dd89badff 100644 --- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h +++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h @@ -128,16 +128,24 @@ public: // Traverse the hierarchy and visit all child variants. void traverse(const Visitor& visitor) const { TraversalPath root = TraversalPath::ROOT; + if (mLayer) { + root.id = mLayer->id; + } traverse(visitor, root); } // Traverse the hierarchy in z-order, skipping children that have relative parents. void traverseInZOrder(const Visitor& visitor) const { TraversalPath root = TraversalPath::ROOT; + if (mLayer) { + root.id = mLayer->id; + } traverseInZOrder(visitor, root); } const RequestedLayerState* getLayer() const; + const LayerHierarchy* getRelativeParent() const; + const LayerHierarchy* getParent() const; std::string getDebugString(const char* prefix = "") const; std::string getDebugStringShort() const; // Traverse the hierarchy and return true if loops are found. The outInvalidRelativeRoot diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp index 66197be44a..fe42422f6f 100644 --- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp +++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp @@ -20,8 +20,7 @@ #define LOG_TAG "LayerLifecycleManager" #include "LayerLifecycleManager.h" -#include "Layer.h" // temporarily needed for LayerHandle -#include "LayerHandle.h" +#include "Client.h" // temporarily needed for LayerCreationArgs #include "LayerLog.h" #include "SwapErase.h" @@ -37,7 +36,6 @@ void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayer mGlobalChanges |= RequestedLayerState::Changes::Hierarchy; for (auto& newLayer : newLayers) { RequestedLayerState& layer = *newLayer.get(); - LLOGV(layer.id, "%s layer %s", __func__, layer.getDebugStringShort().c_str()); 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, @@ -68,6 +66,7 @@ void LayerLifecycleManager::addLayers(std::vector<std::unique_ptr<RequestedLayer if (layer.isRoot()) { updateDisplayMirrorLayers(layer); } + LLOGV(layer.id, "%s", layer.getDebugString().c_str()); mLayers.emplace_back(std::move(newLayer)); } } @@ -81,6 +80,7 @@ void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& dest continue; } RequestedLayerState& layer = it->second.owner; + LLOGV(layer.id, "%s", layer.getDebugString().c_str()); layer.handleAlive = false; if (!layer.canBeDestroyed()) { continue; @@ -148,7 +148,7 @@ void LayerLifecycleManager::onHandlesDestroyed(const std::vector<uint32_t>& dest while (it != mLayers.end()) { RequestedLayerState* layer = it->get(); if (layer->changes.test(RequestedLayerState::Changes::Destroyed)) { - LLOGV(layer->id, "destroyed layer %s", layer->getDebugStringShort().c_str()); + LLOGV(layer->id, "destroyed %s", layer->getDebugStringShort().c_str()); std::iter_swap(it, mLayers.end() - 1); mDestroyedLayers.emplace_back(std::move(mLayers.back())); if (it == mLayers.end() - 1) { @@ -166,7 +166,7 @@ void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState for (const auto& transaction : transactions) { for (const auto& resolvedComposerState : transaction.states) { const auto& clientState = resolvedComposerState.state; - uint32_t layerId = LayerHandle::getLayerId(clientState.surface); + uint32_t layerId = resolvedComposerState.layerId; if (layerId == UNASSIGNED_LAYER_ID) { ALOGW("%s Handle %p is not valid", __func__, clientState.surface.get()); continue; @@ -174,53 +174,57 @@ void LayerLifecycleManager::applyTransactions(const std::vector<TransactionState RequestedLayerState* layer = getLayerFromId(layerId); if (layer == nullptr) { - LOG_ALWAYS_FATAL("%s Layer with handle %p (layerid=%d) not found", __func__, - clientState.surface.get(), layerId); + LOG_ALWAYS_FATAL("%s Layer with layerid=%d not found", __func__, layerId); continue; } if (!layer->handleAlive) { - LOG_ALWAYS_FATAL("%s Layer's handle %p (layerid=%d) is not alive. Possible out of " + LOG_ALWAYS_FATAL("%s Layer's with layerid=%d) is not alive. Possible out of " "order LayerLifecycleManager updates", - __func__, clientState.surface.get(), layerId); + __func__, layerId); continue; } + if (transaction.flags & ISurfaceComposer::eAnimation) { + layer->changes |= RequestedLayerState::Changes::Animation; + } + 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, - {}}; + if (layer->bgColorLayerId == UNASSIGNED_LAYER_ID && layer->bgColor.a != 0) { + LayerCreationArgs backgroundLayerArgs(layer->id, + /*internalLayer=*/true); + backgroundLayerArgs.parentId = layer->id; + backgroundLayerArgs.name = layer->name + "BackgroundColorLayer"; + backgroundLayerArgs.flags = ISurfaceComposerClient::eFXSurfaceEffect; std::vector<std::unique_ptr<RequestedLayerState>> newLayers; newLayers.emplace_back( std::make_unique<RequestedLayerState>(backgroundLayerArgs)); RequestedLayerState* backgroundLayer = newLayers.back().get(); + backgroundLayer->bgColorLayer = true; 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->color = layer->bgColor; backgroundLayer->dataspace = layer->bgColorDataspace; - layer->bgColorLayerId = backgroundLayer->id; addLayers({std::move(newLayers)}); - } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && - layer->bgColorAlpha == 0) { + } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID && layer->bgColor.a == 0) { RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); - bgColorLayer->parentId = UNASSIGNED_LAYER_ID; - onHandlesDestroyed({layer->bgColorLayerId}); + layer->bgColorLayerId = UNASSIGNED_LAYER_ID; + bgColorLayer->parentId = unlinkLayer(bgColorLayer->parentId, bgColorLayer->id); + onHandlesDestroyed({bgColorLayer->id}); } else if (layer->bgColorLayerId != UNASSIGNED_LAYER_ID) { RequestedLayerState* bgColorLayer = getLayerFromId(layer->bgColorLayerId); - bgColorLayer->color.rgb = layer->color.rgb; - bgColorLayer->color.a = layer->bgColorAlpha; + bgColorLayer->color = layer->bgColor; bgColorLayer->dataspace = layer->bgColorDataspace; + bgColorLayer->what |= layer_state_t::eColorChanged | + layer_state_t::eDataspaceChanged | layer_state_t::eAlphaChanged; + bgColorLayer->changes |= RequestedLayerState::Changes::Content; mGlobalChanges |= RequestedLayerState::Changes::Content; } } @@ -256,8 +260,7 @@ void LayerLifecycleManager::commitChanges() { listener->onLayerAdded(*layer); } } - layer->what = 0; - layer->changes.clear(); + layer->clearChanges(); } for (auto& destroyedLayer : mDestroyedLayers) { diff --git a/services/surfaceflinger/FrontEnd/LayerLog.h b/services/surfaceflinger/FrontEnd/LayerLog.h index 47e1e30c48..4943483e62 100644 --- a/services/surfaceflinger/FrontEnd/LayerLog.h +++ b/services/surfaceflinger/FrontEnd/LayerLog.h @@ -25,3 +25,5 @@ #else #define LLOGV(LAYER_ID, x, ...) ALOGV("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__); #endif + +#define LLOGD(LAYER_ID, x, ...) ALOGD("[%d] %s " x, (LAYER_ID), __func__, ##__VA_ARGS__); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index dbb7fbf462..8a450933f0 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -27,14 +27,22 @@ using namespace ftl::flag_operators; LayerSnapshot::LayerSnapshot(const RequestedLayerState& state, const LayerHierarchy::TraversalPath& path) : path(path) { + static uint32_t sUniqueSequenceId = 0; + // Provide a unique id for clones otherwise keeping using the sequence id. + // The seq id can still be useful for debugging if its available. + uniqueSequence = (path.isClone()) ? sUniqueSequenceId++ : state.id; sequence = static_cast<int32_t>(state.id); name = state.name; textureName = state.textureName; premultipliedAlpha = state.premultipliedAlpha; inputInfo.name = state.name; - inputInfo.id = static_cast<int32_t>(state.id); + inputInfo.id = static_cast<int32_t>(uniqueSequence); inputInfo.ownerUid = static_cast<int32_t>(state.ownerUid); inputInfo.ownerPid = state.ownerPid; + changes = RequestedLayerState::Changes::Created; + mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror + ? path + : LayerHierarchy::TraversalPath::ROOT; } // As documented in libhardware header, formats in the range @@ -139,7 +147,8 @@ std::string LayerSnapshot::getIsVisibleReason() const { // visible std::stringstream reason; if (sidebandStream != nullptr) reason << " sidebandStream"; - if (externalTexture != nullptr) reason << " buffer"; + if (externalTexture != nullptr) + reason << " buffer:" << externalTexture->getId() << " frame:" << frameNumber; if (fillsColor() || color.a > 0.0f) reason << " color{" << color << "}"; if (drawShadows()) reason << " shadowSettings.length=" << shadowSettings.length; if (backgroundBlurRadius > 0) reason << " backgroundBlurRadius=" << backgroundBlurRadius; @@ -164,7 +173,8 @@ bool LayerSnapshot::hasInputInfo() const { std::string LayerSnapshot::getDebugString() const { std::stringstream debug; debug << "Snapshot{" << path.toString() << name << " isVisible=" << isVisible << " {" - << getIsVisibleReason() << "} changes=" << changes.string() << "}"; + << getIsVisibleReason() << "} changes=" << changes.string() + << " layerStack=" << outputFilter.layerStack.id << "}"; return debug.str(); } diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 5d74203af8..6fb2f57d96 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -57,6 +57,12 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { bool isHiddenByPolicyFromParent = false; bool isHiddenByPolicyFromRelativeParent = false; ftl::Flags<RequestedLayerState::Changes> changes; + // Some consumers of this snapshot (input, layer traces) rely on each snapshot to be unique. + // For mirrored layers, snapshots will have the same sequence so this unique id provides + // an alternative identifier when needed. + uint32_t uniqueSequence; + // Layer id used to create this snapshot. Multiple snapshots will have the same sequence if they + // generated from the same layer, for example when mirroring. int32_t sequence; std::string name; uint32_t textureName; @@ -84,6 +90,9 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { scheduler::LayerInfo::FrameRate frameRate; ui::Transform::RotationFlags fixedTransformHint; bool handleSkipScreenshotFlag = false; + int32_t frameRateSelectionPriority; + LayerHierarchy::TraversalPath mirrorRootPath; + bool unreachable = true; ChildState childState; static bool isOpaqueFormat(PixelFormat format); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index c9aeb24cf5..a16de1bcfb 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -274,7 +274,7 @@ void updateVisibility(LayerSnapshot& snapshot, bool visible) { // InputDispatcher, and obviously if they aren't visible they can't occlude // anything. const bool visibleForInput = - (snapshot.inputInfo.token != nullptr) ? snapshot.canReceiveInput() : snapshot.isVisible; + snapshot.hasInputInfo() ? snapshot.canReceiveInput() : snapshot.isVisible; snapshot.inputInfo.setInputConfig(gui::WindowInfo::InputConfig::NOT_VISIBLE, !visibleForInput); } @@ -296,6 +296,26 @@ bool needsInputInfo(const LayerSnapshot& snapshot, const RequestedLayerState& re gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL); } +void updateMetadata(LayerSnapshot& snapshot, const RequestedLayerState& requested, + const LayerSnapshotBuilder::Args& args) { + snapshot.metadata.clear(); + for (const auto& [key, mandatory] : args.supportedLayerGenericMetadata) { + auto compatIter = args.genericLayerMetadataKeyMap.find(key); + if (compatIter == std::end(args.genericLayerMetadataKeyMap)) { + continue; + } + const uint32_t id = compatIter->second; + auto it = requested.metadata.mMap.find(id); + if (it == std::end(requested.metadata.mMap)) { + continue; + } + + snapshot.metadata.emplace(key, + compositionengine::GenericLayerMetadataEntry{mandatory, + it->second}); + } +} + void clearChanges(LayerSnapshot& snapshot) { snapshot.changes.clear(); snapshot.contentDirty = false; @@ -308,6 +328,7 @@ void clearChanges(LayerSnapshot& snapshot) { LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() { LayerSnapshot snapshot; + snapshot.path = LayerHierarchy::TraversalPath::ROOT; snapshot.changes = ftl::Flags<RequestedLayerState::Changes>(); snapshot.isHiddenByPolicyFromParent = false; snapshot.isHiddenByPolicyFromRelativeParent = false; @@ -337,21 +358,17 @@ LayerSnapshot LayerSnapshotBuilder::getRootSnapshot() { LayerSnapshotBuilder::LayerSnapshotBuilder() : mRootSnapshot(getRootSnapshot()) {} LayerSnapshotBuilder::LayerSnapshotBuilder(Args args) : LayerSnapshotBuilder() { - args.forceUpdate = true; + args.forceUpdate = ForceUpdateFlags::ALL; updateSnapshots(args); } bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) { - if (args.forceUpdate || args.displayChanges) { + if (args.forceUpdate != ForceUpdateFlags::NONE || args.displayChanges) { // force update requested, or we have display changes, so skip the fast path return false; } if (args.layerLifecycleManager.getGlobalChanges().get() == 0) { - // there are no changes, so just clear the change flags from before. - for (auto& snapshot : mSnapshots) { - clearChanges(*snapshot); - } return true; } @@ -376,14 +393,12 @@ bool LayerSnapshotBuilder::tryFastUpdate(const Args& args) { // Walk through the snapshots, clearing previous change flags and updating the snapshots // if needed. for (auto& snapshot : mSnapshots) { - clearChanges(*snapshot); auto it = layersWithChanges.find(snapshot->path.id); if (it != layersWithChanges.end()) { ALOGV("%s fast path snapshot changes = %s", __func__, mRootSnapshot.changes.string().c_str()); LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT; - updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root, - /*newSnapshot=*/false); + updateSnapshot(*snapshot, args, *it->second, mRootSnapshot, root); } } return true; @@ -393,37 +408,45 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) { ATRACE_NAME("UpdateSnapshots"); if (args.parentCrop) { mRootSnapshot.geomLayerBounds = *args.parentCrop; - } else if (args.forceUpdate || args.displayChanges) { + } else if (args.forceUpdate == ForceUpdateFlags::ALL || args.displayChanges) { mRootSnapshot.geomLayerBounds = getMaxDisplayBounds(args.displays); } if (args.displayChanges) { mRootSnapshot.changes = RequestedLayerState::Changes::AffectsChildren | RequestedLayerState::Changes::Geometry; } + if (args.forceUpdate == ForceUpdateFlags::HIERARCHY) { + mRootSnapshot.changes |= + RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility; + } LayerHierarchy::TraversalPath root = LayerHierarchy::TraversalPath::ROOT; - for (auto& [childHierarchy, variant] : args.root.mChildren) { - LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, - childHierarchy->getLayer()->id, - variant); - updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot); + if (args.root.getLayer()) { + // The hierarchy can have a root layer when used for screenshots otherwise, it will have + // multiple children. + LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, args.root.getLayer()->id, + LayerHierarchy::Variant::Attached); + updateSnapshotsInHierarchy(args, args.root, root, mRootSnapshot); + } else { + for (auto& [childHierarchy, variant] : args.root.mChildren) { + LayerHierarchy::ScopedAddToTraversalPath addChildToPath(root, + childHierarchy->getLayer()->id, + variant); + updateSnapshotsInHierarchy(args, *childHierarchy, root, mRootSnapshot); + } } - sortSnapshotsByZ(args); + const bool hasUnreachableSnapshots = sortSnapshotsByZ(args); clearChanges(mRootSnapshot); // Destroy unreachable snapshots - if (args.layerLifecycleManager.getDestroyedLayers().empty()) { + if (!hasUnreachableSnapshots) { return; } - std::unordered_set<uint32_t> destroyedLayerIds; - for (auto& destroyedLayer : args.layerLifecycleManager.getDestroyedLayers()) { - destroyedLayerIds.emplace(destroyedLayer->id); - } auto it = mSnapshots.begin(); while (it < mSnapshots.end()) { auto& traversalPath = it->get()->path; - if (destroyedLayerIds.find(traversalPath.id) == destroyedLayerIds.end()) { + if (!it->get()->unreachable) { it++; continue; } @@ -436,6 +459,10 @@ void LayerSnapshotBuilder::updateSnapshots(const Args& args) { } void LayerSnapshotBuilder::update(const Args& args) { + for (auto& snapshot : mSnapshots) { + clearChanges(*snapshot); + } + if (tryFastUpdate(args)) { return; } @@ -449,8 +476,9 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( LayerSnapshot* snapshot = getSnapshot(traversalPath); const bool newSnapshot = snapshot == nullptr; if (newSnapshot) { - snapshot = createSnapshot(traversalPath, *layer); + snapshot = createSnapshot(traversalPath, *layer, parentSnapshot); } + scheduler::LayerInfo::FrameRate oldFrameRate = snapshot->frameRate; if (traversalPath.isRelative()) { bool parentIsRelative = traversalPath.variant == LayerHierarchy::Variant::Relative; updateRelativeState(*snapshot, parentSnapshot, parentIsRelative, args); @@ -458,7 +486,7 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( if (traversalPath.isAttached()) { resetRelativeState(*snapshot); } - updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath, newSnapshot); + updateSnapshot(*snapshot, args, *layer, parentSnapshot, traversalPath); } for (auto& [childHierarchy, variant] : hierarchy.mChildren) { @@ -469,6 +497,10 @@ const LayerSnapshot& LayerSnapshotBuilder::updateSnapshotsInHierarchy( updateSnapshotsInHierarchy(args, *childHierarchy, traversalPath, *snapshot); updateChildState(*snapshot, childSnapshot, args); } + + if (oldFrameRate == snapshot->frameRate) { + snapshot->changes.clear(RequestedLayerState::Changes::FrameRate); + } return *snapshot; } @@ -485,27 +517,34 @@ LayerSnapshot* LayerSnapshotBuilder::getSnapshot(const LayerHierarchy::Traversal return it == mIdToSnapshot.end() ? nullptr : it->second; } -LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& id, - const RequestedLayerState& layer) { - mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, id)); +LayerSnapshot* LayerSnapshotBuilder::createSnapshot(const LayerHierarchy::TraversalPath& path, + const RequestedLayerState& layer, + const LayerSnapshot& parentSnapshot) { + mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, path)); LayerSnapshot* snapshot = mSnapshots.back().get(); snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1; - mIdToSnapshot[id] = snapshot; + if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) { + snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath; + } + mIdToSnapshot[path] = snapshot; return snapshot; } -void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { - if (!mResortSnapshots && !args.forceUpdate && +bool LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { + if (!mResortSnapshots && args.forceUpdate == ForceUpdateFlags::NONE && !args.layerLifecycleManager.getGlobalChanges().any( RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Visibility)) { // We are not force updating and there are no hierarchy or visibility changes. Avoid sorting // the snapshots. - return; + return false; } - mResortSnapshots = false; + for (auto& snapshot : mSnapshots) { + snapshot->unreachable = true; + } + size_t globalZ = 0; args.root.traverseInZOrder( [this, &globalZ](const LayerHierarchy&, @@ -515,11 +554,7 @@ void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { return false; } - if (snapshot->isHiddenByPolicy() && - !snapshot->changes.test(RequestedLayerState::Changes::Visibility)) { - return false; - } - + snapshot->unreachable = false; if (snapshot->getIsVisible() || snapshot->hasInputInfo()) { updateVisibility(*snapshot, snapshot->getIsVisible()); size_t oldZ = snapshot->globalZ; @@ -537,12 +572,17 @@ void LayerSnapshotBuilder::sortSnapshotsByZ(const Args& args) { return true; }); mNumInterestingSnapshots = (int)globalZ; + bool hasUnreachableSnapshots = false; while (globalZ < mSnapshots.size()) { mSnapshots[globalZ]->globalZ = globalZ; /* mark unreachable snapshots as explicitly invisible */ updateVisibility(*mSnapshots[globalZ], false); + if (mSnapshots[globalZ]->unreachable) { + hasUnreachableSnapshots = true; + } globalZ++; } + return hasUnreachableSnapshots; } void LayerSnapshotBuilder::updateRelativeState(LayerSnapshot& snapshot, @@ -569,7 +609,8 @@ void LayerSnapshotBuilder::updateChildState(LayerSnapshot& snapshot, if (snapshot.childState.hasValidFrameRate) { return; } - if (args.forceUpdate || childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) { + if (args.forceUpdate == ForceUpdateFlags::ALL || + childSnapshot.changes.test(RequestedLayerState::Changes::FrameRate)) { // 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 // RefreshRateSelector::rankFrameRates for details. @@ -612,35 +653,32 @@ uint32_t getDisplayRotationFlags( void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& args, const RequestedLayerState& requested, const LayerSnapshot& parentSnapshot, - const LayerHierarchy::TraversalPath& path, - bool newSnapshot) { + const LayerHierarchy::TraversalPath& path) { // Always update flags and visibility ftl::Flags<RequestedLayerState::Changes> parentChanges = parentSnapshot.changes & (RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry | RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Metadata | - RequestedLayerState::Changes::AffectsChildren); - snapshot.changes = parentChanges | requested.changes; + RequestedLayerState::Changes::AffectsChildren | + RequestedLayerState::Changes::FrameRate); + snapshot.changes |= parentChanges | requested.changes; snapshot.isHiddenByPolicyFromParent = parentSnapshot.isHiddenByPolicyFromParent || parentSnapshot.invalidTransform || requested.isHiddenByPolicy() || (args.excludeLayerIds.find(path.id) != args.excludeLayerIds.end()); snapshot.contentDirty = requested.what & layer_state_t::CONTENT_DIRTY; // TODO(b/238781169) scope down the changes to only buffer updates. - snapshot.hasReadyFrame = - (snapshot.contentDirty || requested.autoRefresh) && (requested.externalTexture); - // TODO(b/238781169) how is this used? ag/15523870 - snapshot.sidebandStreamHasFrame = false; + snapshot.hasReadyFrame = requested.hasReadyFrame(); + snapshot.sidebandStreamHasFrame = requested.hasSidebandStreamFrame(); updateSurfaceDamage(requested, snapshot.hasReadyFrame, args.forceFullDamage, snapshot.surfaceDamage); - - const bool forceUpdate = newSnapshot || args.forceUpdate || - snapshot.changes.any(RequestedLayerState::Changes::Visibility | - RequestedLayerState::Changes::Created); - snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID - ? parentSnapshot.outputFilter.layerStack - : requested.layerStack; + snapshot.outputFilter.layerStack = parentSnapshot.path == LayerHierarchy::TraversalPath::ROOT + ? requested.layerStack + : parentSnapshot.outputFilter.layerStack; uint32_t displayRotationFlags = getDisplayRotationFlags(args.displays, snapshot.outputFilter.layerStack); + const bool forceUpdate = args.forceUpdate == ForceUpdateFlags::ALL || + snapshot.changes.any(RequestedLayerState::Changes::Visibility | + RequestedLayerState::Changes::Created); // always update the buffer regardless of visibility if (forceUpdate || requested.what & layer_state_t::BUFFER_CHANGES || args.displayChanges) { @@ -673,7 +711,8 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a snapshot.desiredSdrHdrRatio = requested.desiredSdrHdrRatio; } - if (snapshot.isHiddenByPolicyFromParent && !newSnapshot) { + if (snapshot.isHiddenByPolicyFromParent && + !snapshot.changes.test(RequestedLayerState::Changes::Created)) { if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Hierarchy | RequestedLayerState::Changes::Geometry | @@ -690,9 +729,6 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a snapshot.isSecure = parentSnapshot.isSecure || (requested.flags & layer_state_t::eLayerSecure); snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay; - snapshot.outputFilter.layerStack = requested.parentId != UNASSIGNED_LAYER_ID - ? parentSnapshot.outputFilter.layerStack - : requested.layerStack; snapshot.outputFilter.toInternalDisplay = parentSnapshot.outputFilter.toInternalDisplay || (requested.flags & layer_state_t::eLayerSkipScreenshot); snapshot.stretchEffect = (requested.stretchEffect.hasEffect()) @@ -708,11 +744,6 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a snapshot.gameMode = requested.metadata.has(gui::METADATA_GAME_MODE) ? requested.gameMode : parentSnapshot.gameMode; - snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() || - (requested.requestedFrameRate.type == - scheduler::LayerInfo::FrameRateCompatibility::NoVote)) - ? requested.requestedFrameRate - : parentSnapshot.frameRate; snapshot.fixedTransformHint = requested.fixedTransformHint != ui::Transform::ROT_INVALID ? requested.fixedTransformHint : parentSnapshot.fixedTransformHint; @@ -722,11 +753,27 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a (requested.layerStackToMirror != ui::INVALID_LAYER_STACK); } + if (forceUpdate || + snapshot.changes.any(RequestedLayerState::Changes::FrameRate | + RequestedLayerState::Changes::Hierarchy)) { + snapshot.frameRate = (requested.requestedFrameRate.rate.isValid() || + (requested.requestedFrameRate.type == + scheduler::LayerInfo::FrameRateCompatibility::NoVote)) + ? requested.requestedFrameRate + : parentSnapshot.frameRate; + } + + if (forceUpdate || requested.what & layer_state_t::eMetadataChanged) { + updateMetadata(snapshot, requested, args); + } + if (forceUpdate || requested.changes.get() != 0) { snapshot.compositionType = requested.getCompositionType(); snapshot.dimmingEnabled = requested.dimmingEnabled; snapshot.layerOpaqueFlagSet = (requested.flags & layer_state_t::eLayerOpaque) == layer_state_t::eLayerOpaque; + snapshot.cachingHint = requested.cachingHint; + snapshot.frameRateSelectionPriority = requested.frameRateSelectionPriority; } if (forceUpdate || snapshot.changes.any(RequestedLayerState::Changes::Content)) { @@ -764,18 +811,16 @@ void LayerSnapshotBuilder::updateSnapshot(LayerSnapshot& snapshot, const Args& a } snapshot.forceClientComposition = snapshot.isHdrY410 || snapshot.shadowSettings.length > 0 || requested.blurRegions.size() > 0 || snapshot.stretchEffect.hasEffect(); - snapshot.isOpaque = snapshot.isContentOpaque() && !snapshot.roundedCorner.hasRoundedCorners() && + snapshot.contentOpaque = snapshot.isContentOpaque(); + snapshot.isOpaque = snapshot.contentOpaque && !snapshot.roundedCorner.hasRoundedCorners() && snapshot.color.a == 1.f; snapshot.blendMode = getBlendMode(snapshot, requested); - // TODO(b/238781169) pass this from flinger - // snapshot.fps; - // snapshot.metadata; LLOGV(snapshot.sequence, - "%supdated [%d]%s changes parent:%s global:%s local:%s requested:%s %s from parent %s", - args.forceUpdate ? "Force " : "", requested.id, requested.name.c_str(), - parentSnapshot.changes.string().c_str(), snapshot.changes.string().c_str(), - requested.changes.string().c_str(), std::to_string(requested.what).c_str(), - snapshot.getDebugString().c_str(), parentSnapshot.getDebugString().c_str()); + "%supdated %s changes:%s parent:%s requested:%s requested:%s from parent %s", + args.forceUpdate == ForceUpdateFlags::ALL ? "Force " : "", + snapshot.getDebugString().c_str(), snapshot.changes.string().c_str(), + parentSnapshot.changes.string().c_str(), requested.changes.string().c_str(), + std::to_string(requested.what).c_str(), parentSnapshot.getDebugString().c_str()); } void LayerSnapshotBuilder::updateRoundedCorner(LayerSnapshot& snapshot, @@ -910,9 +955,14 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo = *requested.windowInfoHandle->getInfo(); } else { snapshot.inputInfo = {}; + // b/271132344 revisit this and see if we can always use the layers uid/pid + snapshot.inputInfo.name = requested.name; + snapshot.inputInfo.ownerUid = static_cast<int32_t>(requested.ownerUid); + snapshot.inputInfo.ownerPid = requested.ownerPid; } - snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id); + snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence); + snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id); if (!needsInputInfo(snapshot, requested)) { return; } @@ -935,7 +985,9 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, } snapshot.inputInfo.alpha = snapshot.color.a; - snapshot.inputInfo.touchOcclusionMode = parentSnapshot.inputInfo.touchOcclusionMode; + snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo() + ? requested.windowInfoHandle->getInfo()->touchOcclusionMode + : parentSnapshot.inputInfo.touchOcclusionMode; if (requested.dropInputMode == gui::DropInputMode::ALL || parentSnapshot.dropInputMode == gui::DropInputMode::ALL) { snapshot.dropInputMode = gui::DropInputMode::ALL; @@ -974,7 +1026,7 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, // touches from going outside the cloned area. if (path.isClone()) { snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE; - auto clonedRootSnapshot = getSnapshot(path.getMirrorRoot()); + auto clonedRootSnapshot = getSnapshot(snapshot.mirrorRootPath); if (clonedRootSnapshot) { const Rect rect = displayInfo.transform.transform(Rect{clonedRootSnapshot->transformedBounds}); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h index 0902ab8067..3997a0ad83 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.h @@ -35,10 +35,15 @@ namespace android::surfaceflinger::frontend { // snapshots when there are only buffer updates. class LayerSnapshotBuilder { public: + enum class ForceUpdateFlags { + NONE, + ALL, + HIERARCHY, + }; struct Args { LayerHierarchy root; const LayerLifecycleManager& layerLifecycleManager; - bool forceUpdate = false; + ForceUpdateFlags forceUpdate = ForceUpdateFlags::NONE; bool includeMetadata = false; const display::DisplayMap<ui::LayerStack, frontend::DisplayInfo>& displays; // Set to true if there were display changes since last update. @@ -48,6 +53,8 @@ public: bool forceFullDamage = false; std::optional<FloatRect> parentCrop = std::nullopt; std::unordered_set<uint32_t> excludeLayerIds; + const std::unordered_map<std::string, bool>& supportedLayerGenericMetadata; + const std::unordered_map<std::string, uint32_t>& genericLayerMetadataKeyMap; }; LayerSnapshotBuilder(); @@ -92,8 +99,7 @@ private: LayerHierarchy::TraversalPath& traversalPath, const LayerSnapshot& parentSnapshot); void updateSnapshot(LayerSnapshot&, const Args&, const RequestedLayerState&, - const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&, - bool newSnapshot); + const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath&); static void updateRelativeState(LayerSnapshot& snapshot, const LayerSnapshot& parentSnapshot, bool parentIsRelative, const Args& args); static void resetRelativeState(LayerSnapshot& snapshot); @@ -106,9 +112,11 @@ private: void updateInput(LayerSnapshot& snapshot, const RequestedLayerState& requested, const LayerSnapshot& parentSnapshot, const LayerHierarchy::TraversalPath& path, const Args& args); - void sortSnapshotsByZ(const Args& args); + // Return true if there are unreachable snapshots + bool sortSnapshotsByZ(const Args& args); LayerSnapshot* createSnapshot(const LayerHierarchy::TraversalPath& id, - const RequestedLayerState& layer); + const RequestedLayerState& layer, + const LayerSnapshot& parentSnapshot); void updateChildState(LayerSnapshot& snapshot, const LayerSnapshot& childSnapshot, const Args& args); diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index 28340844a3..a5fdaf4fe3 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -24,7 +24,6 @@ #include "Layer.h" #include "LayerCreationArgs.h" -#include "LayerHandle.h" #include "LayerLog.h" #include "RequestedLayerState.h" @@ -33,14 +32,6 @@ 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); } @@ -64,17 +55,17 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) layerCreationFlags(args.flags), textureName(args.textureName), ownerUid(args.ownerUid), - ownerPid(args.ownerPid) { + ownerPid(args.ownerPid), + parentId(args.parentId), + layerIdToMirror(args.layerIdToMirror) { 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()); - if (args.parentHandle != nullptr) { + if (parentId != UNASSIGNED_LAYER_ID) { canBeRoot = false; } - layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); if (layerIdToMirror != UNASSIGNED_LAYER_ID) { changes |= RequestedLayerState::Changes::Mirror; } else if (args.layerStackToMirror != ui::INVALID_LAYER_STACK) { @@ -137,6 +128,7 @@ RequestedLayerState::RequestedLayerState(const LayerCreationArgs& args) dataspace = ui::Dataspace::V0_SRGB; gameMode = gui::GameMode::Unsupported; requestedFrameRate = {}; + cachingHint = gui::CachingHint::Enabled; } void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerState) { @@ -164,8 +156,13 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta if (hadBufferOrSideStream != hasBufferOrSideStream) { changes |= RequestedLayerState::Changes::Geometry | RequestedLayerState::Changes::VisibleRegion | - RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input | - RequestedLayerState::Changes::Buffer; + RequestedLayerState::Changes::Visibility | RequestedLayerState::Changes::Input; + } + if (clientState.what & layer_state_t::eBufferChanged) { + changes |= RequestedLayerState::Changes::Buffer; + } + if (clientState.what & layer_state_t::eSidebandStreamChanged) { + changes |= RequestedLayerState::Changes::SidebandStream; } } if (what & (layer_state_t::eAlphaChanged)) { @@ -196,12 +193,14 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta static const mat4 identityMatrix = mat4(); hasColorTransform = colorTransform != identityMatrix; } - if (clientState.what & (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged)) { + if (clientState.what & + (layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged | + layer_state_t::eLayerStackChanged)) { changes |= RequestedLayerState::Changes::Z; } if (clientState.what & layer_state_t::eReparent) { changes |= RequestedLayerState::Changes::Parent; - parentId = getLayerIdFromSurfaceControl(clientState.parentSurfaceControlForChild); + parentId = resolvedComposerState.parentId; 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 @@ -210,7 +209,7 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta } if (clientState.what & layer_state_t::eRelativeLayerChanged) { changes |= RequestedLayerState::Changes::RelativeParent; - relativeParentId = getLayerIdFromSurfaceControl(clientState.relativeLayerSurfaceControl); + relativeParentId = resolvedComposerState.relativeParentId; isRelativeOf = true; relativeLayerSurfaceControl = nullptr; } @@ -227,10 +226,8 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta changes |= RequestedLayerState::Changes::RelativeParent; } if (clientState.what & layer_state_t::eInputInfoChanged) { - wp<IBinder>& touchableRegionCropHandle = - windowInfoHandle->editInfo()->touchableRegionCropHandle; - touchCropId = LayerHandle::getLayerId(touchableRegionCropHandle.promote()); - touchableRegionCropHandle.clear(); + touchCropId = resolvedComposerState.touchCropId; + windowInfoHandle->editInfo()->touchableRegionCropHandle.clear(); } if (clientState.what & layer_state_t::eStretchChanged) { stretchEffect.sanitize(); @@ -452,4 +449,22 @@ bool RequestedLayerState::hasBlur() const { return backgroundBlurRadius > 0 || blurRegions.size() > 0; } +bool RequestedLayerState::hasFrameUpdate() const { + return what & layer_state_t::CONTENT_DIRTY && + (externalTexture || bgColorLayerId != UNASSIGNED_LAYER_ID); +} + +bool RequestedLayerState::hasReadyFrame() const { + return hasFrameUpdate() || changes.test(Changes::SidebandStream) || autoRefresh; +} + +bool RequestedLayerState::hasSidebandStreamFrame() const { + return hasFrameUpdate() && sidebandStream.get(); +} + +void RequestedLayerState::clearChanges() { + what = 0; + changes.clear(); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h index 6840b25a5b..216e95f414 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h @@ -52,10 +52,14 @@ struct RequestedLayerState : layer_state_t { FrameRate = 1u << 13, VisibleRegion = 1u << 14, Buffer = 1u << 15, + SidebandStream = 1u << 16, + Animation = 1u << 17, }; static Rect reduce(const Rect& win, const Region& exclude); RequestedLayerState(const LayerCreationArgs&); void merge(const ResolvedComposerState&); + void clearChanges(); + // Currently we only care about the primary display ui::Transform getTransform(uint32_t displayRotationFlags) const; ui::Size getUnrotatedBufferSize(uint32_t displayRotationFlags) const; @@ -72,6 +76,9 @@ struct RequestedLayerState : layer_state_t { bool hasValidRelativeParent() const; bool hasInputInfo() const; bool hasBlur() const; + bool hasFrameUpdate() const; + bool hasReadyFrame() const; + bool hasSidebandStreamFrame() 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 @@ -98,18 +105,19 @@ struct RequestedLayerState : layer_state_t { std::shared_ptr<renderengine::ExternalTexture> externalTexture; gui::GameMode gameMode; scheduler::LayerInfo::FrameRate requestedFrameRate; - ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK; + uint32_t parentId = UNASSIGNED_LAYER_ID; + uint32_t relativeParentId = UNASSIGNED_LAYER_ID; uint32_t layerIdToMirror = UNASSIGNED_LAYER_ID; + ui::LayerStack layerStackToMirror = ui::INVALID_LAYER_STACK; + uint32_t touchCropId = UNASSIGNED_LAYER_ID; + uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID; // book keeping states bool handleAlive = true; bool isRelativeOf = false; - uint32_t parentId = UNASSIGNED_LAYER_ID; - uint32_t relativeParentId = UNASSIGNED_LAYER_ID; std::vector<uint32_t> mirrorIds{}; - uint32_t touchCropId = UNASSIGNED_LAYER_ID; - uint32_t bgColorLayerId = UNASSIGNED_LAYER_ID; ftl::Flags<RequestedLayerState::Changes> changes; + bool bgColorLayer = false; }; } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 084d9b93db..0f2af2f809 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -196,7 +196,8 @@ Layer::Layer(const LayerCreationArgs& args) mDrawingState.color.b = -1.0_hf; } - mFrameTracker.setDisplayRefreshPeriod(args.flinger->mScheduler->getLeaderVsyncPeriod().ns()); + mFrameTracker.setDisplayRefreshPeriod( + args.flinger->mScheduler->getPacesetterVsyncPeriod().ns()); mOwnerUid = args.ownerUid; mOwnerPid = args.ownerPid; @@ -566,6 +567,7 @@ void Layer::prepareBasicGeometryCompositionState() { : Hwc2::IComposerClient::BlendMode::COVERAGE; } + // Please keep in sync with LayerSnapshotBuilder auto* snapshot = editLayerSnapshot(); snapshot->outputFilter = getOutputFilter(); snapshot->isVisible = isVisible(); @@ -592,6 +594,7 @@ void Layer::prepareGeometryCompositionState() { const auto& drawingState{getDrawingState()}; auto* snapshot = editLayerSnapshot(); + // Please keep in sync with LayerSnapshotBuilder snapshot->geomBufferSize = getBufferSize(drawingState); snapshot->geomContentCrop = getBufferCrop(); snapshot->geomCrop = getCrop(drawingState); @@ -624,6 +627,7 @@ void Layer::prepareGeometryCompositionState() { void Layer::preparePerFrameCompositionState() { const auto& drawingState{getDrawingState()}; + // Please keep in sync with LayerSnapshotBuilder auto* snapshot = editLayerSnapshot(); snapshot->forceClientComposition = false; @@ -637,6 +641,7 @@ void Layer::preparePerFrameCompositionState() { snapshot->dimmingEnabled = isDimmingEnabled(); snapshot->currentSdrHdrRatio = getCurrentSdrHdrRatio(); snapshot->desiredSdrHdrRatio = getDesiredSdrHdrRatio(); + snapshot->cachingHint = getCachingHint(); const bool usesRoundedCorners = hasRoundedCorners(); @@ -666,8 +671,9 @@ void Layer::preparePerFrameCompositionState() { } void Layer::preparePerFrameBufferCompositionState() { - // Sideband layers + // Please keep in sync with LayerSnapshotBuilder auto* snapshot = editLayerSnapshot(); + // Sideband layers if (snapshot->sidebandStream.get() && !snapshot->sidebandStreamHasFrame) { snapshot->compositionType = aidl::android::hardware::graphics::composer3::Composition::SIDEBAND; @@ -675,6 +681,9 @@ void Layer::preparePerFrameBufferCompositionState() { } else if ((mDrawingState.flags & layer_state_t::eLayerIsDisplayDecoration) != 0) { snapshot->compositionType = aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + } else if ((mDrawingState.flags & layer_state_t::eLayerIsRefreshRateIndicator) != 0) { + snapshot->compositionType = + aidl::android::hardware::graphics::composer3::Composition::REFRESH_RATE_INDICATOR; } else { // Normal buffer layers snapshot->hdrMetadata = mBufferInfo.mHdrMetadata; @@ -690,6 +699,7 @@ void Layer::preparePerFrameBufferCompositionState() { } void Layer::preparePerFrameEffectsCompositionState() { + // Please keep in sync with LayerSnapshotBuilder auto* snapshot = editLayerSnapshot(); snapshot->color = getColor(); snapshot->compositionType = @@ -698,6 +708,7 @@ void Layer::preparePerFrameEffectsCompositionState() { void Layer::prepareCursorCompositionState() { const State& drawingState{getDrawingState()}; + // Please keep in sync with LayerSnapshotBuilder auto* snapshot = editLayerSnapshot(); // Apply the layer's transform, followed by the display's global transform @@ -783,7 +794,7 @@ void Layer::transferAvailableJankData(const std::deque<sp<CallbackHandle>>& hand // transaction // ---------------------------------------------------------------------------- -uint32_t Layer::doTransaction(uint32_t flags, nsecs_t latchTime) { +uint32_t Layer::doTransaction(uint32_t flags) { ATRACE_CALL(); // TODO: This is unfortunate. @@ -811,12 +822,12 @@ uint32_t Layer::doTransaction(uint32_t flags, nsecs_t latchTime) { mFlinger->mUpdateInputInfo = true; } - commitTransaction(mDrawingState, latchTime); + commitTransaction(mDrawingState); return flags; } -void Layer::commitTransaction(State&, nsecs_t currentLatchTime) { +void Layer::commitTransaction(State&) { // Set the present state for all bufferlessSurfaceFramesTX to Presented. The // bufferSurfaceFrameTX will be presented in latchBuffer. for (auto& [token, surfaceFrame] : mDrawingState.bufferlessSurfaceFramesTX) { @@ -828,7 +839,6 @@ void Layer::commitTransaction(State&, nsecs_t currentLatchTime) { } } mDrawingState.bufferlessSurfaceFramesTX.clear(); - mLastLatchTime = currentLatchTime; } uint32_t Layer::clearTransactionFlags(uint32_t mask) { @@ -1247,7 +1257,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran return parentFrameRate; }(); - *transactionNeeded |= setFrameRateForLayerTree(frameRate); + *transactionNeeded |= setFrameRateForLayerTreeLegacy(frameRate); // The frame rate is propagated to the children bool childrenHaveFrameRate = false; @@ -1261,7 +1271,7 @@ bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* tran if (!frameRate.rate.isValid() && frameRate.type != FrameRateCompatibility::NoVote && childrenHaveFrameRate) { *transactionNeeded |= - setFrameRateForLayerTree(FrameRate(Fps(), FrameRateCompatibility::NoVote)); + setFrameRateForLayerTreeLegacy(FrameRate(Fps(), FrameRateCompatibility::NoVote)); } // We return whether this layer ot its children has a vote. We ignore ExactOrMultiple votes for @@ -1374,7 +1384,7 @@ void Layer::addSurfaceFramePresentedForBuffer( surfaceFrame->setAcquireFenceTime(acquireFenceTime); surfaceFrame->setPresentState(PresentState::Presented, mLastLatchTime); mFlinger->mFrameTimeline->addSurfaceFrame(surfaceFrame); - mLastLatchTime = currentLatchTime; + updateLastLatchTime(currentLatchTime); } std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForTransaction( @@ -1413,7 +1423,7 @@ std::shared_ptr<frametimeline::SurfaceFrame> Layer::createSurfaceFrameForBuffer( return surfaceFrame; } -bool Layer::setFrameRateForLayerTree(FrameRate frameRate) { +bool Layer::setFrameRateForLayerTreeLegacy(FrameRate frameRate) { if (mDrawingState.frameRateForLayerTree == frameRate) { return false; } @@ -1426,9 +1436,21 @@ bool Layer::setFrameRateForLayerTree(FrameRate frameRate) { mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); - using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType; - mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate); + mFlinger->mScheduler + ->recordLayerHistory(sequence, getLayerProps(), systemTime(), + scheduler::LayerHistory::LayerUpdateType::SetFrameRate); + return true; +} +bool Layer::setFrameRateForLayerTree(FrameRate frameRate, const scheduler::LayerProps& layerProps) { + if (mDrawingState.frameRateForLayerTree == frameRate) { + return false; + } + + mDrawingState.frameRateForLayerTree = frameRate; + mFlinger->mScheduler + ->recordLayerHistory(sequence, layerProps, systemTime(), + scheduler::LayerHistory::LayerUpdateType::SetFrameRate); return true; } @@ -2090,15 +2112,7 @@ LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) { writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags); if (traceFlags & LayerTracing::TRACE_COMPOSITION) { - ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread. - - // Only populate for the primary display. - if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) { - const auto compositionType = getCompositionType(*display); - layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType)); - LayerProtoHelper::writeToProto(getVisibleRegion(display.get()), - [&]() { return layerProto->mutable_visible_region(); }); - } + writeCompositionStateToProto(layerProto); } for (const sp<Layer>& layer : mDrawingChildren) { @@ -2108,6 +2122,18 @@ LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) { return layerProto; } +void Layer::writeCompositionStateToProto(LayerProto* layerProto) { + ftl::FakeGuard guard(mFlinger->mStateLock); // Called from the main thread. + + // Only populate for the primary display. + if (const auto display = mFlinger->getDefaultDisplayDeviceLocked()) { + const auto compositionType = getCompositionType(*display); + layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType)); + LayerProtoHelper::writeToProto(getVisibleRegion(display.get()), + [&]() { return layerProto->mutable_visible_region(); }); + } +} + void Layer::writeToProtoDrawingState(LayerProto* layerInfo) { const ui::Transform transform = getTransform(); auto buffer = getExternalTexture(); @@ -2505,7 +2531,20 @@ bool Layer::hasInputInfo() const { compositionengine::OutputLayer* Layer::findOutputLayerForDisplay( const DisplayDevice* display) const { if (!display) return nullptr; - return display->getCompositionDisplay()->getOutputLayerForLayer(getCompositionEngineLayerFE()); + if (!mFlinger->mLayerLifecycleManagerEnabled) { + return display->getCompositionDisplay()->getOutputLayerForLayer( + getCompositionEngineLayerFE()); + } + sp<LayerFE> layerFE; + frontend::LayerHierarchy::TraversalPath path{.id = static_cast<uint32_t>(sequence)}; + for (auto& [p, layer] : mLayerFEs) { + if (p == path) { + layerFE = layer; + } + } + + if (!layerFE) return nullptr; + return display->getCompositionDisplay()->getOutputLayerForLayer(layerFE); } Region Layer::getVisibleRegion(const DisplayDevice* display) const { @@ -3014,6 +3053,10 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, mLastClientCompositionFence); mLastClientCompositionFence = nullptr; } + } else { + // if we are latching a buffer for the first time then clear the mLastLatchTime since + // we don't want to incorrectly classify a frame if we miss the desired present time. + updateLastLatchTime(0); } mDrawingState.producerId = bufferData.producerId; @@ -3033,7 +3076,7 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, } else { mCallbackHandleAcquireTimeOrFence = mDrawingState.acquireFenceTime->getSignalTime(); } - + mDrawingState.latchedVsyncId = info.vsyncId; mDrawingState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -3043,18 +3086,9 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, 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); + if (mFlinger->mLegacyFrontEndEnabled) { + recordLayerHistoryBufferUpdate(getLayerProps()); + } setFrameTimelineVsyncForBufferTransaction(info, postTime); @@ -3071,6 +3105,32 @@ bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer, return true; } +void Layer::setDesiredPresentTime(nsecs_t desiredPresentTime, bool isAutoTimestamp) { + mDrawingState.desiredPresentTime = desiredPresentTime; + mDrawingState.isAutoTimestamp = isAutoTimestamp; +} + +void Layer::recordLayerHistoryBufferUpdate(const scheduler::LayerProps& layerProps) { + const nsecs_t presentTime = [&] { + if (!mDrawingState.isAutoTimestamp) return mDrawingState.desiredPresentTime; + + const auto prediction = mFlinger->mFrameTimeline->getTokenManager()->getPredictionsForToken( + mDrawingState.latchedVsyncId); + if (prediction.has_value()) return prediction->presentTime; + + return static_cast<nsecs_t>(0); + }(); + mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, + scheduler::LayerHistory::LayerUpdateType::Buffer); +} + +void Layer::recordLayerHistoryAnimationTx(const scheduler::LayerProps& layerProps) { + const nsecs_t presentTime = + mDrawingState.isAutoTimestamp ? 0 : mDrawingState.desiredPresentTime; + mFlinger->mScheduler->recordLayerHistory(sequence, layerProps, presentTime, + scheduler::LayerHistory::LayerUpdateType::AnimationTX); +} + bool Layer::setDataspace(ui::Dataspace dataspace) { mDrawingState.dataspaceRequested = true; if (mDrawingState.dataspace == dataspace) return false; @@ -3091,6 +3151,14 @@ bool Layer::setExtendedRangeBrightness(float currentBufferRatio, float desiredRa return true; } +bool Layer::setCachingHint(gui::CachingHint cachingHint) { + if (mDrawingState.cachingHint == cachingHint) return false; + mDrawingState.cachingHint = cachingHint; + mDrawingState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + bool Layer::setHdrMetadata(const HdrMetadata& hdrMetadata) { if (mDrawingState.hdrMetadata == hdrMetadata) return false; mDrawingState.hdrMetadata = hdrMetadata; @@ -3260,11 +3328,11 @@ bool Layer::hasFrameUpdate() const { (c.buffer != nullptr || c.bgColorLayer != nullptr); } -void Layer::updateTexImage(nsecs_t latchTime) { +void Layer::updateTexImage(nsecs_t latchTime, bool bgColorOnly) { const State& s(getDrawingState()); if (!s.buffer) { - if (s.bgColorLayer) { + if (bgColorOnly) { for (auto& handle : mDrawingState.callbackHandles) { handle->latchTime = latchTime; } @@ -3461,7 +3529,7 @@ bool Layer::simpleBufferUpdate(const layer_state_t& s) const { } if (s.what & layer_state_t::eBackgroundColorChanged) { - if (mDrawingState.bgColorLayer || s.bgColorAlpha != 0) { + if (mDrawingState.bgColorLayer || s.bgColor.a != 0) { ALOGV("%s: false [eBackgroundColorChanged changed]", __func__); return false; } @@ -3785,6 +3853,11 @@ void Layer::onPostComposition(const DisplayDevice* display, } bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { + const bool bgColorOnly = mDrawingState.bgColorLayer != nullptr; + return latchBufferImpl(recomputeVisibleRegions, latchTime, bgColorOnly); +} + +bool Layer::latchBufferImpl(bool& recomputeVisibleRegions, nsecs_t latchTime, bool bgColorOnly) { ATRACE_FORMAT_INSTANT("latchBuffer %s - %" PRIu64, getDebugName(), getDrawingState().frameNumber); @@ -3801,8 +3874,7 @@ bool Layer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { mFlinger->onLayerUpdate(); return false; } - - updateTexImage(latchTime); + updateTexImage(latchTime, bgColorOnly); if (mDrawingState.buffer == nullptr) { return false; } @@ -4006,7 +4078,6 @@ void Layer::updateSnapshot(bool updateGeometry) { snapshot->bufferSize = getBufferSize(mDrawingState); snapshot->externalTexture = mBufferInfo.mBuffer; snapshot->hasReadyFrame = hasReadyFrame(); - snapshot->isInternalDisplayOverlay = isInternalDisplayOverlay(); preparePerFrameCompositionState(); } @@ -4049,7 +4120,7 @@ void Layer::updateRelativeMetadataSnapshot(const LayerMetadata& relativeLayerMet } } -void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, +bool Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener) { bool hadTrustedPresentationListener = hasTrustedPresentationListener(); mTrustedPresentationListener = listener; @@ -4060,6 +4131,20 @@ void Layer::setTrustedPresentationInfo(TrustedPresentationThresholds const& thre } else if (hadTrustedPresentationListener && !haveTrustedPresentationListener) { mFlinger->mNumTrustedPresentationListeners--; } + + // Reset trusted presentation states to ensure we start the time again. + mEnteredTrustedPresentationStateTime = -1; + mLastReportedTrustedPresentationState = false; + mLastComputedTrustedPresentationState = false; + + // If there's a new trusted presentation listener, the code needs to go through the composite + // path to ensure it recomutes the current state and invokes the TrustedPresentationListener if + // we're already in the requested state. + return haveTrustedPresentationListener; +} + +void Layer::updateLastLatchTime(nsecs_t latchTime) { + mLastLatchTime = latchTime; } // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 2955dafa0f..2fb122cac3 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -227,6 +227,8 @@ public: bool dimmingEnabled = true; float currentSdrHdrRatio = 1.f; float desiredSdrHdrRatio = 1.f; + gui::CachingHint cachingHint = gui::CachingHint::Enabled; + int64_t latchedVsyncId = 0; }; explicit Layer(const LayerCreationArgs& args); @@ -296,6 +298,7 @@ public: virtual bool isDimmingEnabled() const { return getDrawingState().dimmingEnabled; } float getDesiredSdrHdrRatio() const { return getDrawingState().desiredSdrHdrRatio; } float getCurrentSdrHdrRatio() const { return getDrawingState().currentSdrHdrRatio; } + gui::CachingHint getCachingHint() const { return getDrawingState().cachingHint; } bool setTransform(uint32_t /*transform*/); bool setTransformToDisplayInverse(bool /*transformToDisplayInverse*/); @@ -303,8 +306,10 @@ public: const BufferData& /* bufferData */, nsecs_t /* postTime */, nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/); + void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/); bool setDataspace(ui::Dataspace /*dataspace*/); bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio); + bool setCachingHint(gui::CachingHint cachingHint); bool setHdrMetadata(const HdrMetadata& /*hdrMetadata*/); bool setSurfaceDamageRegion(const Region& /*surfaceDamage*/); bool setApi(int32_t /*api*/); @@ -344,6 +349,7 @@ public: void useSurfaceDamage(); void useEmptyDamage(); Region getVisibleRegion(const DisplayDevice*) const; + void updateLastLatchTime(nsecs_t latchtime); /* * isOpaque - true if this surface is opaque @@ -424,6 +430,9 @@ public: */ bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/); + bool latchBufferImpl(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, + bool bgColorOnly); + /* * 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 @@ -601,6 +610,7 @@ public: bool isRemovedFromCurrentState() const; LayerProto* writeToProto(LayersProto& layersProto, uint32_t traceFlags); + void writeCompositionStateToProto(LayerProto* layerProto); // Write states that are modified by the main thread. This includes drawing // state as well as buffer data. This should be called in the main or tracing @@ -620,7 +630,7 @@ public: * doTransaction - process the transaction. This is a good place to figure * out which attributes of the surface have changed. */ - virtual uint32_t doTransaction(uint32_t transactionFlags, nsecs_t currentLatchTime); + virtual uint32_t doTransaction(uint32_t transactionFlags); /* * Remove relative z for the layer if its relative parent is not part of the @@ -754,7 +764,7 @@ public: std::shared_ptr<frametimeline::SurfaceFrame> createSurfaceFrameForBuffer( const FrameTimelineInfo& info, nsecs_t queueTime, std::string debugName); - void setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, + bool setTrustedPresentationInfo(TrustedPresentationThresholds const& thresholds, TrustedPresentationListener const& listener); // Creates a new handle each time, so we only expect @@ -843,6 +853,20 @@ public: void callReleaseBufferCallback(const sp<ITransactionCompletedListener>& listener, const sp<GraphicBuffer>& buffer, uint64_t framenumber, const sp<Fence>& releaseFence); + bool setFrameRateForLayerTreeLegacy(FrameRate); + bool setFrameRateForLayerTree(FrameRate, const scheduler::LayerProps&); + void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&); + void recordLayerHistoryAnimationTx(const scheduler::LayerProps&); + auto getLayerProps() const { + return scheduler::LayerProps{ + .visible = isVisible(), + .bounds = getBounds(), + .transform = getTransform(), + .setFrameRateVote = getFrameRateForLayerTree(), + .frameRateSelectionPriority = getFrameRateSelectionPriority(), + }; + }; + bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } protected: // For unit tests @@ -857,7 +881,7 @@ protected: void preparePerFrameCompositionState(); void preparePerFrameBufferCompositionState(); void preparePerFrameEffectsCompositionState(); - virtual void commitTransaction(State& stateToCommit, nsecs_t currentLatchTime = 0); + virtual void commitTransaction(State& stateToCommit); void gatherBufferInfo(); void onSurfaceFrameCreated(const std::shared_ptr<frametimeline::SurfaceFrame>&); @@ -1015,7 +1039,6 @@ private: void updateTreeHasFrameRateVote(); bool propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool* transactionNeeded); - bool setFrameRateForLayerTree(FrameRate); void setZOrderRelativeOf(const wp<Layer>& relativeOf); bool isTrustedOverlay() const; gui::DropInputMode getDropInputMode() const; @@ -1043,7 +1066,7 @@ private: bool hasFrameUpdate() const; - void updateTexImage(nsecs_t latchTime); + void updateTexImage(nsecs_t latchTime, bool bgColorOnly = false); // Crop that applies to the buffer Rect computeBufferCrop(const State& s); diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h index 01da0199a2..c23bd31d1a 100644 --- a/services/surfaceflinger/LayerFE.h +++ b/services/surfaceflinger/LayerFE.h @@ -16,6 +16,7 @@ #pragma once +#include <android/gui/CachingHint.h> #include <gui/LayerMetadata.h> #include "FrontEnd/LayerSnapshot.h" #include "compositionengine/LayerFE.h" diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 0506c47555..55281fa962 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -247,6 +247,153 @@ void LayerProtoHelper::readFromProto(const BlurRegion& proto, android::BlurRegio outRegion.right = proto.right(); outRegion.bottom = proto.bottom(); } + +void LayerProtoHelper::writeHierarchyToProto( + LayersProto& outLayersProto, const frontend::LayerHierarchy& root, + const frontend::LayerSnapshotBuilder& snapshotBuilder, + const std::unordered_map<uint32_t, sp<Layer>>& legacyLayers, uint32_t traceFlags) { + using Variant = frontend::LayerHierarchy::Variant; + frontend::LayerSnapshot defaultSnapshot; + + LayerProto* layerProto = outLayersProto.add_layers(); + const frontend::RequestedLayerState& layer = *root.getLayer(); + frontend::LayerSnapshot* snapshot = snapshotBuilder.getSnapshot(layer.id); + + if (!snapshot) { + defaultSnapshot.uniqueSequence = layer.id; + snapshot = &defaultSnapshot; + } + writeSnapshotToProto(layerProto, layer, *snapshot, traceFlags); + for (const auto& [child, variant] : root.mChildren) { + if (variant == Variant::Attached || variant == Variant::Detached) { + layerProto->add_children(child->getLayer()->id); + } else if (variant == Variant::Relative) { + layerProto->add_relatives(child->getLayer()->id); + } + } + + auto parent = root.getParent(); + if (parent && parent->getLayer()) { + layerProto->set_parent(parent->getLayer()->id); + } else { + layerProto->set_parent(-1); + } + + auto relativeParent = root.getRelativeParent(); + if (relativeParent && relativeParent->getLayer()) { + layerProto->set_z_order_relative_of(relativeParent->getLayer()->id); + } else { + layerProto->set_z_order_relative_of(-1); + } + + if (traceFlags & LayerTracing::TRACE_COMPOSITION) { + auto it = legacyLayers.find(layer.id); + if (it != legacyLayers.end()) { + it->second->writeCompositionStateToProto(layerProto); + } + } + + for (const auto& [child, variant] : root.mChildren) { + // avoid visiting relative layers twice + if (variant == Variant::Detached) { + continue; + } + writeHierarchyToProto(outLayersProto, *child, snapshotBuilder, legacyLayers, traceFlags); + } +} + +void LayerProtoHelper::writeSnapshotToProto(LayerProto* layerInfo, + const frontend::RequestedLayerState& requestedState, + const frontend::LayerSnapshot& snapshot, + uint32_t traceFlags) { + const ui::Transform transform = snapshot.geomLayerTransform; + auto buffer = requestedState.externalTexture; + if (buffer != nullptr) { + LayerProtoHelper::writeToProto(*buffer, + [&]() { return layerInfo->mutable_active_buffer(); }); + LayerProtoHelper::writeToProtoDeprecated(ui::Transform(requestedState.bufferTransform), + layerInfo->mutable_buffer_transform()); + } + layerInfo->set_invalidate(snapshot.contentDirty); + layerInfo->set_is_protected(snapshot.hasProtectedContent); + layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(snapshot.dataspace))); + layerInfo->set_curr_frame(requestedState.bufferData->frameNumber); + layerInfo->set_requested_corner_radius(requestedState.cornerRadius); + layerInfo->set_corner_radius( + (snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0); + layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius); + layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay); + LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform()); + LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), + [&]() { return layerInfo->mutable_position(); }); + LayerProtoHelper::writeToProto(snapshot.geomLayerBounds, + [&]() { return layerInfo->mutable_bounds(); }); + LayerProtoHelper::writeToProto(snapshot.surfaceDamage, + [&]() { return layerInfo->mutable_damage_region(); }); + + if (requestedState.hasColorTransform) { + LayerProtoHelper::writeToProto(snapshot.colorTransform, + layerInfo->mutable_color_transform()); + } + + LayerProtoHelper::writeToProto(snapshot.croppedBufferSize.toFloatRect(), + [&]() { return layerInfo->mutable_source_bounds(); }); + LayerProtoHelper::writeToProto(snapshot.transformedBounds, + [&]() { return layerInfo->mutable_screen_bounds(); }); + LayerProtoHelper::writeToProto(snapshot.roundedCorner.cropRect, + [&]() { return layerInfo->mutable_corner_radius_crop(); }); + layerInfo->set_shadow_radius(snapshot.shadowRadius); + + layerInfo->set_id(snapshot.uniqueSequence); + layerInfo->set_name(requestedState.name); + layerInfo->set_type("Layer"); + + LayerProtoHelper::writeToProto(requestedState.transparentRegion, + [&]() { return layerInfo->mutable_transparent_region(); }); + + layerInfo->set_layer_stack(snapshot.outputFilter.layerStack.id); + layerInfo->set_z(requestedState.z); + + ui::Transform requestedTransform = requestedState.getTransform(0); + LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { + return layerInfo->mutable_requested_position(); + }); + + LayerProtoHelper::writeToProto(requestedState.crop, + [&]() { return layerInfo->mutable_crop(); }); + + layerInfo->set_is_opaque(snapshot.contentOpaque); + if (requestedState.externalTexture) + layerInfo->set_pixel_format( + decodePixelFormat(requestedState.externalTexture->getPixelFormat())); + LayerProtoHelper::writeToProto(snapshot.color, [&]() { return layerInfo->mutable_color(); }); + LayerProtoHelper::writeToProto(requestedState.color, + [&]() { return layerInfo->mutable_requested_color(); }); + layerInfo->set_flags(requestedState.flags); + + LayerProtoHelper::writeToProtoDeprecated(requestedTransform, + layerInfo->mutable_requested_transform()); + + layerInfo->set_is_relative_of(requestedState.isRelativeOf); + + layerInfo->set_owner_uid(requestedState.ownerUid); + + if ((traceFlags & LayerTracing::TRACE_INPUT) && snapshot.hasInputInfo()) { + LayerProtoHelper::writeToProto(snapshot.inputInfo, {}, + [&]() { return layerInfo->mutable_input_window_info(); }); + } + + if (traceFlags & LayerTracing::TRACE_EXTRA) { + auto protoMap = layerInfo->mutable_metadata(); + for (const auto& entry : requestedState.metadata.mMap) { + (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); + } + } + + LayerProtoHelper::writeToProto(requestedState.destinationFrame, + [&]() { return layerInfo->mutable_destination_frame(); }); +} + } // namespace surfaceflinger } // namespace android diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 6ade1435e5..de4bd01f1a 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -58,6 +58,15 @@ public: static void readFromProto(const ColorTransformProto& colorTransformProto, mat4& matrix); static void writeToProto(const android::BlurRegion region, BlurRegion*); static void readFromProto(const BlurRegion& proto, android::BlurRegion& outRegion); + static void writeHierarchyToProto(LayersProto& layersProto, + const frontend::LayerHierarchy& root, + const frontend::LayerSnapshotBuilder& snapshotBuilder, + const std::unordered_map<uint32_t, sp<Layer>>& mLegacyLayers, + uint32_t traceFlags); + + static void writeSnapshotToProto(LayerProto* outProto, + const frontend::RequestedLayerState& requestedState, + const frontend::LayerSnapshot& snapshot, uint32_t traceFlags); }; } // namespace surfaceflinger diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 0ade4679a3..9a4261d087 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -29,7 +29,6 @@ #include <SkBlendMode.h> #include <SkRect.h> #include <SkSurface.h> -#include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> #undef LOG_TAG @@ -46,15 +45,6 @@ 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) { - constexpr float kFrameRate = 0.f; - constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE; - constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS; - - return SurfaceComposerClient::Transaction().setFrameRate(surface, kFrameRate, kCompatibility, - kSeamlessness); -} - } // namespace SurfaceControlHolder::~SurfaceControlHolder() { @@ -242,7 +232,7 @@ RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, ftl::Flags<Features> f return; } - createTransaction(mSurfaceControl->get()) + createTransaction() .setLayer(mSurfaceControl->get(), INT32_MAX - 2) .setTrustedOverlay(mSurfaceControl->get(), true) .apply(); @@ -272,14 +262,14 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps displayFps, Fps renderFps) -> co } }(); - createTransaction(mSurfaceControl->get()) - .setTransform(mSurfaceControl->get(), transform) - .apply(); + createTransaction().setTransform(mSurfaceControl->get(), transform).apply(); BufferCache::const_iterator it = mBufferCache.find({displayFps.getIntValue(), renderFps.getIntValue(), transformHint}); if (it == mBufferCache.end()) { - const int minFps = mFpsRange.min.getIntValue(); + // HWC minFps is not known by the framework in order + // to consider lower rates we set minFps to 0. + const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue(); const int maxFps = mFpsRange.max.getIntValue(); // Clamp to the range. The current displayFps may be outside of this range if the display @@ -327,7 +317,7 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { frame.offsetBy(width >> 1, height >> 4); } - createTransaction(mSurfaceControl->get()) + createTransaction() .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight)) .setPosition(mSurfaceControl->get(), frame.left, frame.top) @@ -335,14 +325,14 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { } void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { - createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply(); + createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply(); } 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(); + createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply(); } void RefreshRateOverlay::animate() { @@ -351,7 +341,23 @@ void RefreshRateOverlay::animate() { const auto& buffers = getOrCreateBuffers(*mDisplayFps, *mRenderFps); mFrame = (mFrame + 1) % buffers.size(); const auto buffer = buffers[mFrame]; - createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply(); + createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply(); +} + +SurfaceComposerClient::Transaction RefreshRateOverlay::createTransaction() const { + constexpr float kFrameRate = 0.f; + constexpr int8_t kCompatibility = ANATIVEWINDOW_FRAME_RATE_NO_VOTE; + constexpr int8_t kSeamlessness = ANATIVEWINDOW_CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS; + + const sp<SurfaceControl>& surface = mSurfaceControl->get(); + + SurfaceComposerClient::Transaction transaction; + if (isSetByHwc()) { + transaction.setFlags(surface, layer_state_t::eLayerIsRefreshRateIndicator, + layer_state_t::eLayerIsRefreshRateIndicator); + } + transaction.setFrameRate(surface, kFrameRate, kCompatibility, kSeamlessness); + return transaction; } } // namespace android diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index b68a88c928..0b89b8e3a1 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -21,6 +21,7 @@ #include <ftl/flags.h> #include <ftl/small_map.h> +#include <gui/SurfaceComposerClient.h> #include <ui/LayerStack.h> #include <ui/Size.h> #include <ui/Transform.h> @@ -55,6 +56,7 @@ public: Spinner = 1 << 0, RenderRate = 1 << 1, ShowInMiddle = 1 << 2, + SetByHwc = 1 << 3, }; RefreshRateOverlay(FpsRange, ftl::Flags<Features>); @@ -63,6 +65,7 @@ public: void setViewport(ui::Size); void changeRefreshRate(Fps, Fps); void animate(); + bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); } private: using Buffers = std::vector<sp<GraphicBuffer>>; @@ -82,6 +85,8 @@ private: const Buffers& getOrCreateBuffers(Fps, Fps); + SurfaceComposerClient::Transaction createTransaction() const; + struct Key { int displayFps; int renderFps; diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 839500f8a0..327ca3f0aa 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -285,47 +285,73 @@ void RegionSamplingThread::captureSample() { std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; - auto traverseLayers = [&](const LayerVector::Visitor& visitor) { - bool stopLayerFound = false; - auto filterVisitor = [&](Layer* layer) { - // We don't want to capture any layers beyond the stop layer - if (stopLayerFound) return; - - // Likewise if we just found a stop layer, set the flag and abort - for (const auto& [area, stopLayerId, listener] : descriptors) { - if (stopLayerId != UNASSIGNED_LAYER_ID && layer->getSequence() == stopLayerId) { - stopLayerFound = true; - return; - } + auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds, + const ui::Transform transform, bool& outStopTraversal) -> bool { + // Likewise if we just found a stop layer, set the flag and abort + for (const auto& [area, stopLayerId, listener] : descriptors) { + if (stopLayerId != UNASSIGNED_LAYER_ID && layerId == stopLayerId) { + outStopTraversal = true; + return false; } + } - // Compute the layer's position on the screen - const Rect bounds = Rect(layer->getBounds()); - const ui::Transform transform = layer->getTransform(); - constexpr bool roundOutwards = true; - Rect transformed = transform.transform(bounds, roundOutwards); - - // If this layer doesn't intersect with the larger sampledBounds, skip capturing it - Rect ignore; - if (!transformed.intersect(sampledBounds, &ignore)) return; - - // If the layer doesn't intersect a sampling area, skip capturing it - bool intersectsAnyArea = false; - for (const auto& [area, stopLayer, listener] : descriptors) { - if (transformed.intersect(area, &ignore)) { - intersectsAnyArea = true; - listeners.insert(listener); - } + // Compute the layer's position on the screen + constexpr bool roundOutwards = true; + Rect transformed = transform.transform(bounds, roundOutwards); + + // If this layer doesn't intersect with the larger sampledBounds, skip capturing it + Rect ignore; + if (!transformed.intersect(sampledBounds, &ignore)) return false; + + // If the layer doesn't intersect a sampling area, skip capturing it + bool intersectsAnyArea = false; + for (const auto& [area, stopLayer, listener] : descriptors) { + if (transformed.intersect(area, &ignore)) { + intersectsAnyArea = true; + listeners.insert(listener); } - if (!intersectsAnyArea) return; + } + if (!intersectsAnyArea) return false; - ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getDebugName(), bounds.left, - bounds.top, bounds.right, bounds.bottom); - visitor(layer); - }; - mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor); + ALOGV("Traversing [%s] [%d, %d, %d, %d]", layerName, bounds.left, bounds.top, bounds.right, + bounds.bottom); + + return true; }; + std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots; + if (mFlinger.mLayerLifecycleManagerEnabled) { + auto filterFn = [&](const frontend::LayerSnapshot& snapshot, + bool& outStopTraversal) -> bool { + const Rect bounds = + frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds), + snapshot.transparentRegionHint); + const ui::Transform transform = snapshot.geomLayerTransform; + return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform, + outStopTraversal); + }; + getLayerSnapshots = + mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, + filterFn); + } else { + auto traverseLayers = [&](const LayerVector::Visitor& visitor) { + bool stopLayerFound = false; + auto filterVisitor = [&](Layer* layer) { + // We don't want to capture any layers beyond the stop layer + if (stopLayerFound) return; + + if (!layerFilterFn(layer->getDebugName(), layer->getSequence(), + Rect(layer->getBounds()), layer->getTransform(), + stopLayerFound)) { + return; + } + visitor(layer); + }; + mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, filterVisitor); + }; + getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); + } + std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr; if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() && mCachedBuffer->getBuffer()->getHeight() == sampledBounds.getHeight()) { @@ -344,7 +370,6 @@ void RegionSamplingThread::captureSample() { renderengine::impl::ExternalTexture::Usage:: WRITEABLE); } - auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers); constexpr bool kRegionSampling = true; constexpr bool kGrayscale = false; diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index eb6d7e4cfe..57661f199a 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -238,7 +238,7 @@ EventThread::~EventThread() = default; namespace impl { -EventThread::EventThread(const char* name, scheduler::VsyncSchedule& vsyncSchedule, +EventThread::EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule> vsyncSchedule, android::frametimeline::TokenManager* tokenManager, ThrottleVsyncCallback throttleVsyncCallback, GetVsyncPeriodFunction getVsyncPeriodFunction, @@ -248,13 +248,8 @@ EventThread::EventThread(const char* name, scheduler::VsyncSchedule& vsyncSchedu mVsyncTracer(base::StringPrintf("VSYNC-%s", name), 0), mWorkDuration(base::StringPrintf("VsyncWorkDuration-%s", name), workDuration), mReadyDuration(readyDuration), - mVsyncSchedule(vsyncSchedule), - mVsyncRegistration( - vsyncSchedule.getDispatch(), - [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { - onVsync(vsyncTime, wakeupTime, readyTime); - }, - name), + mVsyncSchedule(std::move(vsyncSchedule)), + mVsyncRegistration(mVsyncSchedule->getDispatch(), createDispatchCallback(), name), mTokenManager(tokenManager), mThrottleVsyncCallback(std::move(throttleVsyncCallback)), mGetVsyncPeriodFunction(std::move(getVsyncPeriodFunction)) { @@ -375,7 +370,7 @@ VsyncEventData EventThread::getLatestVsyncEventData( vsyncEventData.frameInterval = frameInterval; const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> { std::lock_guard<std::mutex> lock(mMutex); - const auto vsyncTime = mVsyncSchedule.getTracker().nextAnticipatedVSyncTimeFrom( + const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom( systemTime() + mWorkDuration.get().count() + mReadyDuration.count()); return {vsyncTime, vsyncTime - mReadyDuration.count()}; }(); @@ -384,23 +379,13 @@ VsyncEventData EventThread::getLatestVsyncEventData( return vsyncEventData; } -void EventThread::onScreenReleased() { +void EventThread::enableSyntheticVsync(bool enable) { std::lock_guard<std::mutex> lock(mMutex); - if (!mVSyncState || mVSyncState->synthetic) { + if (!mVSyncState || mVSyncState->synthetic == enable) { return; } - mVSyncState->synthetic = true; - mCondition.notify_all(); -} - -void EventThread::onScreenAcquired() { - std::lock_guard<std::mutex> lock(mMutex); - if (!mVSyncState || !mVSyncState->synthetic) { - return; - } - - mVSyncState->synthetic = false; + mVSyncState->synthetic = enable; mCondition.notify_all(); } @@ -543,7 +528,7 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, const auto throttleVsync = [&] { const auto& vsyncData = event.vsync.vsyncData; if (connection->frameRate.isValid()) { - return !mVsyncSchedule.getTracker() + return !mVsyncSchedule->getTracker() .isVSyncInPhase(vsyncData.preferredExpectedPresentationTime(), connection->frameRate); } @@ -706,6 +691,26 @@ const char* EventThread::toCString(State state) { } } +void EventThread::onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule> schedule) { + std::lock_guard<std::mutex> lock(mMutex); + const bool reschedule = mVsyncRegistration.cancel() == scheduler::CancelResult::Cancelled; + mVsyncSchedule = std::move(schedule); + mVsyncRegistration = + scheduler::VSyncCallbackRegistration(mVsyncSchedule->getDispatch(), + createDispatchCallback(), mThreadName); + if (reschedule) { + mVsyncRegistration.schedule({.workDuration = mWorkDuration.get().count(), + .readyDuration = mReadyDuration.count(), + .earliestVsync = mLastVsyncCallbackTime.ns()}); + } +} + +scheduler::VSyncDispatch::Callback EventThread::createDispatchCallback() { + return [this](nsecs_t vsyncTime, nsecs_t wakeupTime, nsecs_t readyTime) { + onVsync(vsyncTime, wakeupTime, readyTime); + }; +} + } // namespace impl } // namespace android diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 347dc4afd5..87e20a0636 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -106,11 +106,8 @@ public: virtual sp<EventThreadConnection> createEventConnection( ResyncCallback, EventRegistrationFlags eventRegistration = {}) const = 0; - // called before the screen is turned off from main thread - virtual void onScreenReleased() = 0; - - // called after the screen is turned on from main thread - virtual void onScreenAcquired() = 0; + // Feed clients with fake VSYNC, e.g. while the display is off. + virtual void enableSyntheticVsync(bool) = 0; virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; @@ -136,6 +133,8 @@ public: // Retrieves the number of event connections tracked by this EventThread. virtual size_t getEventThreadConnectionCount() = 0; + + virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0; }; namespace impl { @@ -145,8 +144,8 @@ public: using ThrottleVsyncCallback = std::function<bool(nsecs_t, uid_t)>; using GetVsyncPeriodFunction = std::function<nsecs_t(uid_t)>; - EventThread(const char* name, scheduler::VsyncSchedule&, frametimeline::TokenManager*, - ThrottleVsyncCallback, GetVsyncPeriodFunction, + EventThread(const char* name, std::shared_ptr<scheduler::VsyncSchedule>, + frametimeline::TokenManager*, ThrottleVsyncCallback, GetVsyncPeriodFunction, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); ~EventThread(); @@ -159,11 +158,7 @@ public: VsyncEventData getLatestVsyncEventData( const sp<EventThreadConnection>& connection) const override; - // called before the screen is turned off from main thread - void onScreenReleased() override; - - // called after the screen is turned on from main thread - void onScreenAcquired() override; + void enableSyntheticVsync(bool) override; void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; @@ -179,6 +174,8 @@ public: size_t getEventThreadConnectionCount() override; + void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) override; + private: friend EventThreadTest; @@ -202,11 +199,13 @@ private: nsecs_t timestamp, nsecs_t preferredExpectedPresentationTime, nsecs_t preferredDeadlineTimestamp) const; + scheduler::VSyncDispatch::Callback createDispatchCallback(); + const char* const mThreadName; TracedOrdinal<int> mVsyncTracer; TracedOrdinal<std::chrono::nanoseconds> mWorkDuration GUARDED_BY(mMutex); std::chrono::nanoseconds mReadyDuration GUARDED_BY(mMutex); - scheduler::VsyncSchedule& mVsyncSchedule; + std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule; TimePoint mLastVsyncCallbackTime GUARDED_BY(mMutex) = TimePoint::now(); scheduler::VSyncCallbackRegistration mVsyncRegistration GUARDED_BY(mMutex); frametimeline::TokenManager* const mTokenManager; diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h index c4de7497ac..92c2189244 100644 --- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h +++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h @@ -18,12 +18,14 @@ #include <vector> +#include <ui/DisplayId.h> + #include "Display/DisplayModeRequest.h" namespace android::scheduler { struct ISchedulerCallback { - virtual void setVsyncEnabled(bool) = 0; + virtual void setVsyncEnabled(PhysicalDisplayId, bool) = 0; virtual void requestDisplayModes(std::vector<display::DisplayModeRequest>) = 0; virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index e853833bb9..beaf9724a3 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -118,27 +118,17 @@ void LayerHistory::deregisterLayer(Layer* layer) { } } -void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, - LayerUpdateType updateType) { +void LayerHistory::record(int32_t id, const LayerProps& layerProps, nsecs_t presentTime, + nsecs_t now, LayerUpdateType updateType) { 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()); + ALOGV("%s: %d not registered", __func__, id); return; } const auto& info = layerPair->second; - const auto layerProps = LayerInfo::LayerProps{ - .visible = layer->isVisible(), - .bounds = layer->getBounds(), - .transform = layer->getTransform(), - .setFrameRateVote = layer->getFrameRateForLayerTree(), - .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(), - }; - info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps); // Set frame rate to attached choreographer. @@ -149,7 +139,7 @@ void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now, while (it != range.second) { sp<EventThreadConnection> choreographerConnection = it->second.promote(); if (choreographerConnection) { - choreographerConnection->frameRate = layer->getFrameRateForLayerTree().rate; + choreographerConnection->frameRate = layerProps.setFrameRateVote.rate; it++; } else { it = mAttachedChoreographers.erase(it); diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h index 68e7030feb..69caf9ffd2 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.h +++ b/services/surfaceflinger/Scheduler/LayerHistory.h @@ -38,6 +38,7 @@ class Layer; namespace scheduler { class LayerInfo; +struct LayerProps; class LayerHistory { public: @@ -63,7 +64,8 @@ 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); + void record(int32_t id, const LayerProps& props, nsecs_t presentTime, nsecs_t now, + LayerUpdateType updateType); // Updates the default frame rate compatibility which takes effect when the app // does not set a preference for refresh rate. diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 0142ccd93f..5a90d5866e 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -44,14 +44,17 @@ LayerInfo::LayerInfo(const std::string& name, uid_t ownerUid, mOwnerUid(ownerUid), mDefaultVote(defaultVote), mLayerVote({defaultVote, Fps()}), - mRefreshRateHistory(name) {} + mLayerProps(std::make_unique<LayerProps>()), + mRefreshRateHistory(name) { + ; +} void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType, - bool pendingModeChange, LayerProps props) { + bool pendingModeChange, const LayerProps& props) { lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0)); mLastUpdatedTime = std::max(lastPresentTime, now); - mLayerProps = props; + *mLayerProps = props; switch (updateType) { case LayerUpdateType::AnimationTX: mLastAnimationTime = std::max(lastPresentTime, now); @@ -305,6 +308,26 @@ const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const { return mTraceTags.at(type).c_str(); } +LayerInfo::FrameRate LayerInfo::getSetFrameRateVote() const { + return mLayerProps->setFrameRateVote; +} + +bool LayerInfo::isVisible() const { + return mLayerProps->visible; +} + +int32_t LayerInfo::getFrameRateSelectionPriority() const { + return mLayerProps->frameRateSelectionPriority; +} + +FloatRect LayerInfo::getBounds() const { + return mLayerProps->bounds; +} + +ui::Transform LayerInfo::getTransform() const { + return mLayerProps->transform; +} + LayerInfo::RefreshRateHistory::HeuristicTraceTagData LayerInfo::RefreshRateHistory::makeHeuristicTraceTagData() const { const std::string prefix = "LFPS "; diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 93485be0a4..a3523ac25e 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -37,7 +37,7 @@ class Layer; namespace scheduler { using namespace std::chrono_literals; - +struct LayerProps; // Maximum period between presents for a layer to be considered active. constexpr std::chrono::nanoseconds MAX_ACTIVE_LAYER_PERIOD_NS = 1200ms; @@ -132,19 +132,11 @@ public: LayerInfo(const LayerInfo&) = delete; LayerInfo& operator=(const LayerInfo&) = delete; - struct LayerProps { - bool visible = false; - FloatRect bounds; - ui::Transform transform; - FrameRate setFrameRateVote; - int32_t frameRateSelectionPriority = -1; - }; - // Records the last requested present time. It also stores information about when // the layer was last updated. If the present time is farther in the future than the // updated time, the updated time is the present time. void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUpdateType updateType, - bool pendingModeChange, LayerProps props); + bool pendingModeChange, const LayerProps& props); // Sets an explicit layer vote. This usually comes directly from the application via // ANativeWindow_setFrameRate API @@ -168,13 +160,11 @@ public: // updated time, the updated time is the present time. nsecs_t getLastUpdatedTime() const { return mLastUpdatedTime; } - FrameRate getSetFrameRateVote() const { return mLayerProps.setFrameRateVote; } - bool isVisible() const { return mLayerProps.visible; } - int32_t getFrameRateSelectionPriority() const { return mLayerProps.frameRateSelectionPriority; } - - FloatRect getBounds() const { return mLayerProps.bounds; } - - ui::Transform getTransform() const { return mLayerProps.transform; } + FrameRate getSetFrameRateVote() const; + bool isVisible() const; + int32_t getFrameRateSelectionPriority() const; + FloatRect getBounds() const; + ui::Transform getTransform() const; // Returns a C string for tracing a vote const char* getTraceTag(LayerHistory::LayerVoteType type) const; @@ -294,7 +284,7 @@ private: static constexpr size_t HISTORY_SIZE = RefreshRateHistory::HISTORY_SIZE; static constexpr std::chrono::nanoseconds HISTORY_DURATION = 1s; - LayerProps mLayerProps; + std::unique_ptr<LayerProps> mLayerProps; RefreshRateHistory mRefreshRateHistory; @@ -304,5 +294,13 @@ private: static bool sTraceEnabled; }; +struct LayerProps { + bool visible = false; + FloatRect bounds; + ui::Transform transform; + LayerInfo::FrameRate setFrameRateVote; + int32_t frameRateSelectionPriority = -1; +}; + } // namespace scheduler } // namespace android diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp index dec8f59ee9..7457b84011 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.cpp +++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp @@ -75,19 +75,36 @@ void MessageQueue::vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, ns mHandler->dispatchFrame(vsyncId, expectedVsyncTime); } -void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch, +void MessageQueue::initVsync(std::shared_ptr<scheduler::VSyncDispatch> dispatch, frametimeline::TokenManager& tokenManager, std::chrono::nanoseconds workDuration) { std::lock_guard lock(mVsync.mutex); mVsync.workDuration = workDuration; mVsync.tokenManager = &tokenManager; + onNewVsyncScheduleLocked(std::move(dispatch)); +} + +void MessageQueue::onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch> dispatch) { + std::lock_guard lock(mVsync.mutex); + onNewVsyncScheduleLocked(std::move(dispatch)); +} + +void MessageQueue::onNewVsyncScheduleLocked(std::shared_ptr<scheduler::VSyncDispatch> dispatch) { + const bool reschedule = mVsync.registration && + mVsync.registration->cancel() == scheduler::CancelResult::Cancelled; mVsync.registration = std::make_unique< - scheduler::VSyncCallbackRegistration>(dispatch, + scheduler::VSyncCallbackRegistration>(std::move(dispatch), std::bind(&MessageQueue::vsyncCallback, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), "sf"); + if (reschedule) { + mVsync.scheduledFrameTime = + mVsync.registration->schedule({.workDuration = mVsync.workDuration.get().count(), + .readyDuration = 0, + .earliestVsync = mVsync.lastCallbackTime.ns()}); + } } void MessageQueue::destroyVsync() { diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h index 0d59337950..9c9b2f35c0 100644 --- a/services/surfaceflinger/Scheduler/MessageQueue.h +++ b/services/surfaceflinger/Scheduler/MessageQueue.h @@ -65,7 +65,7 @@ class MessageQueue { public: virtual ~MessageQueue() = default; - virtual void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, + virtual void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) = 0; virtual void destroyVsync() = 0; virtual void setDuration(std::chrono::nanoseconds workDuration) = 0; @@ -106,6 +106,8 @@ protected: void vsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime, nsecs_t readyTime); + void onNewVsyncSchedule(std::shared_ptr<scheduler::VSyncDispatch>) EXCLUDES(mVsync.mutex); + private: virtual void onFrameSignal(ICompositor&, VsyncId, TimePoint expectedVsyncTime) = 0; @@ -127,10 +129,12 @@ private: Vsync mVsync; + void onNewVsyncScheduleLocked(std::shared_ptr<scheduler::VSyncDispatch>) REQUIRES(mVsync.mutex); + public: explicit MessageQueue(ICompositor&); - void initVsync(scheduler::VSyncDispatch&, frametimeline::TokenManager&, + void initVsync(std::shared_ptr<scheduler::VSyncDispatch>, frametimeline::TokenManager&, std::chrono::nanoseconds workDuration) override; void destroyVsync() override; void setDuration(std::chrono::nanoseconds workDuration) override; diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h index f95646c48f..02e8719c08 100644 --- a/services/surfaceflinger/Scheduler/OneShotTimer.h +++ b/services/surfaceflinger/Scheduler/OneShotTimer.h @@ -40,7 +40,7 @@ public: OneShotTimer(std::string name, const Interval& interval, const ResetCallback& resetCallback, const TimeoutCallback& timeoutCallback, - std::unique_ptr<Clock> clock = std::make_unique<SteadyClock>()); + std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>()); ~OneShotTimer(); Duration interval() const { return mInterval; } @@ -82,7 +82,7 @@ private: std::thread mThread; // Clock object for the timer. Mocked in unit tests. - std::unique_ptr<Clock> mClock; + std::unique_ptr<android::Clock> mClock; // Semaphore to keep mThread synchronized. sem_t mSemaphore; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index f6fe468dd6..eec7c085cc 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -308,7 +308,7 @@ float RefreshRateSelector::calculateNonExactMatchingLayerScoreLocked(const Layer // 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; + constexpr float kMinMultiplier = 0.75f; if (multiplier >= kMinMultiplier && isFractionalPairOrMultiple(refreshRate, layer.desiredRefreshRate)) { return kScoreForFractionalPairs; @@ -958,7 +958,7 @@ auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt, } const bool ascending = (refreshRateOrder == RefreshRateOrder::Ascending); - const auto id = frameRateMode.modePtr->getId(); + const auto id = modePtr->getId(); if (ascending && frameRateMode.fps < *maxRenderRateForMode.get(id)) { // TODO(b/266481656): Once this bug is fixed, we can remove this workaround and actually // use a lower frame rate when we want Ascending frame rates. @@ -970,14 +970,20 @@ auto RefreshRateSelector::rankFrameRates(std::optional<int> anchorGroupOpt, if (ascending) { score = 1.0f / score; } + + constexpr float kScore = std::numeric_limits<float>::max(); 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; + } else if (ascending && id == getMinRefreshRateByPolicyLocked()->getId()) { + // TODO(b/266481656): Once this bug is fixed, we can remove this workaround + // and actually use a lower frame rate when we want Ascending frame rates. + ranking.emplace_front(ScoredFrameRate{frameRateMode, kScore}); + return; } ALOGV("%s(%s) %s (%s) scored %.2f", whence, ftl::enum_string(refreshRateOrder).c_str(), diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index 4f5842a67a..5052e6e257 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -32,7 +32,6 @@ #include <scheduler/Seamlessness.h> #include "DisplayHardware/DisplayMode.h" -#include "DisplayHardware/HWComposer.h" #include "Scheduler/OneShotTimer.h" #include "Scheduler/StrongTyping.h" #include "ThreadContext.h" @@ -297,6 +296,8 @@ public: RefreshRateSelector(const RefreshRateSelector&) = delete; RefreshRateSelector& operator=(const RefreshRateSelector&) = delete; + const DisplayModes& displayModes() const { return mDisplayModes; } + // 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. Once Config::FrameRateOverride::Enabled becomes the default, diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 17cdff9518..6e332721b8 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -81,7 +81,7 @@ Scheduler::~Scheduler() { mTouchTimer.reset(); // Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler. - demoteLeaderDisplay(); + demotePacesetterDisplay(); } void Scheduler::startTimers() { @@ -106,34 +106,43 @@ void Scheduler::startTimers() { } } -void Scheduler::setLeaderDisplay(std::optional<PhysicalDisplayId> leaderIdOpt) { - demoteLeaderDisplay(); +void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) { + demotePacesetterDisplay(); std::scoped_lock lock(mDisplayLock); - promoteLeaderDisplay(leaderIdOpt); + promotePacesetterDisplay(pacesetterIdOpt); } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - demoteLeaderDisplay(); + registerDisplayInternal(displayId, std::move(selectorPtr), + std::make_shared<VsyncSchedule>(displayId, mFeatures)); +} + +void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId, + RefreshRateSelectorPtr selectorPtr, + std::shared_ptr<VsyncSchedule> vsyncSchedule) { + demotePacesetterDisplay(); std::scoped_lock lock(mDisplayLock); mRefreshRateSelectors.emplace_or_replace(displayId, std::move(selectorPtr)); + mVsyncSchedules.emplace_or_replace(displayId, std::move(vsyncSchedule)); - promoteLeaderDisplay(); + promotePacesetterDisplay(); } void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) { - demoteLeaderDisplay(); + demotePacesetterDisplay(); std::scoped_lock lock(mDisplayLock); mRefreshRateSelectors.erase(displayId); + mVsyncSchedules.erase(displayId); // Do not allow removing the final display. Code in the scheduler expects // there to be at least one display. (This may be relaxed in the future with // headless virtual display.) LOG_ALWAYS_FATAL_IF(mRefreshRateSelectors.empty(), "Cannot unregister all displays!"); - promoteLeaderDisplay(); + promotePacesetterDisplay(); } void Scheduler::run() { @@ -154,13 +163,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, compositor.sample(); } -void Scheduler::createVsyncSchedule(FeatureFlags features) { - mVsyncSchedule = std::make_unique<VsyncSchedule>(features); -} - std::optional<Fps> Scheduler::getFrameRateOverride(uid_t uid) const { const bool supportsFrameRateOverrideByContent = - leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); + pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent(); return mFrameRateOverrideMappings .getFrameRateOverrideForUid(uid, supportsFrameRateOverrideByContent); } @@ -172,11 +177,11 @@ bool Scheduler::isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const } ATRACE_FORMAT("%s uid: %d frameRate: %s", __func__, uid, to_string(*frameRate).c_str()); - return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate); + return getVsyncSchedule()->getTracker().isVSyncInPhase(expectedVsyncTimestamp.ns(), *frameRate); } bool Scheduler::isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const { - return mVsyncSchedule->getTracker().isVSyncInPhase(timePoint.ns(), frameRate); + return getVsyncSchedule()->getTracker().isVSyncInPhase(timePoint.ns(), frameRate); } impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const { @@ -187,8 +192,9 @@ impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() impl::EventThread::GetVsyncPeriodFunction Scheduler::makeGetVsyncPeriodFunction() const { return [this](uid_t uid) { - const Fps refreshRate = leaderSelectorPtr()->getActiveMode().fps; - const nsecs_t currentPeriod = mVsyncSchedule->period().ns() ?: refreshRate.getPeriodNsecs(); + const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().fps; + const nsecs_t currentPeriod = + getVsyncSchedule()->period().ns() ?: refreshRate.getPeriodNsecs(); const auto frameRate = getFrameRateOverride(uid); if (!frameRate.has_value()) { @@ -208,7 +214,7 @@ ConnectionHandle Scheduler::createEventThread(Cycle cycle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration) { auto eventThread = std::make_unique<impl::EventThread>(cycle == Cycle::Render ? "app" : "appSf", - *mVsyncSchedule, tokenManager, + getVsyncSchedule(), tokenManager, makeThrottleVsyncCallback(), makeGetVsyncPeriodFunction(), workDuration, readyDuration); @@ -265,29 +271,21 @@ void Scheduler::onHotplugReceived(ConnectionHandle handle, PhysicalDisplayId dis thread->onHotplugReceived(displayId, connected); } -void Scheduler::onScreenAcquired(ConnectionHandle handle) { - android::EventThread* thread; - { - std::lock_guard<std::mutex> lock(mConnectionsLock); - RETURN_IF_INVALID_HANDLE(handle); - thread = mConnections[handle].thread.get(); - } - thread->onScreenAcquired(); -} - -void Scheduler::onScreenReleased(ConnectionHandle handle) { +void Scheduler::enableSyntheticVsync(bool enable) { + // TODO(b/241285945): Remove connection handles. + const ConnectionHandle handle = mAppConnectionHandle; android::EventThread* thread; { std::lock_guard<std::mutex> lock(mConnectionsLock); RETURN_IF_INVALID_HANDLE(handle); thread = mConnections[handle].thread.get(); } - thread->onScreenReleased(); + thread->enableSyntheticVsync(enable); } void Scheduler::onFrameRateOverridesChanged(ConnectionHandle handle, PhysicalDisplayId displayId) { const bool supportsFrameRateOverrideByContent = - leaderSelectorPtr()->supportsAppFrameRateOverrideByContent(); + pacesetterSelectorPtr()->supportsAppFrameRateOverrideByContent(); std::vector<FrameRateOverride> overrides = mFrameRateOverrideMappings.getAllFrameRateOverrides(supportsFrameRateOverrideByContent); @@ -328,7 +326,7 @@ 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 (leaderSelectorPtr()->getActiveMode() != mPolicy.modeOpt) { + if (pacesetterSelectorPtr()->getActiveMode() != mPolicy.modeOpt) { return; } @@ -393,36 +391,60 @@ void Scheduler::setVsyncConfig(const VsyncConfig& config, Period vsyncPeriod) { setDuration(config.sfWorkDuration); } -void Scheduler::enableHardwareVsync() { - mVsyncSchedule->enableHardwareVsync(mSchedulerCallback); +void Scheduler::enableHardwareVsync(PhysicalDisplayId id) { + auto schedule = getVsyncSchedule(id); + schedule->enableHardwareVsync(mSchedulerCallback); } -void Scheduler::disableHardwareVsync(bool disallow) { - mVsyncSchedule->disableHardwareVsync(mSchedulerCallback, disallow); +void Scheduler::disableHardwareVsync(PhysicalDisplayId id, bool disallow) { + auto schedule = getVsyncSchedule(id); + schedule->disableHardwareVsync(mSchedulerCallback, disallow); } -void Scheduler::resyncToHardwareVsync(bool allowToEnable, Fps refreshRate) { - if (mVsyncSchedule->isHardwareVsyncAllowed(allowToEnable) && refreshRate.isValid()) { - mVsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod()); +void Scheduler::resyncAllToHardwareVsync(bool allowToEnable) { + ATRACE_CALL(); + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + + for (const auto& [id, _] : mRefreshRateSelectors) { + resyncToHardwareVsyncLocked(id, allowToEnable); } } -void Scheduler::setRenderRate(Fps renderFrameRate) { - const auto mode = leaderSelectorPtr()->getActiveMode(); +void Scheduler::resyncToHardwareVsyncLocked(PhysicalDisplayId id, bool allowToEnable, + std::optional<Fps> refreshRate) { + auto schedule = getVsyncScheduleLocked(id); + if (schedule->isHardwareVsyncAllowed(allowToEnable)) { + if (!refreshRate) { + auto selectorPtr = mRefreshRateSelectors.get(id); + LOG_ALWAYS_FATAL_IF(!selectorPtr); + refreshRate = selectorPtr->get()->getActiveMode().modePtr->getFps(); + } + if (refreshRate->isValid()) { + schedule->startPeriodTransition(mSchedulerCallback, refreshRate->getPeriod(), + false /* force */); + } + } +} + +void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + + auto selectorPtr = mRefreshRateSelectors.get(id); + LOG_ALWAYS_FATAL_IF(!selectorPtr); + const auto mode = selectorPtr->get()->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()); + "Mismatch in render frame rates. Selector: %s, Scheduler: %s, Display: " + "%" PRIu64, + to_string(mode.fps).c_str(), to_string(renderFrameRate).c_str(), id.value); 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)); + getVsyncScheduleLocked(id)->getTracker().setRenderRate(renderFrameRate); } void Scheduler::resync() { @@ -432,24 +454,26 @@ void Scheduler::resync() { const nsecs_t last = mLastResyncTime.exchange(now); if (now - last > kIgnoreDelay) { - const auto refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); - resyncToHardwareVsync(false, refreshRate); + resyncAllToHardwareVsync(false /* allowToEnable */); } } -bool Scheduler::addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriodIn) { +bool Scheduler::addResyncSample(PhysicalDisplayId id, nsecs_t timestamp, + std::optional<nsecs_t> hwcVsyncPeriodIn) { const auto hwcVsyncPeriod = ftl::Optional(hwcVsyncPeriodIn).transform([](nsecs_t nanos) { return Period::fromNs(nanos); }); - return mVsyncSchedule->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp), - hwcVsyncPeriod); + return getVsyncSchedule(id)->addResyncSample(mSchedulerCallback, TimePoint::fromNs(timestamp), + hwcVsyncPeriod); } -void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) { - if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) { - enableHardwareVsync(); +void Scheduler::addPresentFence(PhysicalDisplayId id, std::shared_ptr<FenceTime> fence) { + auto schedule = getVsyncSchedule(id); + const bool needMoreSignals = schedule->getController().addPresentFence(std::move(fence)); + if (needMoreSignals) { + schedule->enableHardwareVsync(mSchedulerCallback); } else { - disableHardwareVsync(false); + schedule->disableHardwareVsync(mSchedulerCallback, false /* disallow */); } } @@ -464,10 +488,10 @@ void Scheduler::deregisterLayer(Layer* layer) { mLayerHistory.deregisterLayer(layer); } -void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, +void Scheduler::recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime, LayerHistory::LayerUpdateType updateType) { - if (leaderSelectorPtr()->canSwitch()) { - mLayerHistory.record(layer, presentTime, systemTime(), updateType); + if (pacesetterSelectorPtr()->canSwitch()) { + mLayerHistory.record(id, layerProps, presentTime, systemTime(), updateType); } } @@ -481,7 +505,7 @@ void Scheduler::setDefaultFrameRateCompatibility(Layer* layer) { } void Scheduler::chooseRefreshRateForContent() { - const auto selectorPtr = leaderSelectorPtr(); + const auto selectorPtr = pacesetterSelectorPtr(); if (!selectorPtr->canSwitch()) return; ATRACE_CALL(); @@ -491,22 +515,32 @@ void Scheduler::chooseRefreshRateForContent() { } void Scheduler::resetIdleTimer() { - leaderSelectorPtr()->resetIdleTimer(); + pacesetterSelectorPtr()->resetIdleTimer(); } void Scheduler::onTouchHint() { if (mTouchTimer) { mTouchTimer->reset(); - leaderSelectorPtr()->resetKernelIdleTimer(); + pacesetterSelectorPtr()->resetKernelIdleTimer(); } } -void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { - { +void Scheduler::setDisplayPowerMode(PhysicalDisplayId id, hal::PowerMode powerMode) { + const bool isPacesetter = [this, id]() REQUIRES(kMainThreadContext) { + ftl::FakeGuard guard(mDisplayLock); + return id == mPacesetterDisplayId; + }(); + if (isPacesetter) { + // TODO (b/255657128): This needs to be handled per display. std::lock_guard<std::mutex> lock(mPolicyLock); mPolicy.displayPowerMode = powerMode; } - mVsyncSchedule->getController().setDisplayPowerMode(powerMode); + { + std::scoped_lock lock(mDisplayLock); + auto vsyncSchedule = getVsyncScheduleLocked(id); + vsyncSchedule->getController().setDisplayPowerMode(powerMode); + } + if (!isPacesetter) return; if (mDisplayPowerTimer) { mDisplayPowerTimer->reset(); @@ -517,12 +551,30 @@ void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { mLayerHistory.clear(); } +std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncSchedule( + std::optional<PhysicalDisplayId> idOpt) const { + std::scoped_lock lock(mDisplayLock); + return getVsyncScheduleLocked(idOpt); +} + +std::shared_ptr<const VsyncSchedule> Scheduler::getVsyncScheduleLocked( + std::optional<PhysicalDisplayId> idOpt) const { + ftl::FakeGuard guard(kMainThreadContext); + if (!idOpt) { + LOG_ALWAYS_FATAL_IF(!mPacesetterDisplayId, "Missing a pacesetter!"); + idOpt = mPacesetterDisplayId; + } + auto scheduleOpt = mVsyncSchedules.get(*idOpt); + LOG_ALWAYS_FATAL_IF(!scheduleOpt); + return std::const_pointer_cast<const VsyncSchedule>(scheduleOpt->get()); +} + void Scheduler::kernelIdleTimerCallback(TimerState state) { ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state)); // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate // magic number - const Fps refreshRate = leaderSelectorPtr()->getActiveMode().modePtr->getFps(); + const Fps refreshRate = pacesetterSelectorPtr()->getActiveMode().modePtr->getFps(); constexpr Fps FPS_THRESHOLD_FOR_KERNEL_TIMER = 65_Hz; using namespace fps_approx_ops; @@ -531,12 +583,17 @@ void Scheduler::kernelIdleTimerCallback(TimerState state) { // If we're not in performance mode then the kernel timer shouldn't do // anything, as the refresh rate during DPU power collapse will be the // same. - resyncToHardwareVsync(true /* makeAvailable */, refreshRate); + resyncAllToHardwareVsync(true /* allowToEnable */); } else if (state == TimerState::Expired && refreshRate <= FPS_THRESHOLD_FOR_KERNEL_TIMER) { // Disable HW VSYNC if the timer expired, as we don't need it enabled if // we're not pushing frames, and if we're in PERFORMANCE mode then we'll // need to update the VsyncController model anyway. - disableHardwareVsync(false /* makeUnavailable */); + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + constexpr bool disallow = false; + for (auto& [_, schedule] : mVsyncSchedules) { + schedule->disableHardwareVsync(mSchedulerCallback, disallow); + } } mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired); @@ -581,7 +638,7 @@ void Scheduler::dump(utils::Dumper& dumper) const { { std::scoped_lock lock(mDisplayLock); ftl::FakeGuard guard(kMainThreadContext); - dumper.dump("leaderDisplayId"sv, mLeaderDisplayId); + dumper.dump("pacesetterDisplayId"sv, mPacesetterDisplayId); } dumper.dump("layerHistory"sv, mLayerHistory.dump()); dumper.dump("touchTimer"sv, mTouchTimer.transform(&OneShotTimer::interval)); @@ -593,46 +650,72 @@ void Scheduler::dump(utils::Dumper& dumper) const { } void Scheduler::dumpVsync(std::string& out) const { - mVsyncSchedule->dump(out); + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + if (mPacesetterDisplayId) { + base::StringAppendF(&out, "VsyncSchedule for pacesetter %s:\n", + to_string(*mPacesetterDisplayId).c_str()); + getVsyncScheduleLocked()->dump(out); + } + for (auto& [id, vsyncSchedule] : mVsyncSchedules) { + if (id == mPacesetterDisplayId) { + continue; + } + base::StringAppendF(&out, "VsyncSchedule for follower %s:\n", to_string(id).c_str()); + vsyncSchedule->dump(out); + } } bool Scheduler::updateFrameRateOverrides(GlobalSignals consideredSignals, Fps displayRefreshRate) { if (consideredSignals.idle) return false; const auto frameRateOverrides = - leaderSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements, - displayRefreshRate, consideredSignals); + pacesetterSelectorPtr()->getFrameRateOverrides(mPolicy.contentRequirements, + displayRefreshRate, consideredSignals); // Note that RefreshRateSelector::supportsFrameRateOverrideByContent is checked when querying // the FrameRateOverrideMappings rather than here. 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()); +void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) { + // TODO(b/241286431): Choose the pacesetter display. + mPacesetterDisplayId = pacesetterIdOpt.value_or(mRefreshRateSelectors.begin()->first); + ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str()); - if (const auto leaderPtr = leaderSelectorPtrLocked()) { - leaderPtr->setIdleTimerCallbacks( + auto vsyncSchedule = getVsyncScheduleLocked(*mPacesetterDisplayId); + if (const auto pacesetterPtr = pacesetterSelectorPtrLocked()) { + pacesetterPtr->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(); + pacesetterPtr->startIdleTimer(); + + const Fps refreshRate = pacesetterPtr->getActiveMode().modePtr->getFps(); + vsyncSchedule->startPeriodTransition(mSchedulerCallback, refreshRate.getPeriod(), + true /* force */); + } + + onNewVsyncSchedule(vsyncSchedule->getDispatch()); + { + std::lock_guard<std::mutex> lock(mConnectionsLock); + for (auto& [_, connection] : mConnections) { + connection.thread->onNewVsyncSchedule(vsyncSchedule); + } } } -void Scheduler::demoteLeaderDisplay() { +void Scheduler::demotePacesetterDisplay() { // No need to lock for reads on kMainThreadContext. - if (const auto leaderPtr = FTL_FAKE_GUARD(mDisplayLock, leaderSelectorPtrLocked())) { - leaderPtr->stopIdleTimer(); - leaderPtr->clearIdleTimerCallbacks(); + if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) { + pacesetterPtr->stopIdleTimer(); + pacesetterPtr->clearIdleTimerCallbacks(); } - // Clear state that depends on the leader's RefreshRateSelector. + // Clear state that depends on the pacesetter's RefreshRateSelector. std::scoped_lock lock(mPolicyLock); mPolicy = {}; } @@ -661,10 +744,11 @@ auto Scheduler::applyPolicy(S Policy::*statePtr, T&& newState) -> GlobalSignals 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. + // TODO(b/240743786): The pacesetter 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) + modeChoices.get(*mPacesetterDisplayId) .transform([](const DisplayModeChoice& choice) { return std::make_pair(choice.mode, choice.consideredSignals); }) @@ -797,7 +881,7 @@ GlobalSignals Scheduler::makeGlobalSignals() const { FrameRateMode Scheduler::getPreferredDisplayMode() { std::lock_guard<std::mutex> lock(mPolicyLock); const auto frameRateMode = - leaderSelectorPtr() + pacesetterSelectorPtr() ->getRankedFrameRates(mPolicy.contentRequirements, makeGlobalSignals()) .ranking.front() .frameRateMode; diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index a340919a65..74547d5ea1 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -102,8 +102,8 @@ public: void startTimers(); - // TODO(b/241285191): Remove this API by promoting leader in onScreen{Acquired,Released}. - void setLeaderDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext) + // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}. + void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock); using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>; @@ -114,8 +114,6 @@ public: void run(); - void createVsyncSchedule(FeatureFlags); - using Impl::initVsync; using Impl::getScheduledFrameTime; @@ -157,8 +155,8 @@ public: void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected); void onPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&) EXCLUDES(mPolicyLock); void onNonPrimaryDisplayModeChanged(ConnectionHandle, const FrameRateMode&); - void onScreenAcquired(ConnectionHandle); - void onScreenReleased(ConnectionHandle); + + void enableSyntheticVsync(bool = true) REQUIRES(kMainThreadContext); void onFrameRateOverridesChanged(ConnectionHandle, PhysicalDisplayId) EXCLUDES(mConnectionsLock); @@ -167,42 +165,62 @@ public: void setDuration(ConnectionHandle, std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration); - const VsyncModulator& vsyncModulator() const { return *mVsyncModulator; } + VsyncModulator& vsyncModulator() { return *mVsyncModulator; } + // In some cases, we should only modulate for the pacesetter display. In those + // cases, the caller should pass in the relevant display, and the method + // will no-op if it's not the pacesetter. Other cases are not specific to a + // display. template <typename... Args, typename Handler = std::optional<VsyncConfig> (VsyncModulator::*)(Args...)> - void modulateVsync(Handler handler, Args... args) { + void modulateVsync(std::optional<PhysicalDisplayId> id, Handler handler, Args... args) { + if (id) { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + if (id != mPacesetterDisplayId) { + return; + } + } + if (const auto config = (*mVsyncModulator.*handler)(args...)) { - setVsyncConfig(*config, getLeaderVsyncPeriod()); + setVsyncConfig(*config, getPacesetterVsyncPeriod()); } } void setVsyncConfigSet(const VsyncConfigSet&, Period vsyncPeriod); // Sets the render rate for the scheduler to run at. - void setRenderRate(Fps); + void setRenderRate(PhysicalDisplayId, Fps); - void enableHardwareVsync(); - void disableHardwareVsync(bool disallow); + void enableHardwareVsync(PhysicalDisplayId); + void disableHardwareVsync(PhysicalDisplayId, bool disallow); // Resyncs the scheduler to hardware vsync. // If allowToEnable is true, then hardware vsync will be turned on. // Otherwise, if hardware vsync is not already enabled then this method will // no-op. - void resyncToHardwareVsync(bool allowToEnable, Fps refreshRate); + // If refreshRate is nullopt, use the existing refresh rate of the display. + void resyncToHardwareVsync(PhysicalDisplayId id, bool allowToEnable, + std::optional<Fps> refreshRate = std::nullopt) + EXCLUDES(mDisplayLock) { + std::scoped_lock lock(mDisplayLock); + ftl::FakeGuard guard(kMainThreadContext); + resyncToHardwareVsyncLocked(id, allowToEnable, refreshRate); + } void resync() EXCLUDES(mDisplayLock); void forceNextResync() { mLastResyncTime = 0; } // Passes a vsync sample to VsyncController. Returns true if // VsyncController detected that the vsync period changed and false // otherwise. - bool addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod); - void addPresentFence(std::shared_ptr<FenceTime>); + bool addResyncSample(PhysicalDisplayId, nsecs_t timestamp, + std::optional<nsecs_t> hwcVsyncPeriod); + void addPresentFence(PhysicalDisplayId, std::shared_ptr<FenceTime>) EXCLUDES(mDisplayLock); // Layers are registered on creation, and unregistered when the weak reference expires. void registerLayer(Layer*); - void recordLayerHistory(Layer*, nsecs_t presentTime, LayerHistory::LayerUpdateType) - EXCLUDES(mDisplayLock); + void recordLayerHistory(int32_t id, const LayerProps& layerProps, nsecs_t presentTime, + LayerHistory::LayerUpdateType) EXCLUDES(mDisplayLock); void setModeChangePending(bool pending); void setDefaultFrameRateCompatibility(Layer*); void deregisterLayer(Layer*); @@ -215,22 +233,28 @@ public: // Indicates that touch interaction is taking place. void onTouchHint(); - void setDisplayPowerMode(hal::PowerMode powerMode); + void setDisplayPowerMode(PhysicalDisplayId, hal::PowerMode powerMode) + REQUIRES(kMainThreadContext); - VsyncSchedule& getVsyncSchedule() { return *mVsyncSchedule; } + std::shared_ptr<const VsyncSchedule> getVsyncSchedule( + std::optional<PhysicalDisplayId> idOpt = std::nullopt) const EXCLUDES(mDisplayLock); + std::shared_ptr<VsyncSchedule> getVsyncSchedule( + std::optional<PhysicalDisplayId> idOpt = std::nullopt) EXCLUDES(mDisplayLock) { + return std::const_pointer_cast<VsyncSchedule>( + static_cast<const Scheduler*>(this)->getVsyncSchedule(idOpt)); + } // Returns true if a given vsync timestamp is considered valid vsync // for a given uid bool isVsyncValid(TimePoint expectedVsyncTimestamp, uid_t uid) const; - // Checks if a vsync timestamp is in phase for a frame rate - bool isVsyncInPhase(TimePoint timePoint, const Fps frameRate) const; + bool isVsyncInPhase(TimePoint expectedVsyncTime, Fps frameRate) const; void dump(utils::Dumper&) const; void dump(ConnectionHandle, std::string&) const; - void dumpVsync(std::string&) const; + void dumpVsync(std::string&) const EXCLUDES(mDisplayLock); - // Returns the preferred refresh rate and frame rate for the leader display. + // Returns the preferred refresh rate and frame rate for the pacesetter display. FrameRateMode getPreferredDisplayMode(); // Notifies the scheduler about a refresh rate timeline change. @@ -253,12 +277,12 @@ public: // Retrieves the overridden refresh rate for a given uid. std::optional<Fps> getFrameRateOverride(uid_t) const EXCLUDES(mDisplayLock); - Period getLeaderVsyncPeriod() const EXCLUDES(mDisplayLock) { - return leaderSelectorPtr()->getActiveMode().fps.getPeriod(); + Period getPacesetterVsyncPeriod() const EXCLUDES(mDisplayLock) { + return pacesetterSelectorPtr()->getActiveMode().fps.getPeriod(); } - Fps getLeaderRefreshRate() const EXCLUDES(mDisplayLock) { - return leaderSelectorPtr()->getActiveMode().fps; + Fps getPacesetterRefreshRate() const EXCLUDES(mDisplayLock) { + return pacesetterSelectorPtr()->getActiveMode().fps; } // Returns the framerate of the layer with the given sequence ID @@ -288,16 +312,24 @@ private: void touchTimerCallback(TimerState); void displayPowerTimerCallback(TimerState); + void resyncToHardwareVsyncLocked(PhysicalDisplayId, bool allowToEnable, + std::optional<Fps> refreshRate = std::nullopt) + REQUIRES(kMainThreadContext, mDisplayLock); + void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock); void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod); - // 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) + // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified. + // The new `mPacesetterDisplayId` is never `std::nullopt`. + void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = 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); + // Blocks until the pacesetter'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 demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock); + + void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, + std::shared_ptr<VsyncSchedule>) REQUIRES(kMainThreadContext) + EXCLUDES(mDisplayLock); struct Policy; @@ -355,7 +387,6 @@ private: std::atomic<nsecs_t> mLastResyncTime = 0; const FeatureFlags mFeatures; - std::unique_ptr<VsyncSchedule> mVsyncSchedule; // Shifts the VSYNC phase during certain transactions and refresh rate changes. const sp<VsyncModulator> mVsyncModulator; @@ -380,23 +411,35 @@ private: display::PhysicalDisplayMap<PhysicalDisplayId, RefreshRateSelectorPtr> mRefreshRateSelectors GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext); - ftl::Optional<PhysicalDisplayId> mLeaderDisplayId GUARDED_BY(mDisplayLock) + // TODO (b/266715559): Store in the same map as mRefreshRateSelectors. + display::PhysicalDisplayMap<PhysicalDisplayId, std::shared_ptr<VsyncSchedule>> mVsyncSchedules + GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext); + + ftl::Optional<PhysicalDisplayId> mPacesetterDisplayId GUARDED_BY(mDisplayLock) GUARDED_BY(kMainThreadContext); - RefreshRateSelectorPtr leaderSelectorPtr() const EXCLUDES(mDisplayLock) { + RefreshRateSelectorPtr pacesetterSelectorPtr() const EXCLUDES(mDisplayLock) { std::scoped_lock lock(mDisplayLock); - return leaderSelectorPtrLocked(); + return pacesetterSelectorPtrLocked(); } - RefreshRateSelectorPtr leaderSelectorPtrLocked() const REQUIRES(mDisplayLock) { + RefreshRateSelectorPtr pacesetterSelectorPtrLocked() const REQUIRES(mDisplayLock) { ftl::FakeGuard guard(kMainThreadContext); - const RefreshRateSelectorPtr noLeader; - return mLeaderDisplayId - .and_then([this](PhysicalDisplayId leaderId) + const RefreshRateSelectorPtr noPacesetter; + return mPacesetterDisplayId + .and_then([this](PhysicalDisplayId pacesetterId) REQUIRES(mDisplayLock, kMainThreadContext) { - return mRefreshRateSelectors.get(leaderId); + return mRefreshRateSelectors.get(pacesetterId); }) - .value_or(std::cref(noLeader)); + .value_or(std::cref(noPacesetter)); + } + + std::shared_ptr<const VsyncSchedule> getVsyncScheduleLocked( + std::optional<PhysicalDisplayId> idOpt = std::nullopt) const REQUIRES(mDisplayLock); + std::shared_ptr<VsyncSchedule> getVsyncScheduleLocked( + std::optional<PhysicalDisplayId> idOpt = std::nullopt) REQUIRES(mDisplayLock) { + return std::const_pointer_cast<VsyncSchedule>( + static_cast<const Scheduler*>(this)->getVsyncScheduleLocked(idOpt)); } struct Policy { diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index 95201314a4..77875e3b4d 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -161,7 +161,8 @@ protected: */ class VSyncCallbackRegistration { public: - VSyncCallbackRegistration(VSyncDispatch&, VSyncDispatch::Callback, std::string callbackName); + VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch>, VSyncDispatch::Callback, + std::string callbackName); ~VSyncCallbackRegistration(); VSyncCallbackRegistration(VSyncCallbackRegistration&&); @@ -177,7 +178,7 @@ public: CancelResult cancel(); private: - std::reference_wrapper<VSyncDispatch> mDispatch; + std::shared_ptr<VSyncDispatch> mDispatch; VSyncDispatch::CallbackToken mToken; bool mValidToken; }; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 73d52cf986..26389eb8cc 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -215,10 +215,10 @@ void VSyncDispatchTimerQueueEntry::dump(std::string& result) const { } VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, - VSyncTracker& tracker, nsecs_t timerSlack, - nsecs_t minVsyncDistance) + VsyncSchedule::TrackerPtr tracker, + nsecs_t timerSlack, nsecs_t minVsyncDistance) : mTimeKeeper(std::move(tk)), - mTracker(tracker), + mTracker(std::move(tracker)), mTimerSlack(timerSlack), mMinVsyncDistance(minVsyncDistance) {} @@ -255,7 +255,7 @@ void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( } if (it != skipUpdateIt) { - callback->update(mTracker, now); + callback->update(*mTracker, now); } auto const wakeupTime = *callback->wakeupTime(); if (!min || *min > wakeupTime) { @@ -365,10 +365,10 @@ ScheduleResult VSyncDispatchTimerQueue::scheduleLocked(CallbackToken token, auto const rearmImminent = now > mIntendedWakeupTime; if (CC_UNLIKELY(rearmImminent)) { callback->addPendingWorkloadUpdate(scheduleTiming); - return getExpectedCallbackTime(mTracker, now, scheduleTiming); + return getExpectedCallbackTime(*mTracker, now, scheduleTiming); } - const ScheduleResult result = callback->schedule(scheduleTiming, mTracker, now); + const ScheduleResult result = callback->schedule(scheduleTiming, *mTracker, now); if (!result.has_value()) { return {}; } @@ -434,15 +434,15 @@ void VSyncDispatchTimerQueue::dump(std::string& result) const { } } -VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch, +VSyncCallbackRegistration::VSyncCallbackRegistration(std::shared_ptr<VSyncDispatch> dispatch, VSyncDispatch::Callback callback, std::string callbackName) - : mDispatch(dispatch), - mToken(dispatch.registerCallback(std::move(callback), std::move(callbackName))), + : mDispatch(std::move(dispatch)), + mToken(mDispatch->registerCallback(std::move(callback), std::move(callbackName))), mValidToken(true) {} VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncCallbackRegistration&& other) - : mDispatch(other.mDispatch), + : mDispatch(std::move(other.mDispatch)), mToken(std::move(other.mToken)), mValidToken(std::move(other.mValidToken)) { other.mValidToken = false; @@ -457,28 +457,28 @@ VSyncCallbackRegistration& VSyncCallbackRegistration::operator=(VSyncCallbackReg } VSyncCallbackRegistration::~VSyncCallbackRegistration() { - if (mValidToken) mDispatch.get().unregisterCallback(mToken); + if (mValidToken) mDispatch->unregisterCallback(mToken); } ScheduleResult VSyncCallbackRegistration::schedule(VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mValidToken) { return std::nullopt; } - return mDispatch.get().schedule(mToken, scheduleTiming); + return mDispatch->schedule(mToken, scheduleTiming); } ScheduleResult VSyncCallbackRegistration::update(VSyncDispatch::ScheduleTiming scheduleTiming) { if (!mValidToken) { return std::nullopt; } - return mDispatch.get().update(mToken, scheduleTiming); + return mDispatch->update(mToken, scheduleTiming); } CancelResult VSyncCallbackRegistration::cancel() { if (!mValidToken) { return CancelResult::Error; } - return mDispatch.get().cancel(mToken); + return mDispatch->cancel(mToken); } } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index c3af136d66..6499d69969 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -26,11 +26,11 @@ #include <android-base/thread_annotations.h> #include "VSyncDispatch.h" +#include "VsyncSchedule.h" namespace android::scheduler { class TimeKeeper; -class VSyncTracker; // VSyncDispatchTimerQueueEntry is a helper class representing internal state for each entry in // VSyncDispatchTimerQueue hoisted to public for unit testing. @@ -120,8 +120,8 @@ public: // should be grouped into one wakeup. // \param[in] minVsyncDistance The minimum distance between two vsync estimates before the // vsyncs are considered the same vsync event. - VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VSyncTracker&, nsecs_t timerSlack, - nsecs_t minVsyncDistance); + VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper>, VsyncSchedule::TrackerPtr, + nsecs_t timerSlack, nsecs_t minVsyncDistance); ~VSyncDispatchTimerQueue(); CallbackToken registerCallback(Callback, std::string callbackName) final; @@ -148,7 +148,7 @@ private: static constexpr nsecs_t kInvalidTime = std::numeric_limits<int64_t>::max(); std::unique_ptr<TimeKeeper> const mTimeKeeper; - VSyncTracker& mTracker; + VsyncSchedule::TrackerPtr mTracker; nsecs_t const mTimerSlack; nsecs_t const mMinVsyncDistance; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 5a5afd8fa6..e969fdc679 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -31,6 +31,7 @@ #include <android-base/stringprintf.h> #include <cutils/compiler.h> #include <cutils/properties.h> +#include <ftl/concat.h> #include <gui/TraceUtils.h> #include <utils/Log.h> @@ -45,9 +46,10 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; -VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize, +VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) - : mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), + : mId(id), + mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), @@ -57,12 +59,12 @@ VSyncPredictor::VSyncPredictor(nsecs_t idealPeriod, size_t historySize, inline void VSyncPredictor::traceInt64If(const char* name, int64_t value) const { if (CC_UNLIKELY(mTraceOn)) { - ATRACE_INT64(name, value); + traceInt64(name, value); } } inline void VSyncPredictor::traceInt64(const char* name, int64_t value) const { - ATRACE_INT64(name, value); + ATRACE_INT64(ftl::Concat(ftl::truncated<14>(name), " ", mId.value).c_str(), value); } inline size_t VSyncPredictor::next(size_t i) const { @@ -214,8 +216,8 @@ bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) { it->second = {anticipatedPeriod, intercept}; - ALOGV("model update ts: %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, timestamp, - anticipatedPeriod, intercept); + ALOGV("model update ts %" PRIu64 ": %" PRId64 " slope: %" PRId64 " intercept: %" PRId64, + mId.value, timestamp, anticipatedPeriod, intercept); return true; } @@ -272,13 +274,26 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { // update the mLastVsyncSequence for reference point mLastVsyncSequence = getVsyncSequenceLocked(timePoint); - const auto mod = mLastVsyncSequence->seq % mDivisor; - if (mod == 0) { + const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { + if (!mRenderRate) return 0; + + const auto divisor = + RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), + *mRenderRate); + if (divisor <= 1) return 0; + + const int mod = mLastVsyncSequence->seq % divisor; + if (mod == 0) return 0; + + return divisor - mod; + }(); + + if (renderRatePhase == 0) { return mLastVsyncSequence->vsyncTime; } auto const [slope, intercept] = getVSyncPredictionModelLocked(); - const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * (mDivisor - mod); + const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); } @@ -317,10 +332,10 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::setDivisor(unsigned divisor) { - ALOGV("%s: %d", __func__, divisor); +void VSyncPredictor::setRenderRate(Fps fps) { + ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str()); std::lock_guard lock(mMutex); - mDivisor = divisor; + mRenderRate = fps; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { @@ -334,7 +349,7 @@ VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModelLocked() const { } void VSyncPredictor::setPeriod(nsecs_t period) { - ATRACE_CALL(); + ATRACE_FORMAT("%s %s", __func__, to_string(mId).c_str()); traceInt64("VSP-setPeriod", period); std::lock_guard lock(mMutex); @@ -363,7 +378,6 @@ void VSyncPredictor::clearTimestamps() { mTimestamps.clear(); mLastTimestampIndex = 0; } - mLastVsyncSequence.reset(); } bool VSyncPredictor::needsMoreSamples() const { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index d0e3098c5e..c01c44dc6b 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -21,6 +21,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <ui/DisplayId.h> #include "VSyncTracker.h" @@ -29,14 +30,15 @@ namespace android::scheduler { class VSyncPredictor : public VSyncTracker { public: /* + * \param [in] PhysicalDisplayid The display this corresponds to. * \param [in] idealPeriod The initial ideal period to use. * \param [in] historySize The internal amount of entries to store in the model. * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. */ - VSyncPredictor(nsecs_t idealPeriod, size_t historySize, size_t minimumSamplesForPrediction, - uint32_t outlierTolerancePercent); + VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize, + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); @@ -67,7 +69,7 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); - void setDivisor(unsigned divisor) final EXCLUDES(mMutex); + void setRenderRate(Fps) final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); @@ -76,6 +78,8 @@ private: VSyncPredictor& operator=(VSyncPredictor const&) = delete; void clearTimestamps() REQUIRES(mMutex); + const PhysicalDisplayId mId; + inline void traceInt64If(const char* name, int64_t value) const; inline void traceInt64(const char* name, int64_t value) const; @@ -106,7 +110,7 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); - unsigned mDivisor GUARDED_BY(mMutex) = 1; + std::optional<Fps> mRenderRate GUARDED_BY(mMutex); mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); }; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index b5f212e085..2938aa3fb3 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -21,6 +21,8 @@ #include <assert.h> #include <cutils/properties.h> +#include <ftl/concat.h> +#include <gui/TraceUtils.h> #include <log/log.h> #include <utils/Trace.h> @@ -39,12 +41,13 @@ nsecs_t SystemClock::now() const { return systemTime(SYSTEM_TIME_MONOTONIC); } -VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, - size_t pendingFenceLimit, bool supportKernelIdleTimer) - : mClock(std::move(clock)), +VSyncReactor::VSyncReactor(PhysicalDisplayId id, std::unique_ptr<Clock> clock, + VSyncTracker& tracker, size_t pendingFenceLimit, + bool supportKernelIdleTimer) + : mId(id), + mClock(std::move(clock)), mTracker(tracker), mPendingLimit(pendingFenceLimit), - // TODO(adyabr): change mSupportKernelIdleTimer when the active display changes mSupportKernelIdleTimer(supportKernelIdleTimer) {} VSyncReactor::~VSyncReactor() = default; @@ -114,7 +117,7 @@ void VSyncReactor::updateIgnorePresentFencesInternal() { } void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { - ATRACE_CALL(); + ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodConfirmationInProgress = true; mPeriodTransitioningTo = newPeriod; mMoreSamplesNeeded = true; @@ -122,18 +125,18 @@ void VSyncReactor::startPeriodTransitionInternal(nsecs_t newPeriod) { } void VSyncReactor::endPeriodTransition() { - ATRACE_CALL(); + ATRACE_FORMAT("%s %" PRIu64, __func__, mId.value); mPeriodTransitioningTo.reset(); mPeriodConfirmationInProgress = false; mLastHwVsync.reset(); } -void VSyncReactor::startPeriodTransition(nsecs_t period) { - ATRACE_INT64("VSR-startPeriodTransition", period); +void VSyncReactor::startPeriodTransition(nsecs_t period, bool force) { + ATRACE_INT64(ftl::Concat("VSR-", __func__, " ", mId.value).c_str(), period); std::lock_guard lock(mMutex); mLastHwVsync.reset(); - if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod()) { + if (!mSupportKernelIdleTimer && period == mTracker.currentPeriod() && !force) { endPeriodTransition(); setIgnorePresentFencesInternal(false); mMoreSamplesNeeded = false; @@ -181,7 +184,7 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> std::lock_guard lock(mMutex); if (periodConfirmed(timestamp, hwcVsyncPeriod)) { - ATRACE_NAME("VSR: period confirmed"); + ATRACE_FORMAT("VSR %" PRIu64 ": period confirmed", mId.value); if (mPeriodTransitioningTo) { mTracker.setPeriod(*mPeriodTransitioningTo); *periodFlushed = true; @@ -195,12 +198,12 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> endPeriodTransition(); mMoreSamplesNeeded = mTracker.needsMoreSamples(); } else if (mPeriodConfirmationInProgress) { - ATRACE_NAME("VSR: still confirming period"); + ATRACE_FORMAT("VSR %" PRIu64 ": still confirming period", mId.value); mLastHwVsync = timestamp; mMoreSamplesNeeded = true; *periodFlushed = false; } else { - ATRACE_NAME("VSR: adding sample"); + ATRACE_FORMAT("VSR %" PRIu64 ": adding sample", mId.value); *periodFlushed = false; mTracker.addVsyncTimestamp(timestamp); mMoreSamplesNeeded = mTracker.needsMoreSamples(); diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 4501487392..f2302422ad 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -22,6 +22,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <ui/DisplayId.h> #include <ui/FenceTime.h> #include <scheduler/TimeKeeper.h> @@ -37,14 +38,14 @@ class VSyncTracker; // TODO (b/145217110): consider renaming. class VSyncReactor : public VsyncController { public: - VSyncReactor(std::unique_ptr<Clock> clock, VSyncTracker& tracker, size_t pendingFenceLimit, - bool supportKernelIdleTimer); + VSyncReactor(PhysicalDisplayId, std::unique_ptr<Clock> clock, VSyncTracker& tracker, + size_t pendingFenceLimit, bool supportKernelIdleTimer); ~VSyncReactor(); bool addPresentFence(std::shared_ptr<FenceTime>) final; void setIgnorePresentFences(bool ignore) final; - void startPeriodTransition(nsecs_t period) final; + void startPeriodTransition(nsecs_t period, bool force) final; bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed) final; @@ -61,6 +62,7 @@ private: bool periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_t> hwcVsyncPeriod) REQUIRES(mMutex); + const PhysicalDisplayId mId; std::unique_ptr<Clock> const mClock; VSyncTracker& mTracker; size_t const mPendingLimit; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 8d1629faae..bc0e3bcbb2 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -80,15 +80,16 @@ public: virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; /* - * Sets a divisor on the rate (which is a multiplier of the period). + * Sets a render rate on the tracker. If the render rate is not a divisor + * of the period, the render rate is ignored until the period changes. * 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 + * return vsyncs according to the render rate set. Setting a render rate 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. + * \param [in] Fps The render rate the tracker should operate at. */ - virtual void setDivisor(unsigned divisor) = 0; + virtual void setRenderRate(Fps) = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 726a420649..917789934a 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -63,8 +63,9 @@ public: * itself. The controller will end the period transition internally. * * \param [in] period The period that the system is changing into. + * \param [in] force True to recalibrate even if period matches the existing period. */ - virtual void startPeriodTransition(nsecs_t period) = 0; + virtual void startPeriodTransition(nsecs_t period, bool force) = 0; /* * Tells the tracker to stop using present fences to get a vsync signal. diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.cpp b/services/surfaceflinger/Scheduler/VsyncModulator.cpp index c9af4c27fe..586357f50a 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.cpp +++ b/services/surfaceflinger/Scheduler/VsyncModulator.cpp @@ -187,9 +187,9 @@ void VsyncModulator::binderDied(const wp<IBinder>& who) { static_cast<void>(updateVsyncConfigLocked()); } -bool VsyncModulator::isVsyncConfigDefault() const { +bool VsyncModulator::isVsyncConfigEarly() const { std::lock_guard<std::mutex> lock(mMutex); - return getNextVsyncConfigType() == VsyncConfigType::Late; + return getNextVsyncConfigType() != VsyncConfigType::Late; } } // namespace android::scheduler diff --git a/services/surfaceflinger/Scheduler/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h index dc4dafd979..be0d3348b5 100644 --- a/services/surfaceflinger/Scheduler/VsyncModulator.h +++ b/services/surfaceflinger/Scheduler/VsyncModulator.h @@ -53,8 +53,12 @@ public: explicit VsyncModulator(const VsyncConfigSet&, Now = Clock::now); + bool isVsyncConfigEarly() const EXCLUDES(mMutex); + VsyncConfig getVsyncConfig() const EXCLUDES(mMutex); + void cancelRefreshRateChange() { mRefreshRateChangePending = false; } + [[nodiscard]] VsyncConfig setVsyncConfigSet(const VsyncConfigSet&) EXCLUDES(mMutex); // Changes offsets in response to transaction flags or commit. @@ -72,8 +76,6 @@ public: [[nodiscard]] VsyncConfigOpt onDisplayRefresh(bool usedGpuComposition); - [[nodiscard]] bool isVsyncConfigDefault() const; - protected: // Called from unit tests as well void binderDied(const wp<IBinder>&) override EXCLUDES(mMutex); diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 524555681b..84671aea0d 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -42,8 +42,8 @@ class VsyncSchedule::PredictedVsyncTracer { } public: - explicit PredictedVsyncTracer(VsyncDispatch& dispatch) - : mRegistration(dispatch, makeVsyncCallback(), __func__) { + explicit PredictedVsyncTracer(std::shared_ptr<VsyncDispatch> dispatch) + : mRegistration(std::move(dispatch), makeVsyncCallback(), __func__) { schedule(); } @@ -54,16 +54,19 @@ private: VSyncCallbackRegistration mRegistration; }; -VsyncSchedule::VsyncSchedule(FeatureFlags features) - : mTracker(createTracker()), - mDispatch(createDispatch(*mTracker)), - mController(createController(*mTracker, features)), +VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features) + : mId(id), + mTracker(createTracker(id)), + mDispatch(createDispatch(mTracker)), + mController(createController(id, *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) - ? std::make_unique<PredictedVsyncTracer>(*mDispatch) + ? std::make_unique<PredictedVsyncTracer>(mDispatch) : nullptr) {} -VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller) - : mTracker(std::move(tracker)), +VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, TrackerPtr tracker, DispatchPtr dispatch, + ControllerPtr controller) + : mId(id), + mTracker(std::move(tracker)), mDispatch(std::move(dispatch)), mController(std::move(controller)) {} @@ -95,45 +98,46 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) { // TODO(b/144707443): Tune constants. constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs(); constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; - return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction, - kDiscardOutlierPercent); + return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize, + kMinSamplesForPrediction, kDiscardOutlierPercent); } -VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) { +VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { using namespace std::chrono_literals; // TODO(b/144707443): Tune constants. constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us; constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms; - return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker, + return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), std::move(tracker), kGroupDispatchWithin.count(), kSnapToSameVsyncWithin.count()); } -VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker, +VsyncSchedule::ControllerPtr VsyncSchedule::createController(PhysicalDisplayId id, + VsyncTracker& tracker, FeatureFlags features) { // TODO(b/144707443): Tune constants. constexpr size_t kMaxPendingFences = 20; const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer); - auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker, + auto reactor = std::make_unique<VSyncReactor>(id, std::make_unique<SystemClock>(), tracker, kMaxPendingFences, hasKernelIdleTimer); reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences)); return reactor; } -void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period) { +void VsyncSchedule::startPeriodTransition(ISchedulerCallback& callback, Period period, bool force) { std::lock_guard<std::mutex> lock(mHwVsyncLock); - mController->startPeriodTransition(period.ns()); + mController->startPeriodTransition(period.ns(), force); enableHardwareVsyncLocked(callback); } @@ -165,17 +169,23 @@ void VsyncSchedule::enableHardwareVsync(ISchedulerCallback& callback) { void VsyncSchedule::enableHardwareVsyncLocked(ISchedulerCallback& callback) { if (mHwVsyncState == HwVsyncState::Disabled) { getTracker().resetModel(); - callback.setVsyncEnabled(true); + callback.setVsyncEnabled(mId, true); mHwVsyncState = HwVsyncState::Enabled; } } void VsyncSchedule::disableHardwareVsync(ISchedulerCallback& callback, bool disallow) { std::lock_guard<std::mutex> lock(mHwVsyncLock); - if (mHwVsyncState == HwVsyncState::Enabled) { - callback.setVsyncEnabled(false); + switch (mHwVsyncState) { + case HwVsyncState::Enabled: + callback.setVsyncEnabled(mId, false); + [[fallthrough]]; + case HwVsyncState::Disabled: + mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled; + break; + case HwVsyncState::Disallowed: + break; } - mHwVsyncState = disallow ? HwVsyncState::Disallowed : HwVsyncState::Disabled; } bool VsyncSchedule::isHardwareVsyncAllowed(bool makeAllowed) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index d88f1d1f0b..763d058e28 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -20,13 +20,16 @@ #include <string> #include <ThreadContext.h> +#include <android-base/thread_annotations.h> #include <ftl/enum.h> #include <ftl/optional.h> #include <scheduler/Features.h> #include <scheduler/Time.h> +#include <ui/DisplayId.h> namespace android { class EventThreadTest; +class VsyncScheduleTest; } namespace android::fuzz { @@ -48,7 +51,7 @@ using VsyncTracker = VSyncTracker; // Schedule that synchronizes to hardware VSYNC of a physical display. class VsyncSchedule { public: - explicit VsyncSchedule(FeatureFlags); + VsyncSchedule(PhysicalDisplayId, FeatureFlags); ~VsyncSchedule(); Period period() const; @@ -59,7 +62,9 @@ public: // enable hardware VSYNCs in order to calibrate. // // \param [in] period The period that the system is changing into. - void startPeriodTransition(ISchedulerCallback&, Period period); + // \param [in] force True to force a transition even if it is not a + // change. + void startPeriodTransition(ISchedulerCallback&, Period period, bool force); // Pass a VSYNC sample to VsyncController. Return true if // VsyncController detected that the VSYNC period changed. Enable or disable @@ -72,8 +77,13 @@ public: VsyncTracker& getTracker() { return *mTracker; } VsyncController& getController() { return *mController; } + // TODO(b/185535769): Once these are hidden behind the API, they may no + // longer need to be shared_ptrs. + using DispatchPtr = std::shared_ptr<VsyncDispatch>; + using TrackerPtr = std::shared_ptr<VsyncTracker>; + // TODO(b/185535769): Remove once VsyncSchedule owns all registrations. - VsyncDispatch& getDispatch() { return *mDispatch; } + DispatchPtr getDispatch() { return mDispatch; } void dump(std::string&) const; @@ -82,7 +92,8 @@ public: void enableHardwareVsync(ISchedulerCallback&) EXCLUDES(mHwVsyncLock); // Disable hardware VSYNCs. If `disallow` is true, future calls to - // enableHardwareVsync are ineffective until allowHardwareVsync is called. + // enableHardwareVsync are ineffective until isHardwareVsyncAllowed is + // called with `makeAllowed` set to true. void disableHardwareVsync(ISchedulerCallback&, bool disallow) EXCLUDES(mHwVsyncLock); // If true, enableHardwareVsync can enable hardware VSYNC (if not already @@ -93,21 +104,21 @@ public: bool getPendingHardwareVsyncState() const REQUIRES(kMainThreadContext); +protected: + using ControllerPtr = std::unique_ptr<VsyncController>; + + // For tests. + VsyncSchedule(PhysicalDisplayId, TrackerPtr, DispatchPtr, ControllerPtr); + private: friend class TestableScheduler; friend class android::EventThreadTest; + friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - using TrackerPtr = std::unique_ptr<VsyncTracker>; - using DispatchPtr = std::unique_ptr<VsyncDispatch>; - using ControllerPtr = std::unique_ptr<VsyncController>; - - // For tests. - VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr); - - static TrackerPtr createTracker(); - static DispatchPtr createDispatch(VsyncTracker&); - static ControllerPtr createController(VsyncTracker&, FeatureFlags); + static TrackerPtr createTracker(PhysicalDisplayId); + static DispatchPtr createDispatch(TrackerPtr); + static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); void enableHardwareVsyncLocked(ISchedulerCallback&) REQUIRES(mHwVsyncLock); @@ -135,6 +146,7 @@ private: class PredictedVsyncTracer; using TracerPtr = std::unique_ptr<PredictedVsyncTracer>; + const PhysicalDisplayId mId; const TrackerPtr mTracker; const DispatchPtr mDispatch; const ControllerPtr mController; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index b67188b1e0..63b7f75c84 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -292,6 +292,13 @@ HdrCapabilities filterOut4k30(const HdrCapabilities& displayHdrCapabilities) { displayHdrCapabilities.getDesiredMinLuminance()}; } +uint32_t getLayerIdFromSurfaceControl(sp<SurfaceControl> surfaceControl) { + if (!surfaceControl) { + return UNASSIGNED_LAYER_ID; + } + return LayerHandle::getLayerId(surfaceControl->getHandle()); +} + } // namespace anonymous // --------------------------------------------------------------------------- @@ -456,6 +463,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI android::hardware::details::setTrebleTestingOverride(true); } + // TODO (b/270966065) Update the HWC based refresh rate overlay to support spinner 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); @@ -473,9 +481,9 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true), .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)}; mLayerLifecycleManagerEnabled = - base::GetBoolProperty("debug.sf.enable_layer_lifecycle_manager"s, false); + base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, false); mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled || - base::GetBoolProperty("debug.sf.enable_legacy_frontend"s, true); + base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false); } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -496,14 +504,13 @@ void SurfaceFlinger::binderDied(const wp<IBinder>&) { // the window manager died on us. prepare its eulogy. mBootFinished = false; - // Sever the link to inputflinger since it's gone as well. - static_cast<void>(mScheduler->schedule( - [this] { mInputFlinger.clear(); })); + static_cast<void>(mScheduler->schedule([this]() FTL_FAKE_GUARD(kMainThreadContext) { + // Sever the link to inputflinger since it's gone as well. + mInputFlinger.clear(); - // restore initial conditions (default device unblank, etc) - initializeDisplays(); + initializeDisplays(); + })); - // restart the boot-animation startBootAnim(); } @@ -872,7 +879,9 @@ void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { mDrawingState = mCurrentState; onActiveDisplayChangedLocked(nullptr, *display); - initializeDisplays(); + + static_cast<void>(mScheduler->schedule( + [this]() FTL_FAKE_GUARD(kMainThreadContext) { initializeDisplays(); })); mPowerAdvisor->init(); @@ -1130,21 +1139,33 @@ status_t SurfaceFlinger::getDynamicDisplayInfoFromToken(const sp<IBinder>& displ return NO_ERROR; } -status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* outStats) { +status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, + DisplayStatInfo* outStats) { if (!outStats) { return BAD_VALUE; } - const auto& schedule = mScheduler->getVsyncSchedule(); - outStats->vsyncTime = schedule.vsyncDeadlineAfter(TimePoint::now()).ns(); - outStats->vsyncPeriod = schedule.period().ns(); + std::optional<PhysicalDisplayId> displayIdOpt; + { + Mutex::Autolock lock(mStateLock); + displayIdOpt = getPhysicalDisplayIdLocked(displayToken); + } + + if (!displayIdOpt) { + ALOGE("%s: Invalid physical display token %p", __func__, displayToken.get()); + return NAME_NOT_FOUND; + } + const auto schedule = mScheduler->getVsyncSchedule(displayIdOpt); + outStats->vsyncTime = schedule->vsyncDeadlineAfter(TimePoint::now()).ns(); + outStats->vsyncPeriod = schedule->period().ns(); return NO_ERROR; } void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, bool force) { ATRACE_CALL(); - auto display = getDisplayDeviceLocked(request.mode.modePtr->getPhysicalDisplayId()); + const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); + const auto display = getDisplayDeviceLocked(displayId); if (!display) { ALOGW("%s: display is no longer valid", __func__); return; @@ -1157,23 +1178,25 @@ void SurfaceFlinger::setDesiredActiveMode(display::DisplayModeRequest&& request, force)) { case DisplayDevice::DesiredActiveModeAction::InitiateDisplayModeSwitch: // Set the render rate as setDesiredActiveMode updated it. - mScheduler->setRenderRate(display->refreshRateSelector().getActiveMode().fps); + mScheduler->setRenderRate(displayId, + display->refreshRateSelector().getActiveMode().fps); // Schedule a new frame to initiate the display mode switch. scheduleComposite(FrameHint::kNone); // Start receiving vsync samples now, so that we can detect a period // switch. - mScheduler->resyncToHardwareVsync(true, mode.modePtr->getFps()); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, + mode.modePtr->getFps()); + // As we called to set period, we will call to onRefreshRateChangeCompleted once // VsyncController model is locked. - mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeInitiated); - + mScheduler->modulateVsync(displayId, &VsyncModulator::onRefreshRateChangeInitiated); updatePhaseConfiguration(mode.fps); mScheduler->setModeChangePending(true); break; case DisplayDevice::DesiredActiveModeAction::InitiateRenderRateSwitch: - mScheduler->setRenderRate(mode.fps); + mScheduler->setRenderRate(displayId, mode.fps); updatePhaseConfiguration(mode.fps); mRefreshRateStats->setRefreshRate(mode.fps); if (display->getPhysicalId() == mActiveDisplayId && emitEvent) { @@ -1289,11 +1312,14 @@ void SurfaceFlinger::clearDesiredActiveModeState(const sp<DisplayDevice>& displa } void SurfaceFlinger::desiredActiveModeChangeDone(const sp<DisplayDevice>& display) { - const auto displayFps = display->getDesiredActiveMode()->modeOpt->modePtr->getFps(); - const auto renderFps = display->getDesiredActiveMode()->modeOpt->fps; + const auto desiredActiveMode = display->getDesiredActiveMode(); + const auto& modeOpt = desiredActiveMode->modeOpt; + const auto displayId = modeOpt->modePtr->getPhysicalDisplayId(); + const auto displayFps = modeOpt->modePtr->getFps(); + const auto renderFps = modeOpt->fps; clearDesiredActiveModeState(display); - mScheduler->resyncToHardwareVsync(true, displayFps); - mScheduler->setRenderRate(renderFps); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, displayFps); + mScheduler->setRenderRate(displayId, renderFps); updatePhaseConfiguration(renderFps); } @@ -1571,8 +1597,8 @@ status_t SurfaceFlinger::getHdrConversionCapabilities( const auto aidlConversionCapability = getHwComposer().getHdrConversionCapabilities(); for (auto capability : aidlConversionCapability) { gui::HdrConversionCapability tempCapability; - tempCapability.sourceType = static_cast<int>(capability.sourceType.hdr); - tempCapability.outputType = static_cast<int>(capability.outputType->hdr); + tempCapability.sourceType = static_cast<int>(capability.sourceType); + tempCapability.outputType = static_cast<int>(capability.outputType); tempCapability.addsLatency = capability.addsLatency; hdrConversionCapabilities->push_back(tempCapability); } @@ -2031,27 +2057,16 @@ nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const { void SurfaceFlinger::onComposerHalVsync(hal::HWDisplayId hwcDisplayId, int64_t timestamp, std::optional<hal::VsyncPeriodNanos> vsyncPeriod) { - const std::string tracePeriod = [vsyncPeriod]() { - if (ATRACE_ENABLED() && vsyncPeriod) { - std::stringstream ss; - ss << "(" << *vsyncPeriod << ")"; - return ss.str(); - } - return std::string(); - }(); - ATRACE_FORMAT("onComposerHalVsync%s", tracePeriod.c_str()); + ATRACE_NAME(vsyncPeriod + ? ftl::Concat(__func__, ' ', hwcDisplayId, ' ', *vsyncPeriod, "ns").c_str() + : ftl::Concat(__func__, ' ', hwcDisplayId).c_str()); Mutex::Autolock lock(mStateLock); - - if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp); - displayIdOpt != mActiveDisplayId) { - // Ignore VSYNC for invalid/inactive displays. - return; - } - - const bool periodFlushed = mScheduler->addResyncSample(timestamp, vsyncPeriod); - if (periodFlushed) { - mScheduler->modulateVsync(&VsyncModulator::onRefreshRateChangeCompleted); + if (const auto displayIdOpt = getHwComposer().onVsync(hwcDisplayId, timestamp)) { + if (mScheduler->addResyncSample(*displayIdOpt, timestamp, vsyncPeriod)) { + // period flushed + mScheduler->modulateVsync(displayIdOpt, &VsyncModulator::onRefreshRateChangeCompleted); + } } } @@ -2092,23 +2107,40 @@ void SurfaceFlinger::onComposerHalVsyncIdle(hal::HWDisplayId) { mScheduler->forceNextResync(); } -void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData&) { - // TODO(b/202734676) update refresh rate value on the RefreshRateOverlay +void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) { + ATRACE_CALL(); + if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) { + const Fps fps = Fps::fromPeriodNsecs(data.vsyncPeriodNanos); + ATRACE_FORMAT("%s Fps %d", __func__, fps.getIntValue()); + static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { + { + { + const auto display = getDisplayDeviceLocked(*displayId); + FTL_FAKE_GUARD(kMainThreadContext, + display->updateRefreshRateOverlayRate(fps, + display->getActiveMode() + .fps, + /* setByHwc */ true)); + } + } + })); + } } -void SurfaceFlinger::setVsyncEnabled(bool enabled) { - ATRACE_CALL(); +void SurfaceFlinger::setVsyncEnabled(PhysicalDisplayId id, bool enabled) { + const char* const whence = __func__; + ATRACE_FORMAT("%s (%d) for %" PRIu64, whence, enabled, id.value); // On main thread to avoid race conditions with display power state. static_cast<void>(mScheduler->schedule([=]() FTL_FAKE_GUARD(mStateLock) { { ftl::FakeGuard guard(kMainThreadContext); - mScheduler->getVsyncSchedule().setPendingHardwareVsyncState(enabled); + mScheduler->getVsyncSchedule(id)->setPendingHardwareVsyncState(enabled); } - if (const auto display = getDefaultDisplayDeviceLocked(); - display && display->isPoweredOn()) { - setHWCVsyncEnabled(display->getPhysicalId(), enabled); + ATRACE_FORMAT("%s (%d) for %" PRIu64 " (main thread)", whence, enabled, id.value); + if (const auto display = getDisplayDeviceLocked(id); display && display->isPoweredOn()) { + setHWCVsyncEnabled(id, enabled); } })); } @@ -2135,13 +2167,13 @@ bool SurfaceFlinger::isFencePending(const FenceTimePtr& fence, int graceTimeMs) TimePoint SurfaceFlinger::calculateExpectedPresentTime(TimePoint frameTime) const { const auto& schedule = mScheduler->getVsyncSchedule(); - const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(frameTime); + const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(frameTime); if (mScheduler->vsyncModulator().getVsyncConfig().sfOffset > 0) { return vsyncDeadline; } // Inflate the expected present time if we're targeting the next vsync. - return vsyncDeadline + schedule.period(); + return vsyncDeadline + schedule->period(); } void SurfaceFlinger::configure() FTL_FAKE_GUARD(kMainThreadContext) { @@ -2171,6 +2203,38 @@ bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, LifecycleUpdate return mustComposite; } +void SurfaceFlinger::updateLayerHistory(const frontend::LayerSnapshot& snapshot) { + using Changes = frontend::RequestedLayerState::Changes; + if (snapshot.path.isClone() || + !snapshot.changes.any(Changes::FrameRate | Changes::Buffer | Changes::Animation)) { + return; + } + + const auto layerProps = scheduler::LayerProps{ + .visible = snapshot.isVisible, + .bounds = snapshot.geomLayerBounds, + .transform = snapshot.geomLayerTransform, + .setFrameRateVote = snapshot.frameRate, + .frameRateSelectionPriority = snapshot.frameRateSelectionPriority, + }; + + auto it = mLegacyLayers.find(snapshot.sequence); + LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s", + snapshot.getDebugString().c_str()); + + if (snapshot.changes.test(Changes::Animation)) { + it->second->recordLayerHistoryAnimationTx(layerProps); + } + + if (snapshot.changes.test(Changes::FrameRate)) { + it->second->setFrameRateForLayerTree(snapshot.frameRate, layerProps); + } + + if (snapshot.changes.test(Changes::Buffer)) { + it->second->recordLayerHistoryBufferUpdate(layerProps); + } +} + bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed, bool& outTransactionsAreEmpty) { using Changes = frontend::RequestedLayerState::Changes; @@ -2192,23 +2256,27 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& upda mLayerLifecycleManager.getDestroyedLayers()); } - applyAndCommitDisplayTransactionStates(update.transactions); + bool mustComposite = false; + mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions); { ATRACE_NAME("LayerSnapshotBuilder:update"); - frontend::LayerSnapshotBuilder::Args args{.root = mLayerHierarchyBuilder.getHierarchy(), - .layerLifecycleManager = mLayerLifecycleManager, - .displays = mFrontEndDisplayInfos, - .displayChanges = mFrontEndDisplayInfosChanged, - .globalShadowSettings = - mDrawingState.globalShadowSettings, - .supportsBlur = mSupportsBlur, - .forceFullDamage = mForceFullDamage}; + frontend::LayerSnapshotBuilder::Args + args{.root = mLayerHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLayerLifecycleManager, + .displays = mFrontEndDisplayInfos, + .displayChanges = mFrontEndDisplayInfosChanged, + .globalShadowSettings = mDrawingState.globalShadowSettings, + .supportsBlur = mSupportsBlur, + .forceFullDamage = mForceFullDamage, + .supportedLayerGenericMetadata = + getHwComposer().getSupportedLayerGenericMetadata(), + .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()}; mLayerSnapshotBuilder.update(args); } if (mLayerLifecycleManager.getGlobalChanges().any(Changes::Geometry | Changes::Input | - Changes::Hierarchy)) { + Changes::Hierarchy | Changes::Visibility)) { mUpdateInputInfo = true; } if (mLayerLifecycleManager.getGlobalChanges().any(Changes::VisibleRegion | Changes::Hierarchy | @@ -2216,23 +2284,38 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& upda mVisibleRegionsDirty = true; } outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0; - const bool mustComposite = mLayerLifecycleManager.getGlobalChanges().get() != 0; - { - ATRACE_NAME("LLM:commitChanges"); - mLayerLifecycleManager.commitChanges(); - } + mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0; + bool newDataLatched = false; if (!mLegacyFrontEndEnabled) { ATRACE_NAME("DisplayCallbackAndStatsUpdates"); applyTransactions(update.transactions, vsyncId); + const nsecs_t latchTime = systemTime(); + bool unused = false; + + for (auto& layer : mLayerLifecycleManager.getLayers()) { + if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) && + layer->bgColorLayer) { + sp<Layer> bgColorLayer = getFactory().createEffectLayer( + LayerCreationArgs(this, nullptr, layer->name, + ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(), + std::make_optional(layer->parentId), true)); + mLegacyLayers[bgColorLayer->sequence] = bgColorLayer; + } + if (!layer->hasReadyFrame()) continue; - bool newDataLatched = false; - for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { - if (!snapshot->changes.test(Changes::Buffer)) continue; - auto it = mLegacyLayers.find(snapshot->sequence); + auto it = mLegacyLayers.find(layer->id); LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s", - snapshot->getDebugString().c_str()); + layer->getDebugString().c_str()); + const bool bgColorOnly = + !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID); + it->second->latchBufferImpl(unused, latchTime, bgColorOnly); mLayersWithQueuedFrames.emplace(it->second); + } + + for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { + updateLayerHistory(*snapshot); + if (!snapshot->hasReadyFrame) continue; newDataLatched = true; if (!snapshot->isVisible) break; @@ -2245,13 +2328,20 @@ bool SurfaceFlinger::updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& upda mLegacyLayers.erase(destroyedLayer->id); } + { + ATRACE_NAME("LLM:commitChanges"); + mLayerLifecycleManager.commitChanges(); + } + + commitTransactions(); + // enter boot animation on first buffer latch if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) { ALOGI("Enter boot animation"); mBootStage = BootStage::BOOTANIMATION; } - commitTransactions(); } + mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched; return mustComposite; } @@ -2272,7 +2362,7 @@ bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expe ticks<std::milli, float>(mExpectedPresentTime - TimePoint::now()), mExpectedPresentTime == expectedVsyncTime ? "" : " (adjusted)"); - const Period vsyncPeriod = mScheduler->getVsyncSchedule().period(); + 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 of 1ms @@ -2522,7 +2612,7 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule().period(); + const auto prevVsyncTime = mExpectedPresentTime - mScheduler->getVsyncSchedule()->period(); const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration; refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration; @@ -2605,7 +2695,7 @@ void SurfaceFlinger::composite(TimePoint frameTime, VsyncId vsyncId) // TODO(b/160583065): Enable skip validation when SF caches all client composition layers. const bool hasGpuUseOrReuse = mCompositionCoverage.any(CompositionCoverage::Gpu | CompositionCoverage::GpuReuse); - mScheduler->modulateVsync(&VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse); + mScheduler->modulateVsync({}, &VsyncModulator::onDisplayRefresh, hasGpuUseOrReuse); mLayersWithQueuedFrames.clear(); if (mLayerTracingEnabled && mLayerTracing.flagIsSet(LayerTracing::TRACE_COMPOSITION)) { @@ -2749,9 +2839,9 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { ? mPresentLatencyTracker.trackPendingFrame(compositeTime, presentFenceTime) : Duration::zero(); - const auto& schedule = mScheduler->getVsyncSchedule(); - const TimePoint vsyncDeadline = schedule.vsyncDeadlineAfter(presentTime); - const Period vsyncPeriod = schedule.period(); + 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, @@ -2831,15 +2921,19 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { mTimeStats->incrementTotalFrames(); mTimeStats->setPresentFenceGlobal(presentFenceTime); - const bool isInternalDisplay = defaultDisplay && - FTL_FAKE_GUARD(mStateLock, mPhysicalDisplays) - .get(defaultDisplay->getPhysicalId()) - .transform(&PhysicalDisplay::isInternal) - .value_or(false); - - if (isInternalDisplay && defaultDisplay && defaultDisplay->getPowerMode() == hal::PowerMode::ON && - presentFenceTime->isValid()) { - mScheduler->addPresentFence(std::move(presentFenceTime)); + { + ftl::FakeGuard guard(mStateLock); + for (const auto& [id, physicalDisplay] : mPhysicalDisplays) { + if (auto displayDevice = getDisplayDeviceLocked(id); + displayDevice && displayDevice->isPoweredOn() && physicalDisplay.isInternal()) { + auto presentFenceTimeI = defaultDisplay && defaultDisplay->getPhysicalId() == id + ? std::move(presentFenceTime) + : std::make_shared<FenceTime>(getHwComposer().getPresentFence(id)); + if (presentFenceTimeI->isValid()) { + mScheduler->addPresentFence(id, std::move(presentFenceTimeI)); + } + } + } } const bool isDisplayConnected = @@ -2847,7 +2941,7 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { if (!hasSyncFramework) { if (isDisplayConnected && defaultDisplay->isPoweredOn()) { - mScheduler->enableHardwareVsync(); + mScheduler->enableHardwareVsync(defaultDisplay->getPhysicalId()); } } @@ -2890,14 +2984,19 @@ void SurfaceFlinger::postComposition(nsecs_t callTime) { } // We avoid any reverse traversal upwards so this shouldn't be too expensive - mDrawingState.traverse([&](Layer* layer) { + traverseLegacyLayers([&](Layer* layer) { if (!layer->hasTrustedPresentationListener()) { return; } - const std::optional<const DisplayDevice*> displayOpt = - layerStackToDisplay.get(layer->getLayerSnapshot()->outputFilter.layerStack); + const frontend::LayerSnapshot* snapshot = (mLayerLifecycleManagerEnabled) + ? mLayerSnapshotBuilder.getSnapshot(layer->sequence) + : layer->getLayerSnapshot(); + std::optional<const DisplayDevice*> displayOpt = std::nullopt; + if (snapshot) { + displayOpt = layerStackToDisplay.get(snapshot->outputFilter.layerStack); + } const DisplayDevice* display = displayOpt.value_or(nullptr); - layer->updateTrustedPresentationState(display, layer->getLayerSnapshot(), + layer->updateTrustedPresentationState(display, snapshot, nanoseconds_to_milliseconds(callTime), false); }); } @@ -2958,7 +3057,7 @@ void SurfaceFlinger::commitTransactions() { // so we can call commitTransactionsLocked unconditionally. // We clear the flags with mStateLock held to guarantee that // mCurrentState won't change until the transaction is committed. - mScheduler->modulateVsync(&VsyncModulator::onTransactionCommit); + mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit); commitTransactionsLocked(clearTransactionFlags(eTransactionMask)); mDebugInTransaction = 0; @@ -3330,7 +3429,7 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, } if (display->isVirtual()) { - display->adjustRefreshRate(mScheduler->getLeaderRefreshRate()); + display->adjustRefreshRate(mScheduler->getPacesetterRefreshRate()); } mDisplays.try_emplace(displayToken, std::move(display)); @@ -3399,7 +3498,7 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, // TODO(b/175678251) Call a listener instead. if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) { - updateActiveDisplayVsyncLocked(*display); + resetPhaseConfiguration(display->getActiveMode().fps); } } return; @@ -3433,9 +3532,11 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, } } -void SurfaceFlinger::updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay) { +void SurfaceFlinger::resetPhaseConfiguration(Fps refreshRate) { + // Cancel the pending refresh rate change, if any, before updating the phase configuration. + mScheduler->vsyncModulator().cancelRefreshRateChange(); + mVsyncConfiguration->reset(); - const Fps refreshRate = activeDisplay.getActiveMode().fps; updatePhaseConfiguration(refreshRate); mRefreshRateStats->setRefreshRate(refreshRate); } @@ -3581,6 +3682,10 @@ void SurfaceFlinger::commitTransactionsLocked(uint32_t transactionFlags) { }); } + if (transactionFlags & eInputInfoUpdateNeeded) { + mUpdateInputInfo = true; + } + doCommitTransactions(); } @@ -3784,10 +3889,9 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, std::move(modulatorPtr)); - mScheduler->createVsyncSchedule(features); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); - setVsyncEnabled(false); + setVsyncEnabled(display->getPhysicalId(), false); mScheduler->startTimers(); const auto configs = mVsyncConfiguration->getCurrentConfigs(); @@ -3803,7 +3907,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { /* workDuration */ activeRefreshRate.getPeriod(), /* readyDuration */ configs.late.sfWorkDuration); - mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), + mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mFrameTimeline->getTokenManager(), configs.late.sfWorkDuration); mRegionSamplingThread = @@ -3868,7 +3972,7 @@ void SurfaceFlinger::commitOffscreenLayers() { for (Layer* offscreenLayer : mOffscreenLayers) { offscreenLayer->traverse(LayerVector::StateSet::Drawing, [](Layer* layer) { if (layer->clearTransactionFlags(eTransactionNeeded)) { - layer->doTransaction(0, 0); + layer->doTransaction(0); layer->commitChildList(); } }); @@ -3904,7 +4008,7 @@ bool SurfaceFlinger::latchBuffers() { // second frame. But layer 0's second frame could be waiting on display. mDrawingState.traverse([&](Layer* layer) { if (layer->clearTransactionFlags(eTransactionNeeded) || mForceTransactionDisplayChange) { - const uint32_t flags = layer->doTransaction(0, latchTime); + const uint32_t flags = layer->doTransaction(0); if (flags & Layer::eVisibleRegion) { mVisibleRegionsDirty = true; } @@ -3915,6 +4019,14 @@ bool SurfaceFlinger::latchBuffers() { mLayersWithQueuedFrames.emplace(sp<Layer>::fromExisting(layer)); } else { layer->useEmptyDamage(); + if (!layer->hasBuffer()) { + // The last latch time is used to classify a missed frame as buffer stuffing + // instead of a missed frame. This is used to identify scenarios where we + // could not latch a buffer or apply a transaction due to backpressure. + // We only update the latch time for buffer less layers here, the latch time + // is updated for buffer layers when the buffer is latched. + layer->updateLastLatchTime(latchTime); + } } }); mForceTransactionDisplayChange = false; @@ -3964,7 +4076,7 @@ bool SurfaceFlinger::latchBuffers() { return !mLayersWithQueuedFrames.empty() && newDataLatched; } -status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle, +status_t SurfaceFlinger::addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parent, uint32_t* outTransformHint) { if (mNumLayers >= MAX_LAYERS) { @@ -3994,7 +4106,8 @@ status_t SurfaceFlinger::addClientLayer(const LayerCreationArgs& args, const sp< if (outTransformHint) { *outTransformHint = mActiveDisplayTransformHint; } - + args.parentId = LayerHandle::getLayerId(args.parentHandle.promote()); + args.layerIdToMirror = LayerHandle::getLayerId(args.mirrorLayerHandle.promote()); { std::scoped_lock<std::mutex> lock(mCreatedLayersLock); mCreatedLayers.emplace_back(layer, parent, args.addToRoot); @@ -4017,11 +4130,12 @@ uint32_t SurfaceFlinger::clearTransactionFlags(uint32_t mask) { void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule, const sp<IBinder>& applyToken, FrameHint frameHint) { - mScheduler->modulateVsync(&VsyncModulator::setTransactionSchedule, schedule, applyToken); + mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken); uint32_t transactionFlags = mTransactionFlags.fetch_or(mask); ATRACE_INT("mTransactionFlags", transactionFlags); if (const bool scheduled = transactionFlags & mask; !scheduled) { + mScheduler->resync(); scheduleCommit(frameHint); } else if (frameHint == FrameHint::kActive) { // Even if the next frame is already scheduled, we should reset the idle timer @@ -4065,7 +4179,12 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC const TransactionHandler::TransactionFlushState& flushState) { using TransactionReadiness = TransactionHandler::TransactionReadiness; auto ready = TransactionReadiness::Ready; - flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s) -> bool { + flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const layer_state_t& s, + const std::shared_ptr< + renderengine:: + ExternalTexture>& + externalTexture) + -> bool { sp<Layer> layer = LayerHandle::getLayer(s.surface); const auto& transaction = *flushState.transaction; // check for barrier frames @@ -4075,7 +4194,8 @@ TransactionHandler::TransactionReadiness SurfaceFlinger::transactionReadyBufferC // don't wait on the barrier since we know that's stale information. if (layer->getDrawingState().producerId > s.bufferData->producerId) { layer->callReleaseBufferCallback(s.bufferData->releaseBufferListener, - s.bufferData->buffer, s.bufferData->frameNumber, + externalTexture->getBuffer(), + s.bufferData->frameNumber, s.bufferData->acquireFence); // Delete the entire state at this point and not just release the buffer because // everything associated with the Layer in this Transaction is now out of date. @@ -4158,7 +4278,7 @@ bool SurfaceFlinger::flushTransactionQueues(VsyncId vsyncId) { bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions, VsyncId vsyncId) { - Mutex::Autolock _l(mStateLock); + Mutex::Autolock lock(mStateLock); return applyTransactionsLocked(transactions, vsyncId); } @@ -4204,7 +4324,7 @@ bool SurfaceFlinger::frameIsEarly(TimePoint expectedPresentTime, VsyncId vsyncId return false; } - const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule().period() / 2; + const Duration earlyLatchVsyncThreshold = mScheduler->getVsyncSchedule()->period() / 2; return predictedPresentTime >= expectedPresentTime && predictedPresentTime - expectedPresentTime >= earlyLatchVsyncThreshold; @@ -4239,9 +4359,8 @@ bool SurfaceFlinger::shouldLatchUnsignaled(const sp<Layer>& layer, const layer_s // We don't want to latch unsignaled if are in early / client composition // as it leads to jank due to RenderEngine waiting for unsignaled buffer // or window animations being slow. - const auto isDefaultVsyncConfig = mScheduler->vsyncModulator().isVsyncConfigDefault(); - if (!isDefaultVsyncConfig) { - ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; !isDefaultVsyncConfig)", + if (mScheduler->vsyncModulator().isVsyncConfigEarly()) { + ALOGV("%s: false (LatchUnsignaledConfig::AutoSingleLayer; isVsyncConfigEarly)", __func__); return false; } @@ -4320,6 +4439,21 @@ status_t SurfaceFlinger::setTransactionState( layerName.c_str(), transactionId); mBufferCountTracker.increment(resolvedState.state.surface->localBinder()); } + resolvedState.layerId = LayerHandle::getLayerId(resolvedState.state.surface); + if (resolvedState.state.what & layer_state_t::eReparent) { + resolvedState.parentId = + getLayerIdFromSurfaceControl(resolvedState.state.parentSurfaceControlForChild); + } + if (resolvedState.state.what & layer_state_t::eRelativeLayerChanged) { + resolvedState.relativeParentId = + getLayerIdFromSurfaceControl(resolvedState.state.relativeLayerSurfaceControl); + } + if (resolvedState.state.what & layer_state_t::eInputInfoChanged) { + wp<IBinder>& touchableRegionCropHandle = + resolvedState.state.windowInfoHandle->editInfo()->touchableRegionCropHandle; + resolvedState.touchCropId = + LayerHandle::getLayerId(touchableRegionCropHandle.promote()); + } } TransactionState state{frameTimelineInfo, @@ -4394,10 +4528,14 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin } 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, - LayerUpdateType::AnimationTX); + const auto layerProps = scheduler::LayerProps{ + .visible = layer->isVisible(), + .bounds = layer->getBounds(), + .transform = layer->getTransform(), + .setFrameRateVote = layer->getFrameRateForLayerTree(), + .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(), + }; + layer->recordLayerHistoryAnimationTx(layerProps); } } } @@ -4440,7 +4578,7 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin bool SurfaceFlinger::applyAndCommitDisplayTransactionStates( std::vector<TransactionState>& transactions) { - Mutex::Autolock _l(mStateLock); + Mutex::Autolock lock(mStateLock); bool needsTraversal = false; uint32_t transactionFlags = 0; for (auto& transaction : transactions) { @@ -4469,6 +4607,7 @@ bool SurfaceFlinger::applyAndCommitDisplayTransactionStates( for (const auto& [_, display] : mDisplays) { mFrontEndDisplayInfos.try_emplace(display->getLayerStack(), display->getFrontEndInfo()); } + needsTraversal = true; } return needsTraversal; @@ -4648,7 +4787,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime } } if (what & layer_state_t::eBackgroundColorChanged) { - if (layer->setBackgroundColor(s.color.rgb, s.bgColorAlpha, s.bgColorDataspace)) { + if (layer->setBackgroundColor(s.bgColor.rgb, s.bgColor.a, s.bgColorDataspace)) { flags |= eTraversalNeeded; } } @@ -4788,6 +4927,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime flags |= eTraversalNeeded; } } + if (what & layer_state_t::eCachingHintChanged) { + if (layer->setCachingHint(s.cachingHint)) { + flags |= eTraversalNeeded; + } + } if (what & layer_state_t::eHdrMetadataChanged) { if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded; } @@ -4852,9 +4996,15 @@ uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTime layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); } + if ((what & layer_state_t::eBufferChanged) == 0) { + layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp); + } + if (what & layer_state_t::eTrustedPresentationInfoChanged) { - layer->setTrustedPresentationInfo(s.trustedPresentationThresholds, - s.trustedPresentationListener); + if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds, + s.trustedPresentationListener)) { + flags |= eTraversalNeeded; + } } if (what & layer_state_t::eFlushJankData) { @@ -4886,8 +5036,6 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f uint64_t transactionId) { layer_state_t& s = composerState.state; s.sanitize(permissions); - const nsecs_t latchTime = systemTime(); - bool unused; std::vector<ListenerCallbacks> filteredListeners; for (auto& listener : s.listeners) { @@ -4940,6 +5088,12 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f sp<CallbackHandle>::make(listener, callbackIds, s.surface)); } } + // TODO(b/238781169) remove after screenshot refactor, currently screenshots + // requires to read drawing state from binder thread. So we need to fix that + // before removing this. + if (what & layer_state_t::eCropChanged) { + if (layer->setCrop(s.crop)) flags |= eTraversalNeeded; + } if (what & layer_state_t::eSidebandStreamChanged) { if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; } @@ -4947,7 +5101,6 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime, desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp, frameTimelineInfo)) { - layer->latchBuffer(unused, latchTime); flags |= eTraversalNeeded; } mLayersWithQueuedFrames.emplace(layer); @@ -4955,16 +5108,23 @@ uint32_t SurfaceFlinger::updateLayerCallbacksAndStats(const FrameTimelineInfo& f layer->setFrameTimelineVsyncForBufferlessTransaction(frameTimelineInfo, postTime); } + if ((what & layer_state_t::eBufferChanged) == 0) { + layer->setDesiredPresentTime(desiredPresentTime, isAutoTimestamp); + } + if (what & layer_state_t::eTrustedPresentationInfoChanged) { - layer->setTrustedPresentationInfo(s.trustedPresentationThresholds, - s.trustedPresentationListener); + if (layer->setTrustedPresentationInfo(s.trustedPresentationThresholds, + s.trustedPresentationListener)) { + flags |= eTraversalNeeded; + } } - const auto& snapshot = mLayerSnapshotBuilder.getSnapshot(layer->getSequence()); + const auto& requestedLayerState = mLayerLifecycleManager.getLayerFromId(layer->getSequence()); bool willPresentCurrentTransaction = - snapshot && (snapshot->hasReadyFrame || snapshot->sidebandStreamHasFrame); + requestedLayerState && requestedLayerState->hasReadyFrame(); if (layer->setTransactionCompletedListeners(callbackHandles, willPresentCurrentTransaction)) flags |= eTraversalNeeded; + return flags; } @@ -5053,7 +5213,7 @@ status_t SurfaceFlinger::mirrorDisplay(DisplayId displayId, const LayerCreationA args.name, args.flags, -1 /* parentId */); } - { + if (mLegacyFrontEndEnabled) { std::scoped_lock<std::mutex> lock(mMirrorDisplayLock); mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client); } @@ -5156,15 +5316,25 @@ void SurfaceFlinger::onHandleDestroyed(BBinder* handle, sp<Layer>& layer, uint32 setTransactionFlags(eTransactionFlushNeeded); } -void SurfaceFlinger::onInitializeDisplays() { - const auto display = getDefaultDisplayDeviceLocked(); +void SurfaceFlinger::initializeDisplays() { + const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()); if (!display) return; const sp<IBinder> token = display->getDisplayToken().promote(); LOG_ALWAYS_FATAL_IF(token == nullptr); + TransactionState state; + state.inputWindowCommands = mInputWindowCommands; + const nsecs_t now = systemTime(); + state.desiredPresentTime = now; + state.postTime = now; + state.permissions = layer_state_t::ACCESS_SURFACE_FLINGER; + state.originPid = mPid; + state.originUid = static_cast<int>(getuid()); + const uint64_t transactionId = (static_cast<uint64_t>(mPid) << 32) | mUniqueTransactionId++; + state.id = transactionId; + // reset screen orientation and use primary layer stack - std::vector<ResolvedComposerState> state; Vector<DisplayState> displays; DisplayState d; d.what = DisplayState::eDisplayProjectionChanged | @@ -5176,24 +5346,21 @@ void SurfaceFlinger::onInitializeDisplays() { d.layerStackSpaceRect.makeInvalid(); d.width = 0; d.height = 0; - displays.add(d); - - nsecs_t now = systemTime(); + state.displays.add(d); - int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++; - // It should be on the main thread, apply it directly. - applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands, - /* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false, - {}, mPid, getuid(), transactionId); + std::vector<TransactionState> transactions; + transactions.emplace_back(state); - setPowerModeInternal(display, hal::PowerMode::ON); -} + if (mLegacyFrontEndEnabled) { + applyTransactions(transactions, VsyncId{0}); + } else { + applyAndCommitDisplayTransactionStates(transactions); + } -void SurfaceFlinger::initializeDisplays() { - // Async since we may be called from the main thread. - static_cast<void>(mScheduler->schedule( - [this]() FTL_FAKE_GUARD(mStateLock) - FTL_FAKE_GUARD(kMainThreadContext) { onInitializeDisplays(); })); + { + ftl::FakeGuard guard(mStateLock); + setPowerModeInternal(display, hal::PowerMode::ON); + } } void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) { @@ -5210,7 +5377,6 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: return; } - const bool isActiveDisplay = displayId == mActiveDisplayId; const bool isInternalDisplay = mPhysicalDisplays.get(displayId) .transform(&PhysicalDisplay::isInternal) .value_or(false); @@ -5247,11 +5413,12 @@ 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 (isActiveDisplay && mode != hal::PowerMode::DOZE_SUSPEND) { + if (displayId == mActiveDisplayId && mode != hal::PowerMode::DOZE_SUSPEND) { setHWCVsyncEnabled(displayId, - mScheduler->getVsyncSchedule().getPendingHardwareVsyncState()); - mScheduler->onScreenAcquired(mAppConnectionHandle); - mScheduler->resyncToHardwareVsync(true, refreshRate); + mScheduler->getVsyncSchedule(displayId) + ->getPendingHardwareVsyncState()); + mScheduler->enableSyntheticVsync(false); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate); } mVisibleRegionsDirty = true; @@ -5264,9 +5431,9 @@ 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 (isActiveDisplay && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { - mScheduler->disableHardwareVsync(true); - mScheduler->onScreenReleased(mAppConnectionHandle); + if (displayId == mActiveDisplayId && *currentModeOpt != hal::PowerMode::DOZE_SUSPEND) { + mScheduler->disableHardwareVsync(displayId, true); + mScheduler->enableSyntheticVsync(); } // Make sure HWVsync is disabled before turning off the display @@ -5278,18 +5445,18 @@ 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 (isActiveDisplay && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) { + if (displayId == mActiveDisplayId && *currentModeOpt == hal::PowerMode::DOZE_SUSPEND) { ALOGI("Force repainting for DOZE_SUSPEND -> DOZE or ON."); mVisibleRegionsDirty = true; scheduleRepaint(); - mScheduler->onScreenAcquired(mAppConnectionHandle); - mScheduler->resyncToHardwareVsync(true, refreshRate); + mScheduler->enableSyntheticVsync(false); + mScheduler->resyncToHardwareVsync(displayId, true /* allowToEnable */, refreshRate); } } else if (mode == hal::PowerMode::DOZE_SUSPEND) { // Leave display going to doze - if (isActiveDisplay) { - mScheduler->disableHardwareVsync(true); - mScheduler->onScreenReleased(mAppConnectionHandle); + if (displayId == mActiveDisplayId) { + mScheduler->disableHardwareVsync(displayId, true); + mScheduler->enableSyntheticVsync(); } getHwComposer().setPowerMode(displayId, mode); } else { @@ -5297,10 +5464,10 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: getHwComposer().setPowerMode(displayId, mode); } - if (isActiveDisplay) { + if (displayId == mActiveDisplayId) { mTimeStats->setPowerMode(mode); mRefreshRateStats->setPowerMode(mode); - mScheduler->setDisplayPowerMode(mode); + mScheduler->setDisplayPowerMode(displayId, mode); } ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str()); @@ -5623,14 +5790,27 @@ LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const { } } + if (mLegacyFrontEndEnabled) { + LayersProto layersProto; + for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) { + if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) { + continue; + } + layer->writeToProto(layersProto, traceFlags); + } + return layersProto; + } + + const frontend::LayerHierarchy& root = mLayerHierarchyBuilder.getHierarchy(); LayersProto layersProto; - for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) { - if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) { + for (auto& [child, variant] : root.mChildren) { + if (variant != frontend::LayerHierarchy::Variant::Attached || + stackIdsToSkip.find(child->getLayer()->layerStack.id) != stackIdsToSkip.end()) { continue; } - layer->writeToProto(layersProto, traceFlags); + LayerProtoHelper::writeHierarchyToProto(layersProto, *child, mLayerSnapshotBuilder, + mLegacyLayers, traceFlags); } - return layersProto; } @@ -6712,7 +6892,8 @@ status_t SurfaceFlinger::captureDisplay(const DisplayCaptureArgs& args, GetLayerSnapshotsFunction getLayerSnapshots; if (mLayerLifecycleManagerEnabled) { - getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, args.uid); + getLayerSnapshots = + getLayerSnapshotsForScreenshots(layerStack, args.uid, /*snapshotFilterFn=*/nullptr); } else { auto traverseLayers = [this, args, layerStack](const LayerVector::Visitor& visitor) { traverseLayersInLayerStack(layerStack, args.uid, visitor); @@ -6754,7 +6935,8 @@ status_t SurfaceFlinger::captureDisplay(DisplayId displayId, GetLayerSnapshotsFunction getLayerSnapshots; if (mLayerLifecycleManagerEnabled) { - getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID); + getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, + /*snapshotFilterFn=*/nullptr); } else { auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) { traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, visitor); @@ -6922,7 +7104,7 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( ->schedule([=]() { bool protectedLayerFound = false; auto layers = getLayerSnapshots(); - for (auto& [layer, layerFe] : layers) { + for (auto& [_, layerFe] : layers) { protectedLayerFound |= (layerFe->mSnapshot->isVisible && layerFe->mSnapshot->hasProtectedContent); @@ -7017,12 +7199,14 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( ATRACE_CALL(); auto layers = getLayerSnapshots(); - for (auto& [layer, layerFE] : layers) { + for (auto& [_, layerFE] : layers) { frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get(); captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure); captureResults.capturedHdrLayers |= isHdrLayer(*snapshot); layerFE->mSnapshot->geomLayerTransform = renderArea->getTransform() * layerFE->mSnapshot->geomLayerTransform; + layerFE->mSnapshot->geomInverseLayerTransform = + layerFE->mSnapshot->geomLayerTransform.inverse(); } // We allow the system server to take screenshots of secure layers for @@ -7133,6 +7317,16 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl( return presentFuture; } +void SurfaceFlinger::traverseLegacyLayers(const LayerVector::Visitor& visitor) const { + if (mLayerLifecycleManagerEnabled) { + for (auto& layer : mLegacyLayers) { + visitor(layer.second.get()); + } + } else { + mDrawingState.traverse(visitor); + } +} + // --------------------------------------------------------------------------- void SurfaceFlinger::State::traverse(const LayerVector::Visitor& visitor) const { @@ -7421,10 +7615,20 @@ status_t SurfaceFlinger::setOverrideFrameRate(uid_t uid, float frameRate) { } void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { + bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG); for (const auto& [id, display] : mPhysicalDisplays) { if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal) { + if (setByHwc) { + const auto status = + getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable); + if (status != NO_ERROR) { + ALOGE("Error updating the refresh rate changed callback debug enabled"); + return; + } + } + if (const auto device = getDisplayDeviceLocked(id)) { - device->enableRefreshRateOverlay(enable, mRefreshRateOverlaySpinner, + device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner, mRefreshRateOverlayRenderRate, mRefreshRateOverlayShowInMiddle); } @@ -7543,31 +7747,38 @@ void SurfaceFlinger::onActiveDisplayChangedLocked(const DisplayDevice* inactiveD const DisplayDevice& activeDisplay) { ATRACE_CALL(); + // For the first display activated during boot, there is no need to force setDesiredActiveMode, + // because DM is about to send its policy via setDesiredDisplayModeSpecs. + bool forceApplyPolicy = false; + if (inactiveDisplayPtr) { inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false); + forceApplyPolicy = true; } mActiveDisplayId = activeDisplay.getPhysicalId(); activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true); - updateActiveDisplayVsyncLocked(activeDisplay); + resetPhaseConfiguration(activeDisplay.getActiveMode().fps); + mScheduler->setModeChangePending(false); - mScheduler->setLeaderDisplay(mActiveDisplayId); + mScheduler->setPacesetterDisplay(mActiveDisplayId); onActiveDisplaySizeChanged(activeDisplay); mActiveDisplayTransformHint = activeDisplay.getTransformHint(); - // 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. - constexpr bool kForce = true; - applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(), kForce); + // The policy of the new active/pacesetter 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(), + forceApplyPolicy); } status_t SurfaceFlinger::addWindowInfosListener( - const sp<IWindowInfosListener>& windowInfosListener) const { + const sp<IWindowInfosListener>& windowInfosListener) { mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener); + setTransactionFlags(eInputInfoUpdateNeeded); return NO_ERROR; } @@ -7728,6 +7939,7 @@ std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToComposit compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly, int64_t vsyncId) { std::vector<std::pair<Layer*, LayerFE*>> layers; if (mLayerLifecycleManagerEnabled) { + nsecs_t currentTime = systemTime(); mLayerSnapshotBuilder.forEachVisibleSnapshot( [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) { if (cursorOnly && @@ -7746,6 +7958,7 @@ std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToComposit snapshot->getDebugString().c_str()); auto& legacyLayer = it->second; sp<LayerFE> layerFE = legacyLayer->getCompositionEngineLayerFE(snapshot->path); + snapshot->fps = getLayerFramerate(currentTime, snapshot->sequence); layerFE->mSnapshot = std::move(snapshot); refreshArgs.layers.push_back(layerFE); layers.emplace_back(legacyLayer.get(), layerFE.get()); @@ -7770,29 +7983,40 @@ std::vector<std::pair<Layer*, LayerFE*>> SurfaceFlinger::moveSnapshotsToComposit } std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> -SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack, - uint32_t uid) { - return [this, layerStack, uid]() { +SurfaceFlinger::getLayerSnapshotsForScreenshots( + std::optional<ui::LayerStack> layerStack, uint32_t uid, + std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)> + snapshotFilterFn) { + return [&, layerStack, uid]() { std::vector<std::pair<Layer*, sp<LayerFE>>> layers; - for (auto& snapshot : mLayerSnapshotBuilder.getSnapshots()) { - if (layerStack && snapshot->outputFilter.layerStack != *layerStack) { - continue; - } - if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) { - continue; - } - if (!snapshot->isVisible || !snapshot->hasSomethingToDraw()) { - continue; - } + bool stopTraversal = false; + mLayerSnapshotBuilder.forEachVisibleSnapshot( + [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) { + if (stopTraversal) { + return; + } + if (layerStack && snapshot->outputFilter.layerStack != *layerStack) { + return; + } + if (uid != CaptureArgs::UNSET_UID && snapshot->inputInfo.ownerUid != uid) { + return; + } + if (!snapshot->hasSomethingToDraw()) { + return; + } + if (snapshotFilterFn && !snapshotFilterFn(*snapshot, stopTraversal)) { + return; + } - auto it = mLegacyLayers.find(snapshot->sequence); - LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), "Couldnt find layer object for %s", - snapshot->getDebugString().c_str()); - auto& legacyLayer = it->second; - sp<LayerFE> layerFE = getFactory().createLayerFE(legacyLayer->getName()); - layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot); - layers.emplace_back(legacyLayer.get(), std::move(layerFE)); - } + auto it = mLegacyLayers.find(snapshot->sequence); + LOG_ALWAYS_FATAL_IF(it == mLegacyLayers.end(), + "Couldnt find layer object for %s", + snapshot->getDebugString().c_str()); + Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get(); + sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name); + layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot); + layers.emplace_back(legacyLayer, std::move(layerFE)); + }); return layers; }; @@ -7802,21 +8026,27 @@ std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> SurfaceFlinger::getLayerSnapshotsForScreenshots(uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds, bool childrenOnly, const FloatRect& parentCrop) { - return [this, excludeLayerIds = std::move(excludeLayerIds), uid, rootLayerId, childrenOnly, + return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly, parentCrop]() { + auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly); frontend::LayerSnapshotBuilder::Args - args{.root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly), + args{.root = root, .layerLifecycleManager = mLayerLifecycleManager, + .forceUpdate = frontend::LayerSnapshotBuilder::ForceUpdateFlags::HIERARCHY, .displays = mFrontEndDisplayInfos, .displayChanges = true, .globalShadowSettings = mDrawingState.globalShadowSettings, .supportsBlur = mSupportsBlur, .forceFullDamage = mForceFullDamage, .parentCrop = {parentCrop}, - .excludeLayerIds = std::move(excludeLayerIds)}; + .excludeLayerIds = std::move(excludeLayerIds), + .supportedLayerGenericMetadata = + getHwComposer().getSupportedLayerGenericMetadata(), + .genericLayerMetadataKeyMap = getGenericLayerMetadataKeyMap()}; mLayerSnapshotBuilder.update(args); - auto getLayerSnapshotsFn = getLayerSnapshotsForScreenshots({}, uid); + auto getLayerSnapshotsFn = + getLayerSnapshotsForScreenshots({}, uid, /*snapshotFilterFn=*/nullptr); std::vector<std::pair<Layer*, sp<LayerFE>>> layers = getLayerSnapshotsFn(); args.root = mLayerHierarchyBuilder.getHierarchy(); args.parentCrop.reset(); @@ -8559,8 +8789,12 @@ binder::Status SurfaceComposerAIDL::getMaxAcquiredBufferCount(int32_t* buffers) binder::Status SurfaceComposerAIDL::addWindowInfosListener( const sp<gui::IWindowInfosListener>& windowInfosListener) { status_t status; + const int pid = IPCThreadState::self()->getCallingPid(); const int uid = IPCThreadState::self()->getCallingUid(); - if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { + // TODO(b/270566761) update permissions check so that only system_server and shell can add + // WindowInfosListeners + if (uid == AID_SYSTEM || uid == AID_GRAPHICS || + checkPermission(sAccessSurfaceFlinger, pid, uid)) { status = mFlinger->addWindowInfosListener(windowInfosListener); } else { status = PERMISSION_DENIED; @@ -8571,8 +8805,10 @@ binder::Status SurfaceComposerAIDL::addWindowInfosListener( binder::Status SurfaceComposerAIDL::removeWindowInfosListener( const sp<gui::IWindowInfosListener>& windowInfosListener) { status_t status; + const int pid = IPCThreadState::self()->getCallingPid(); const int uid = IPCThreadState::self()->getCallingUid(); - if (uid == AID_SYSTEM || uid == AID_GRAPHICS) { + if (uid == AID_SYSTEM || uid == AID_GRAPHICS || + checkPermission(sAccessSurfaceFlinger, pid, uid)) { status = mFlinger->removeWindowInfosListener(windowInfosListener); } else { status = PERMISSION_DENIED; diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index b41f414f62..63d54bc24d 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -162,7 +162,8 @@ enum { eDisplayTransactionNeeded = 0x04, eTransformHintUpdateNeeded = 0x08, eTransactionFlushNeeded = 0x10, - eTransactionMask = 0x1f, + eInputInfoUpdateNeeded = 0x20, + eTransactionMask = 0x3f, }; // Latch Unsignaled buffer behaviours @@ -618,7 +619,7 @@ private: status_t getMaxAcquiredBufferCount(int* buffers) const; - status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener) const; + status_t addWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); status_t removeWindowInfosListener( const sp<gui::IWindowInfosListener>& windowInfosListener) const; @@ -646,7 +647,7 @@ private: // Toggles hardware VSYNC by calling into HWC. // TODO(b/241286146): Rename for self-explanatory API. - void setVsyncEnabled(bool) override; + void setVsyncEnabled(PhysicalDisplayId, bool) override; void requestDisplayModes(std::vector<display::DisplayModeRequest>) override; void kernelTimerChanged(bool expired) override; void triggerOnFrameRateOverridesChanged() override; @@ -725,6 +726,7 @@ private: REQUIRES(kMainThreadContext); bool updateLayerSnapshots(VsyncId vsyncId, LifecycleUpdate& update, bool transactionsFlushed, bool& out) REQUIRES(kMainThreadContext); + void updateLayerHistory(const frontend::LayerSnapshot& snapshot); LifecycleUpdate flushLifecycleUpdates() REQUIRES(kMainThreadContext); void updateInputFlinger(); @@ -735,6 +737,8 @@ private: void updateCursorAsync(); void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock); + + void resetPhaseConfiguration(Fps) REQUIRES(mStateLock, kMainThreadContext); void updatePhaseConfiguration(Fps) REQUIRES(mStateLock); /* @@ -816,7 +820,7 @@ private: void markLayerPendingRemovalLocked(const sp<Layer>& layer); // add a layer to SurfaceFlinger - status_t addClientLayer(const LayerCreationArgs& args, const sp<IBinder>& handle, + status_t addClientLayer(LayerCreationArgs& args, const sp<IBinder>& handle, const sp<Layer>& layer, const wp<Layer>& parentLayer, uint32_t* outTransformHint); @@ -853,8 +857,7 @@ private: */ // Called during boot, and restart after system_server death. - void initializeDisplays(); - void onInitializeDisplays() REQUIRES(mStateLock, kMainThreadContext); + void initializeDisplays() REQUIRES(kMainThreadContext); sp<const DisplayDevice> getDisplayDeviceLocked(const wp<IBinder>& displayToken) const REQUIRES(mStateLock) { @@ -1119,20 +1122,26 @@ private: std::chrono::nanoseconds presentLatency); int getMaxAcquiredBufferCountForRefreshRate(Fps refreshRate) const; - void updateActiveDisplayVsyncLocked(const DisplayDevice& activeDisplay) - REQUIRES(mStateLock, kMainThreadContext); - bool isHdrLayer(const frontend::LayerSnapshot& snapshot) const; ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const REQUIRES(mStateLock); + void traverseLegacyLayers(const LayerVector::Visitor& visitor) const; sp<StartPropertySetThread> mStartPropertySetThread; surfaceflinger::Factory& mFactory; pid_t mPid; std::future<void> mRenderEnginePrimeCacheFuture; - // access must be protected by mStateLock + // mStateLock has conventions related to the current thread, because only + // the main thread should modify variables protected by mStateLock. + // - read access from a non-main thread must lock mStateLock, since the main + // thread may modify these variables. + // - write access from a non-main thread is not permitted. + // - read access from the main thread can use an ftl::FakeGuard, since other + // threads must not modify these variables. + // - write access from the main thread must lock mStateLock, since another + // thread may be reading these variables. mutable Mutex mStateLock; State mCurrentState{LayerVector::StateSet::Current}; std::atomic<int32_t> mTransactionFlags = 0; @@ -1386,7 +1395,9 @@ private: [](const auto& display) { return display.isRefreshRateOverlayEnabled(); }); } std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots( - std::optional<ui::LayerStack> layerStack, uint32_t uid); + std::optional<ui::LayerStack> layerStack, uint32_t uid, + std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)> + snapshotFilterFn); std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshotsForScreenshots( uint32_t rootLayerId, uint32_t uid, std::unordered_set<uint32_t> excludeLayerIds, bool childrenOnly, const FloatRect& parentCrop); diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp index 2f464873ea..ba08ceebc7 100644 --- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp +++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp @@ -15,6 +15,7 @@ */ #include <gui/SurfaceComposerClient.h> +#include <renderengine/mock/FakeExternalTexture.h> #include <ui/Fence.h> #include <ui/Rect.h> @@ -220,12 +221,12 @@ proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer) { } } if (layer.what & layer_state_t::eBackgroundColorChanged) { - proto.set_bg_color_alpha(layer.bgColorAlpha); + proto.set_bg_color_alpha(layer.bgColor.a); proto.set_bg_color_dataspace(static_cast<int32_t>(layer.bgColorDataspace)); proto::LayerState_Color3* colorProto = proto.mutable_color(); - colorProto->set_r(layer.color.r); - colorProto->set_g(layer.color.g); - colorProto->set_b(layer.color.b); + colorProto->set_r(layer.bgColor.r); + colorProto->set_g(layer.bgColor.g); + colorProto->set_b(layer.bgColor.b); } if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) { proto.set_color_space_agnostic(layer.colorSpaceAgnostic); @@ -313,6 +314,14 @@ TransactionState TransactionProtoParser::fromProto(const proto::TransactionState ResolvedComposerState s; s.state.what = 0; fromProto(proto.layer_changes(i), s.state); + if (s.state.bufferData) { + s.externalTexture = std::make_shared< + renderengine::mock::FakeExternalTexture>(s.state.bufferData->getWidth(), + s.state.bufferData->getHeight(), + s.state.bufferData->getId(), + s.state.bufferData->getPixelFormat(), + s.state.bufferData->getUsage()); + } t.states.emplace_back(s); } @@ -501,12 +510,12 @@ void TransactionProtoParser::fromProto(const proto::LayerState& proto, layer_sta layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo); } if (proto.what() & layer_state_t::eBackgroundColorChanged) { - layer.bgColorAlpha = proto.bg_color_alpha(); + layer.bgColor.a = proto.bg_color_alpha(); layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace()); const proto::LayerState_Color3& colorProto = proto.color(); - layer.color.r = colorProto.r(); - layer.color.g = colorProto.g(); - layer.color.b = colorProto.b(); + layer.bgColor.r = colorProto.r(); + layer.bgColor.g = colorProto.g(); + layer.bgColor.b = colorProto.b(); } if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) { layer.colorSpaceAgnostic = proto.color_space_agnostic(); diff --git a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp index ab98dbfe2f..31f4723915 100644 --- a/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp +++ b/services/surfaceflinger/Tracing/tools/LayerTraceGenerator.cpp @@ -191,29 +191,20 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, return false; } - Factory mFactory; - sp<MockSurfaceFlinger> flinger = sp<MockSurfaceFlinger>::make(mFactory); - TestableSurfaceFlinger mFlinger(flinger); - mFlinger.setupRenderEngine( + Factory factory; + sp<MockSurfaceFlinger> flingerPtr = sp<MockSurfaceFlinger>::make(factory); + TestableSurfaceFlinger flinger(flingerPtr); + flinger.setupRenderEngine( std::make_unique<testing::NiceMock<renderengine::mock::RenderEngine>>()); - mock::VsyncController* mVsyncController = new testing::NiceMock<mock::VsyncController>(); - mock::VSyncTracker* mVSyncTracker = new testing::NiceMock<mock::VSyncTracker>(); - mock::EventThread* mEventThread = new testing::NiceMock<mock::EventThread>(); - mock::EventThread* mSFEventThread = new testing::NiceMock<mock::EventThread>(); - mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController), - std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker), - std::unique_ptr<EventThread>(mEventThread), - std::unique_ptr<EventThread>(mSFEventThread), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - TestableSurfaceFlinger::kOneDisplayMode, true /* useNiceMock */); - - Hwc2::mock::Composer* mComposer = new testing::NiceMock<Hwc2::mock::Composer>(); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - 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()); + flinger.setupMockScheduler({.useNiceMock = true}); + + Hwc2::mock::Composer* composerPtr = new testing::NiceMock<Hwc2::mock::Composer>(); + flinger.setupComposer(std::unique_ptr<Hwc2::Composer>(composerPtr)); + flinger.mutableMaxRenderTargetSize() = 16384; + + flingerPtr->setLayerTracingFlags(LayerTracing::TRACE_INPUT | LayerTracing::TRACE_BUFFERS); + flingerPtr->setLayerTraceSize(512 * 1024); // 512MB buffer size + flingerPtr->startLayerTracing(traceFile.entry(0).elapsed_realtime_nanos()); std::unique_ptr<TraceGenFlingerDataMapper> mapper = std::make_unique<TraceGenFlingerDataMapper>(); TraceGenFlingerDataMapper* dataMapper = mapper.get(); @@ -234,7 +225,7 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, parser.fromProto(entry.added_layers(j), tracingArgs); gui::CreateSurfaceResult outResult; - LayerCreationArgs args(mFlinger.flinger(), nullptr /* client */, tracingArgs.name, + LayerCreationArgs args(flinger.flinger(), nullptr /* client */, tracingArgs.name, tracingArgs.flags, LayerMetadata(), std::make_optional<int32_t>(tracingArgs.layerId)); @@ -247,10 +238,10 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, } else if (tracingArgs.parentId != -1) { parentHandle = dataMapper->getLayerHandle(tracingArgs.parentId); } - mFlinger.createLayer(args, parentHandle, outResult); + flinger.createLayer(args, parentHandle, outResult); } else { sp<IBinder> mirrorFromHandle = dataMapper->getLayerHandle(tracingArgs.mirrorFromId); - mFlinger.mirrorLayer(args, mirrorFromHandle, outResult); + flinger.mirrorLayer(args, mirrorFromHandle, outResult); } LOG_ALWAYS_FATAL_IF(outResult.layerId != tracingArgs.layerId, "Could not create layer expected:%d actual:%d", tracingArgs.layerId, @@ -261,19 +252,19 @@ bool LayerTraceGenerator::generate(const proto::TransactionTraceFile& traceFile, for (int j = 0; j < entry.transactions_size(); j++) { // apply transactions TransactionState transaction = parser.fromProto(entry.transactions(j)); - mFlinger.setTransactionStateInternal(transaction); + flinger.setTransactionStateInternal(transaction); } const auto frameTime = TimePoint::fromNs(entry.elapsed_realtime_nanos()); const auto vsyncId = VsyncId{entry.vsync_id()}; - mFlinger.commit(frameTime, vsyncId); + flinger.commit(frameTime, vsyncId); for (int j = 0; j < entry.removed_layer_handles_size(); j++) { dataMapper->mLayerHandles.erase(entry.removed_layer_handles(j)); } } - flinger->stopLayerTracing(outputLayersTracePath); + flingerPtr->stopLayerTracing(outputLayersTracePath); ALOGD("End of generating trace file. File written to %s", outputLayersTracePath); dataMapper->mLayerHandles.clear(); return true; diff --git a/services/surfaceflinger/Tracing/tools/run.sh b/services/surfaceflinger/Tracing/tools/run.sh index baa93f1a1c..307a4d8338 100644 --- a/services/surfaceflinger/Tracing/tools/run.sh +++ b/services/surfaceflinger/Tracing/tools/run.sh @@ -5,7 +5,15 @@ set -ex # Build, push and run layertracegenerator $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode layertracegenerator adb wait-for-device && adb push $OUT/system/bin/layertracegenerator /data/layertracegenerator -echo "Writing transaction trace to file" -adb shell service call SurfaceFlinger 1041 i32 0 -adb shell /data/layertracegenerator + +if [ -z "$1" ] + then + echo "Writing transaction trace to file" + adb shell service call SurfaceFlinger 1041 i32 0 + adb shell /data/layertracegenerator + else + echo "Pushing transaction trace to device" + adb push $1 /data/transaction_trace.winscope + adb shell /data/layertracegenerator /data/transaction_trace.winscope +fi adb pull /data/misc/wmtrace/layers_trace.winscope
\ No newline at end of file diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h index 6c5a8b213d..2daea259fa 100644 --- a/services/surfaceflinger/TransactionState.h +++ b/services/surfaceflinger/TransactionState.h @@ -20,6 +20,7 @@ #include <memory> #include <mutex> #include <vector> +#include "FrontEnd/LayerCreationArgs.h" #include "renderengine/ExternalTexture.h" #include <gui/LayerState.h> @@ -39,6 +40,10 @@ public: ResolvedComposerState() = default; ResolvedComposerState(ComposerState&& source) { state = std::move(source.state); } std::shared_ptr<renderengine::ExternalTexture> externalTexture; + uint32_t layerId = UNASSIGNED_LAYER_ID; + uint32_t parentId = UNASSIGNED_LAYER_ID; + uint32_t relativeParentId = UNASSIGNED_LAYER_ID; + uint32_t touchCropId = UNASSIGNED_LAYER_ID; }; struct TransactionState { @@ -85,7 +90,7 @@ struct TransactionState { for (auto state = states.begin(); state != states.end();) { if (state->state.hasBufferChanges() && state->state.hasValidBuffer() && state->state.surface) { - int result = visitor(state->state); + int result = visitor(state->state, state->externalTexture); if (result == STOP_TRAVERSAL) return; if (result == DELETE_AND_CONTINUE_TRAVERSAL) { state = states.erase(state); diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp index a1313e3a03..292083b9bc 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -17,7 +17,6 @@ #include <ftl/small_vector.h> #include <gui/ISurfaceComposer.h> -#include "SurfaceFlinger.h" #include "WindowInfosListenerInvoker.h" namespace android { diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h index a1d66a186e..d60a9c4157 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.h +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -16,6 +16,8 @@ #pragma once +#include <unordered_set> + #include <android/gui/BnWindowInfosReportedListener.h> #include <android/gui/IWindowInfosListener.h> #include <android/gui/IWindowInfosReportedListener.h> @@ -49,8 +51,6 @@ private: static constexpr size_t kStaticCapacity = 3; ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity> mWindowInfosListeners GUARDED_BY(mListenersMutex); - - sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener; }; } // namespace android diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index 609fd33a3f..72cecc9f89 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -226,19 +226,19 @@ public: TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), - std::make_unique<android::mock::VSyncTracker>(), selectorPtr, + std::make_shared<android::mock::VSyncTracker>(), selectorPtr, std::move(modulatorPtr), callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, - std::unique_ptr<VSyncTracker> tracker, + VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) { - mVsyncSchedule = std::unique_ptr<VsyncSchedule>( - new VsyncSchedule(std::move(tracker), nullptr, std::move(controller))); - const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); - registerDisplay(displayId, std::move(selectorPtr)); + registerDisplayInternal(displayId, std::move(selectorPtr), + std::shared_ptr<VsyncSchedule>( + new VsyncSchedule(displayId, std::move(tracker), nullptr, + std::move(controller)))); } ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) { @@ -247,7 +247,7 @@ public: auto &mutableLayerHistory() { return mLayerHistory; } - auto refreshRateSelector() { return leaderSelectorPtr(); } + auto refreshRateSelector() { return pacesetterSelectorPtr(); } void replaceTouchTimer(int64_t millis) { if (mTouchTimer) { @@ -402,9 +402,8 @@ public: SurfaceFlinger *flinger() { return mFlinger.get(); } scheduler::TestableScheduler *scheduler() { return mScheduler; } - // Allow reading display state without locking, as if called on the SF main thread. - auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS { - return mFlinger->onInitializeDisplays(); + void initializeDisplays() { + FTL_FAKE_GUARD(kMainThreadContext, mFlinger->initializeDisplays()); } void setGlobalShadowSettings(FuzzedDataProvider *fdp) { @@ -542,7 +541,7 @@ public: mFlinger->createDisplay(String8(fdp->ConsumeRandomLengthString().c_str()), fdp->ConsumeBool()); - onInitializeDisplays(); + initializeDisplays(); mFlinger->getPhysicalDisplayToken(physicalDisplayId); mFlinger->mStartPropertySetThread = @@ -647,10 +646,10 @@ public: // The ISchedulerCallback argument can be nullptr for a no-op implementation. void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - scheduler::ISchedulerCallback *callback = nullptr, + scheduler::ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -789,7 +788,7 @@ public: } private: - void setVsyncEnabled(bool) override {} + void setVsyncEnabled(PhysicalDisplayId, bool) override {} void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index 61fb29a964..f17d2e1cb4 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -48,6 +48,7 @@ constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode:: constexpr uint16_t kRandomStringLength = 256; constexpr std::chrono::duration kSyncPeriod(16ms); +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); template <typename T> void dump(T* component, FuzzedDataProvider* fdp) { @@ -76,7 +77,7 @@ private: FuzzedDataProvider mFdp; - std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule; + std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule; }; PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { @@ -90,12 +91,13 @@ PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { } void SchedulerFuzzer::fuzzEventThread() { - mVsyncSchedule = std::unique_ptr<scheduler::VsyncSchedule>( - new scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(), - std::make_unique<mock::VSyncDispatch>(), nullptr)); + mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>( + new scheduler::VsyncSchedule(getPhysicalDisplayId(), + std::make_shared<mock::VSyncTracker>(), + std::make_shared<mock::VSyncDispatch>(), nullptr)); const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); }; std::unique_ptr<android::impl::EventThread> thread = std::make_unique< - android::impl::EventThread>("fuzzer", *mVsyncSchedule, nullptr, nullptr, getVsyncPeriod, + android::impl::EventThread>("fuzzer", mVsyncSchedule, nullptr, nullptr, getVsyncPeriod, (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); @@ -109,8 +111,7 @@ void SchedulerFuzzer::fuzzEventThread() { thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral<uint64_t>()); thread->registerDisplayEventConnection(connection); - thread->onScreenAcquired(); - thread->onScreenReleased(); + thread->enableSyntheticVsync(mFdp.ConsumeBool()); dump<android::impl::EventThread>(thread.get(), &mFdp); } @@ -132,7 +133,7 @@ void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* disp } void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { - FuzzImplVSyncTracker stubTracker{mFdp.ConsumeIntegral<nsecs_t>()}; + auto stubTracker = std::make_shared<FuzzImplVSyncTracker>(mFdp.ConsumeIntegral<nsecs_t>()); scheduler::VSyncDispatchTimerQueue mDispatch{std::make_unique<scheduler::ControllableClock>(), stubTracker, mFdp.ConsumeIntegral<nsecs_t>() /*dispatchGroupThreshold*/, @@ -145,17 +146,17 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { scheduler::VSyncDispatchTimerQueueEntry entry( "fuzz", [](auto, auto, auto) {}, mFdp.ConsumeIntegral<nsecs_t>() /*vSyncMoveThreshold*/); - entry.update(stubTracker, 0); + entry.update(*stubTracker, 0); entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, - stubTracker, 0); + *stubTracker, 0); entry.disarm(); entry.ensureNotRunning(); entry.schedule({.workDuration = mFdp.ConsumeIntegral<nsecs_t>(), .readyDuration = mFdp.ConsumeIntegral<nsecs_t>(), .earliestVsync = mFdp.ConsumeIntegral<nsecs_t>()}, - stubTracker, 0); + *stubTracker, 0); auto const wakeup = entry.wakeupTime(); auto const ready = entry.readyTime(); entry.callback(entry.executing(), *wakeup, *ready); @@ -169,7 +170,8 @@ void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); - scheduler::VSyncPredictor tracker{mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize, + scheduler::VSyncPredictor tracker{DEFAULT_DISPLAY_ID, + mFdp.ConsumeIntegral<uint16_t>() /*period*/, historySize, minimumSamplesForPrediction, mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); @@ -218,9 +220,9 @@ void SchedulerFuzzer::fuzzLayerHistory() { sp<FuzzImplLayer> layer2 = sp<FuzzImplLayer>::make(flinger.flinger()); for (int i = 0; i < historySize; ++i) { - historyV1.record(layer1.get(), time1, time1, + historyV1.record(layer1->getSequence(), layer1->getLayerProps(), time1, time1, scheduler::LayerHistory::LayerUpdateType::Buffer); - historyV1.record(layer2.get(), time2, time2, + historyV1.record(layer2->getSequence(), layer2->getLayerProps(), time2, time2, scheduler::LayerHistory::LayerUpdateType::Buffer); time1 += mFdp.PickValueInArray(kVsyncPeriods); time2 += mFdp.PickValueInArray(kVsyncPeriods); @@ -242,13 +244,15 @@ void SchedulerFuzzer::fuzzLayerHistory() { void SchedulerFuzzer::fuzzVSyncReactor() { std::shared_ptr<FuzzImplVSyncTracker> vSyncTracker = std::make_shared<FuzzImplVSyncTracker>(); - scheduler::VSyncReactor reactor(std::make_unique<ClockWrapper>( + scheduler::VSyncReactor reactor(DEFAULT_DISPLAY_ID, + std::make_unique<ClockWrapper>( std::make_shared<FuzzImplClock>()), *vSyncTracker, mFdp.ConsumeIntegral<uint8_t>() /*pendingLimit*/, false); - reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>()); - bool periodFlushed = mFdp.ConsumeBool(); + reactor.startPeriodTransition(mFdp.ConsumeIntegral<nsecs_t>(), mFdp.ConsumeBool()); + bool periodFlushed = false; // Value does not matter, since this is an out + // param from addHwVsyncTimestamp. reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral<nsecs_t>() /*newPeriod*/, std::nullopt, &periodFlushed); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 713b71042a..e6be9a8b21 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -100,7 +100,7 @@ public: return true; } - void setDivisor(unsigned) override {} + void setRenderRate(Fps) override {} nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp index bf7cae9091..0b8c51ec1d 100644 --- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp @@ -544,6 +544,7 @@ void LayerRenderTypeTransactionTest::setBackgroundColorHelper(uint32_t layerType .apply(); { + SCOPED_TRACE("final color"); auto shot = screenshot(); shot->expectColor(Rect(0, 0, width, height), finalColor); shot->expectBorder(Rect(0, 0, width, height), Color::BLACK); diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp index e69db7c167..0ea0824732 100644 --- a/services/surfaceflinger/tests/MirrorLayer_test.cpp +++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp @@ -18,6 +18,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" +#include <android-base/properties.h> #include <private/android_filesystem_config.h> #include "LayerTransactionTest.h" #include "utils/TransactionUtils.h" @@ -119,15 +120,20 @@ TEST_F(MirrorLayerTest, MirrorColorLayer) { shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); } - // Remove child layer + if (base::GetBoolProperty("debug.sf.enable_legacy_frontend", true)) { + GTEST_SKIP() << "Skipping test because mirroring behavior changes with legacy frontend"; + } + + // Remove child layer and verify we can still mirror the layer when + // its offscreen. Transaction().reparent(mChildLayer, nullptr).apply(); { SCOPED_TRACE("Removed Child Layer"); auto shot = screenshot(); // Grandchild mirror - shot->expectColor(Rect(550, 550, 750, 750), Color::RED); + shot->expectColor(Rect(550, 550, 750, 750), Color::BLACK); // Child mirror - shot->expectColor(Rect(750, 750, 950, 950), Color::RED); + shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); } // Add grandchild layer to offscreen layer @@ -136,9 +142,9 @@ TEST_F(MirrorLayerTest, MirrorColorLayer) { SCOPED_TRACE("Added Grandchild Layer"); auto shot = screenshot(); // Grandchild mirror - shot->expectColor(Rect(550, 550, 750, 750), Color::RED); + shot->expectColor(Rect(550, 550, 750, 750), Color::WHITE); // Child mirror - shot->expectColor(Rect(750, 750, 950, 950), Color::RED); + shot->expectColor(Rect(750, 750, 950, 950), Color::BLACK); } // Add child layer diff --git a/services/surfaceflinger/tests/TextureFiltering_test.cpp b/services/surfaceflinger/tests/TextureFiltering_test.cpp index e9b1fbb354..d0ab105414 100644 --- a/services/surfaceflinger/tests/TextureFiltering_test.cpp +++ b/services/surfaceflinger/tests/TextureFiltering_test.cpp @@ -187,8 +187,6 @@ TEST_F(TextureFilteringTest, LayerCaptureWithCropNoFiltering) { // Expect no filtering because the output source crop and output buffer are the same size. TEST_F(TextureFilteringTest, OutputSourceCropDisplayFrameMatchNoFiltering) { - // Transaction().setCrop(mLayer, Rect{25, 25, 75, 75}).apply(); - gui::DisplayCaptureArgs captureArgs; captureArgs.displayToken = mDisplay; captureArgs.width = 50; @@ -224,4 +222,17 @@ TEST_F(TextureFilteringTest, ParentCropNoFiltering) { mCapture->expectColor(Rect{50, 25, 75, 75}, Color::BLUE); } +// Expect no filtering because parent's position transform shouldn't scale the layer. +TEST_F(TextureFilteringTest, ParentHasTransformNoFiltering) { + Transaction().setPosition(mParent, 100, 100).apply(); + + LayerCaptureArgs captureArgs; + captureArgs.layerHandle = mParent->getHandle(); + captureArgs.sourceCrop = Rect{0, 0, 100, 100}; + ScreenCapture::captureLayers(&mCapture, captureArgs); + + mCapture->expectColor(Rect{0, 0, 50, 100}, Color::RED); + mCapture->expectColor(Rect{50, 0, 100, 100}, Color::BLUE); +} + } // namespace android diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp index 53c3c3998f..d71486fca7 100644 --- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp +++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp @@ -18,61 +18,61 @@ #include <gui/SurfaceComposerClient.h> #include <private/android_filesystem_config.h> #include <future> -#include "utils/TransactionUtils.h" namespace android { using Transaction = SurfaceComposerClient::Transaction; using gui::DisplayInfo; using gui::WindowInfo; +using WindowInfosPredicate = std::function<bool(const std::vector<WindowInfo>&)>; + class WindowInfosListenerTest : public ::testing::Test { protected: void SetUp() override { seteuid(AID_SYSTEM); mClient = sp<SurfaceComposerClient>::make(); - mWindowInfosListener = sp<SyncWindowInfosListener>::make(); - mClient->addWindowInfosListener(mWindowInfosListener); } - void TearDown() override { - mClient->removeWindowInfosListener(mWindowInfosListener); - seteuid(AID_ROOT); - } + void TearDown() override { seteuid(AID_ROOT); } - struct SyncWindowInfosListener : public gui::WindowInfosListener { + struct WindowInfosListener : public gui::WindowInfosListener { public: + WindowInfosListener(WindowInfosPredicate predicate, std::promise<void>& promise) + : mPredicate(std::move(predicate)), mPromise(promise) {} + void onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos, const std::vector<DisplayInfo>&) override { - windowInfosPromise.set_value(windowInfos); - } - - std::vector<WindowInfo> waitForWindowInfos() { - std::future<std::vector<WindowInfo>> windowInfosFuture = - windowInfosPromise.get_future(); - std::vector<WindowInfo> windowInfos = windowInfosFuture.get(); - windowInfosPromise = std::promise<std::vector<WindowInfo>>(); - return windowInfos; + if (mPredicate(windowInfos)) { + mPromise.set_value(); + } } private: - std::promise<std::vector<WindowInfo>> windowInfosPromise; + WindowInfosPredicate mPredicate; + std::promise<void>& mPromise; }; sp<SurfaceComposerClient> mClient; - sp<SyncWindowInfosListener> mWindowInfosListener; + + bool waitForWindowInfosPredicate(WindowInfosPredicate predicate) { + std::promise<void> promise; + auto listener = sp<WindowInfosListener>::make(std::move(predicate), promise); + mClient->addWindowInfosListener(listener); + auto future = promise.get_future(); + bool satisfied = future.wait_for(std::chrono::seconds{1}) == std::future_status::ready; + mClient->removeWindowInfosListener(listener); + return satisfied; + } }; std::optional<WindowInfo> findMatchingWindowInfo(WindowInfo targetWindowInfo, std::vector<WindowInfo> windowInfos) { - std::optional<WindowInfo> foundWindowInfo = std::nullopt; for (WindowInfo windowInfo : windowInfos) { if (windowInfo.token == targetWindowInfo.token) { - foundWindowInfo = std::make_optional<>(windowInfo); - break; + return windowInfo; } } - - return foundWindowInfo; + return std::nullopt; } TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { @@ -92,15 +92,17 @@ TEST_F(WindowInfosListenerTest, WindowInfoAddedAndRemoved) { .setInputWindowInfo(surfaceControl, windowInfo) .apply(); - std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos(); - std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); - ASSERT_NE(std::nullopt, foundWindowInfo); + auto windowPresent = [&](const std::vector<WindowInfo>& windowInfos) { + return findMatchingWindowInfo(windowInfo, windowInfos).has_value(); + }; + ASSERT_TRUE(waitForWindowInfosPredicate(windowPresent)); Transaction().reparent(surfaceControl, nullptr).apply(); - windowInfos = mWindowInfosListener->waitForWindowInfos(); - foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); - ASSERT_EQ(std::nullopt, foundWindowInfo); + auto windowNotPresent = [&](const std::vector<WindowInfo>& windowInfos) { + return !findMatchingWindowInfo(windowInfo, windowInfos).has_value(); + }; + ASSERT_TRUE(waitForWindowInfosPredicate(windowNotPresent)); } TEST_F(WindowInfosListenerTest, WindowInfoChanged) { @@ -121,19 +123,28 @@ TEST_F(WindowInfosListenerTest, WindowInfoChanged) { .setInputWindowInfo(surfaceControl, windowInfo) .apply(); - std::vector<WindowInfo> windowInfos = mWindowInfosListener->waitForWindowInfos(); - std::optional<WindowInfo> foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); - ASSERT_NE(std::nullopt, foundWindowInfo); - ASSERT_TRUE(foundWindowInfo->touchableRegion.isEmpty()); + auto windowIsPresentAndTouchableRegionEmpty = [&](const std::vector<WindowInfo>& windowInfos) { + auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + if (!foundWindowInfo) { + return false; + } + return foundWindowInfo->touchableRegion.isEmpty(); + }; + ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionEmpty)); Rect touchableRegions(0, 0, 50, 50); windowInfo.addTouchableRegion(Rect(0, 0, 50, 50)); Transaction().setInputWindowInfo(surfaceControl, windowInfo).apply(); - windowInfos = mWindowInfosListener->waitForWindowInfos(); - foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); - ASSERT_NE(std::nullopt, foundWindowInfo); - ASSERT_TRUE(foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion)); + auto windowIsPresentAndTouchableRegionMatches = + [&](const std::vector<WindowInfo>& windowInfos) { + auto foundWindowInfo = findMatchingWindowInfo(windowInfo, windowInfos); + if (!foundWindowInfo) { + return false; + } + return foundWindowInfo->touchableRegion.hasSameRects(windowInfo.touchableRegion); + }; + ASSERT_TRUE(waitForWindowInfosPredicate(windowIsPresentAndTouchableRegionMatches)); } } // namespace android diff --git a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp index 0e214af706..5f9214c548 100644 --- a/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp +++ b/services/surfaceflinger/tests/tracing/TransactionTraceTestSuite.cpp @@ -91,6 +91,8 @@ struct LayerInfo { uint64_t curr_frame; float x; float y; + uint32_t bufferWidth; + uint32_t bufferHeight; }; bool operator==(const LayerInfo& lh, const LayerInfo& rh) { @@ -105,7 +107,8 @@ bool compareById(const LayerInfo& a, const LayerInfo& b) { 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; + << " y=" << info.y << " bufferWidth=" << info.bufferWidth + << " bufferHeight=" << info.bufferHeight; } struct find_id : std::unary_function<LayerInfo, bool> { @@ -114,6 +117,18 @@ struct find_id : std::unary_function<LayerInfo, bool> { bool operator()(LayerInfo const& m) const { return m.id == id; } }; +static LayerInfo getLayerInfoFromProto(::android::surfaceflinger::LayerProto& proto) { + return {proto.id(), + proto.name(), + proto.parent(), + proto.z(), + proto.curr_frame(), + proto.has_position() ? proto.position().x() : -1, + proto.has_position() ? proto.position().y() : -1, + proto.has_active_buffer() ? proto.active_buffer().width() : 0, + proto.has_active_buffer() ? proto.active_buffer().height() : 0}; +} + TEST_P(TransactionTraceTestSuite, validateEndState) { ASSERT_GT(mActualLayersTraceProto.entry_size(), 0); ASSERT_GT(mExpectedLayersTraceProto.entry_size(), 0); @@ -128,10 +143,7 @@ TEST_P(TransactionTraceTestSuite, validateEndState) { 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}); + expectedLayers.push_back(getLayerInfoFromProto(layer)); } std::sort(expectedLayers.begin(), expectedLayers.end(), compareById); @@ -139,10 +151,7 @@ TEST_P(TransactionTraceTestSuite, validateEndState) { 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}); + actualLayers.push_back(getLayerInfoFromProto(layer)); } std::sort(actualLayers.begin(), actualLayers.end(), compareById); diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 87c3c65a74..df3ffd288c 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -103,16 +103,18 @@ cc_test { "SurfaceFlinger_DestroyDisplayTest.cpp", "SurfaceFlinger_DisplayModeSwitching.cpp", "SurfaceFlinger_DisplayTransactionCommitTest.cpp", + "SurfaceFlinger_ExcludeDolbyVisionTest.cpp", "SurfaceFlinger_GetDisplayNativePrimariesTest.cpp", + "SurfaceFlinger_HdrOutputControlTest.cpp", "SurfaceFlinger_HotplugTest.cpp", + "SurfaceFlinger_InitializeDisplaysTest.cpp", + "SurfaceFlinger_MultiDisplayPacesetterTest.cpp", "SurfaceFlinger_NotifyPowerBoostTest.cpp", - "SurfaceFlinger_OnInitializeDisplaysTest.cpp", "SurfaceFlinger_PowerHintTest.cpp", "SurfaceFlinger_SetDisplayStateTest.cpp", "SurfaceFlinger_SetPowerModeInternalTest.cpp", "SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp", "SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp", - "SurfaceFlinger_ExcludeDolbyVisionTest.cpp", "SchedulerTest.cpp", "SetFrameRateTest.cpp", "RefreshRateSelectorTest.cpp", @@ -133,6 +135,7 @@ cc_test { "VSyncPredictorTest.cpp", "VSyncReactorTest.cpp", "VsyncConfigurationTest.cpp", + "VsyncScheduleTest.cpp", ], } diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 0416e93f1e..19a93e1820 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -99,7 +99,7 @@ public: ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); + mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _)) .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0))); @@ -122,36 +122,6 @@ public: ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - 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), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - TestableSurfaceFlinger::kTwoDisplayModes); - } - void setupForceGeometryDirty() { // TODO: This requires the visible region and other related // state to be set, and is problematic for BufferLayers since they are @@ -176,7 +146,6 @@ public: bool mDisplayOff = false; TestableSurfaceFlinger mFlinger; sp<DisplayDevice> mDisplay; - sp<DisplayDevice> mExternalDisplay; sp<compositionengine::mock::DisplaySurface> mDisplaySurface = sp<compositionengine::mock::DisplaySurface>::make(); sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); @@ -315,13 +284,16 @@ struct BaseDisplayVariant { constexpr auto kDisplayConnectionType = ui::DisplayConnectionType::Internal; constexpr bool kIsPrimary = true; - test->mDisplay = FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, - kDisplayConnectionType, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(test->mDisplaySurface) - .setNativeWindow(test->mNativeWindow) - .setSecure(Derived::IS_SECURE) - .setPowerMode(Derived::INIT_POWER_MODE) - .inject(); + test->mDisplay = + FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay, + kDisplayConnectionType, HWC_DISPLAY, kIsPrimary) + .setDisplaySurface(test->mDisplaySurface) + .setNativeWindow(test->mNativeWindow) + .setSecure(Derived::IS_SECURE) + .setPowerMode(Derived::INIT_POWER_MODE) + .setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector()) + .skipRegisterDisplay() + .inject(); Mock::VerifyAndClear(test->mNativeWindow.get()); constexpr bool kIsInternal = kDisplayConnectionType == ui::DisplayConnectionType::Internal; diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index e0b508aa73..e32cf8863b 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -29,9 +29,7 @@ using testing::SetArgPointee; using android::hardware::graphics::composer::hal::HWDisplayId; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; - -DisplayTransactionTest::DisplayTransactionTest() { +DisplayTransactionTest::DisplayTransactionTest(bool withMockScheduler) { 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()); @@ -48,7 +46,10 @@ DisplayTransactionTest::DisplayTransactionTest() { return nullptr; }); - injectMockScheduler(); + if (withMockScheduler) { + injectMockScheduler(PhysicalDisplayId::fromPort(0)); + } + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); injectMockComposer(0); @@ -61,7 +62,9 @@ DisplayTransactionTest::~DisplayTransactionTest() { mFlinger.resetScheduler(nullptr); } -void DisplayTransactionTest::injectMockScheduler() { +void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { + LOG_ALWAYS_FATAL_IF(mFlinger.scheduler()); + EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); EXPECT_CALL(*mEventThread, createEventConnection(_, _)) .WillOnce(Return(sp<EventThreadConnection>::make(mEventThread, @@ -74,10 +77,11 @@ void DisplayTransactionTest::injectMockScheduler() { mock::EventThread::kCallingUid, ResyncCallback()))); - mFlinger.setupScheduler(std::unique_ptr<scheduler::VsyncController>(mVsyncController), - std::unique_ptr<scheduler::VSyncTracker>(mVSyncTracker), + mFlinger.setupScheduler(std::make_unique<mock::VsyncController>(), + std::make_shared<mock::VSyncTracker>(), std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), + TestableSurfaceFlinger::DefaultDisplayMode{displayId}, TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); } diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 223f4db889..e64cb38b16 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -85,7 +85,7 @@ public: // -------------------------------------------------------------------- // Mock/Fake injection - void injectMockScheduler(); + void injectMockScheduler(PhysicalDisplayId); void injectMockComposer(int virtualDisplayCount); void injectFakeBufferQueueFactory(); void injectFakeNativeWindowSurfaceFactory(); @@ -128,8 +128,6 @@ public: renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); Hwc2::mock::Composer* mComposer = nullptr; - mock::VsyncController* mVsyncController = new mock::VsyncController; - mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker; mock::EventThread* mEventThread = new mock::EventThread; mock::EventThread* mSFEventThread = new mock::EventThread; @@ -139,7 +137,7 @@ public: surfaceflinger::mock::NativeWindowSurface* mNativeWindowSurface = nullptr; protected: - DisplayTransactionTest(); + DisplayTransactionTest(bool withMockScheduler = true); }; constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667; @@ -158,7 +156,6 @@ constexpr int POWER_MODE_LEET = 1337; // An out of range power mode value #define BOOL_SUBSTITUTE(TYPENAME) enum class TYPENAME : bool { FALSE = false, TRUE = true }; BOOL_SUBSTITUTE(Async); -BOOL_SUBSTITUTE(Critical); BOOL_SUBSTITUTE(Primary); BOOL_SUBSTITUTE(Secure); BOOL_SUBSTITUTE(Virtual); @@ -238,8 +235,8 @@ struct HwcDisplayIdGetter<PhysicalDisplayIdType<PhysicalDisplay>> { // 1) PhysicalDisplayIdType<...> for generated ID of physical display backed by HWC. // 2) HalVirtualDisplayIdType<...> for hard-coded ID of virtual display backed by HWC. // 3) GpuVirtualDisplayIdType for virtual display without HWC backing. -template <typename DisplayIdType, int width, int height, Critical critical, Async async, - Secure secure, Primary primary, int grallocUsage, int displayFlags> +template <typename DisplayIdType, int width, int height, Async async, Secure secure, + Primary primary, int grallocUsage, int displayFlags> struct DisplayVariant { using DISPLAY_ID = DisplayIdGetter<DisplayIdType>; using CONNECTION_TYPE = DisplayConnectionTypeGetter<DisplayIdType>; @@ -255,9 +252,6 @@ struct DisplayVariant { static constexpr Virtual VIRTUAL = IsPhysicalDisplayId<DisplayIdType>{} ? Virtual::FALSE : Virtual::TRUE; - // When creating native window surfaces for the framebuffer, whether those should be critical - static constexpr Critical CRITICAL = critical; - // When creating native window surfaces for the framebuffer, whether those should be async static constexpr Async ASYNC = async; @@ -486,17 +480,16 @@ constexpr uint32_t GRALLOC_USAGE_PHYSICAL_DISPLAY = constexpr int PHYSICAL_DISPLAY_FLAGS = 0x1; -template <typename PhysicalDisplay, int width, int height, Critical critical> +template <typename PhysicalDisplay, int width, int height> struct PhysicalDisplayVariant - : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical, - Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, - GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, - HwcDisplayVariant< - PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, - DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, critical, - Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, - GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, - PhysicalDisplay> {}; + : DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, Async::FALSE, + Secure::TRUE, PhysicalDisplay::PRIMARY, GRALLOC_USAGE_PHYSICAL_DISPLAY, + PHYSICAL_DISPLAY_FLAGS>, + HwcDisplayVariant<PhysicalDisplay::HWC_DISPLAY_ID, DisplayType::PHYSICAL, + DisplayVariant<PhysicalDisplayIdType<PhysicalDisplay>, width, height, + Async::FALSE, Secure::TRUE, PhysicalDisplay::PRIMARY, + GRALLOC_USAGE_PHYSICAL_DISPLAY, PHYSICAL_DISPLAY_FLAGS>, + PhysicalDisplay> {}; template <bool hasIdentificationData> struct PrimaryDisplay { @@ -508,14 +501,16 @@ struct PrimaryDisplay { static constexpr auto GET_IDENTIFICATION_DATA = getInternalEdid; }; -template <bool hasIdentificationData> -struct ExternalDisplay { - static constexpr auto CONNECTION_TYPE = ui::DisplayConnectionType::External; +template <ui::DisplayConnectionType connectionType, bool hasIdentificationData> +struct SecondaryDisplay { + static constexpr auto CONNECTION_TYPE = connectionType; static constexpr Primary PRIMARY = Primary::FALSE; static constexpr uint8_t PORT = 254; static constexpr HWDisplayId HWC_DISPLAY_ID = 1002; static constexpr bool HAS_IDENTIFICATION_DATA = hasIdentificationData; - static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; + static constexpr auto GET_IDENTIFICATION_DATA = + connectionType == ui::DisplayConnectionType::Internal ? getInternalEdid + : getExternalEdid; }; struct TertiaryDisplay { @@ -525,15 +520,18 @@ struct TertiaryDisplay { static constexpr auto GET_IDENTIFICATION_DATA = getExternalEdid; }; -// A primary display is a physical display that is critical -using PrimaryDisplayVariant = - PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160, Critical::TRUE>; +using PrimaryDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<false>, 3840, 2160>; + +using InnerDisplayVariant = PhysicalDisplayVariant<PrimaryDisplay<true>, 1840, 2208>; +using OuterDisplayVariant = + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::Internal, true>, 1080, + 2092>; -// An external display is physical display that is not critical. using ExternalDisplayVariant = - PhysicalDisplayVariant<ExternalDisplay<false>, 1920, 1280, Critical::FALSE>; + PhysicalDisplayVariant<SecondaryDisplay<ui::DisplayConnectionType::External, false>, 1920, + 1280>; -using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200, Critical::FALSE>; +using TertiaryDisplayVariant = PhysicalDisplayVariant<TertiaryDisplay, 1600, 1200>; // A virtual display not supported by the HWC. constexpr uint32_t GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY = 0; @@ -542,12 +540,11 @@ constexpr int VIRTUAL_DISPLAY_FLAGS = 0x0; template <int width, int height, Secure secure> struct NonHwcVirtualDisplayVariant - : DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, Async::TRUE, secure, - Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, - VIRTUAL_DISPLAY_FLAGS> { - using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Critical::FALSE, - Async::TRUE, secure, Primary::FALSE, - GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>; + : DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure, Primary::FALSE, + GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS> { + using Base = DisplayVariant<GpuVirtualDisplayIdType, width, height, Async::TRUE, secure, + Primary::FALSE, GRALLOC_USAGE_NONHWC_VIRTUAL_DISPLAY, + VIRTUAL_DISPLAY_FLAGS>; static void injectHwcDisplay(DisplayTransactionTest*) {} @@ -589,17 +586,14 @@ constexpr uint32_t GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY = GRALLOC_USAGE_HW_COMPOSER template <int width, int height, Secure secure> struct HwcVirtualDisplayVariant - : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, Async::TRUE, - secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, - VIRTUAL_DISPLAY_FLAGS>, - HwcDisplayVariant< - HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, - DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, - Async::TRUE, secure, Primary::FALSE, - GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>> { - using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Critical::FALSE, - Async::TRUE, secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER, - VIRTUAL_DISPLAY_FLAGS>; + : DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure, + Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, VIRTUAL_DISPLAY_FLAGS>, + HwcDisplayVariant<HWC_VIRTUAL_DISPLAY_HWC_DISPLAY_ID, DisplayType::VIRTUAL, + DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, + secure, Primary::FALSE, GRALLOC_USAGE_HWC_VIRTUAL_DISPLAY, + VIRTUAL_DISPLAY_FLAGS>> { + using Base = DisplayVariant<HalVirtualDisplayIdType<42>, width, height, Async::TRUE, secure, + Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER, VIRTUAL_DISPLAY_FLAGS>; using Self = HwcVirtualDisplayVariant<width, height, secure>; static std::shared_ptr<compositionengine::Display> injectCompositionDisplay( diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index f6bcadc586..f1cdca3ee1 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -125,7 +125,7 @@ protected: ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; - std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule; + std::shared_ptr<scheduler::VsyncSchedule> mVsyncSchedule; std::unique_ptr<impl::EventThread> mThread; sp<MockEventThreadConnection> mConnection; sp<MockEventThreadConnection> mThrottledConnection; @@ -140,12 +140,12 @@ EventThreadTest::EventThreadTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mVsyncSchedule = std::unique_ptr<scheduler::VsyncSchedule>( - new scheduler::VsyncSchedule(std::make_unique<mock::VSyncTracker>(), - std::make_unique<mock::VSyncDispatch>(), nullptr)); - - mock::VSyncDispatch& mockDispatch = - *static_cast<mock::VSyncDispatch*>(&mVsyncSchedule->getDispatch()); + auto mockDispatchPtr = std::make_shared<mock::VSyncDispatch>(); + mVsyncSchedule = std::shared_ptr<scheduler::VsyncSchedule>( + new scheduler::VsyncSchedule(INTERNAL_DISPLAY_ID, + std::make_shared<mock::VSyncTracker>(), mockDispatchPtr, + nullptr)); + mock::VSyncDispatch& mockDispatch = *mockDispatchPtr; EXPECT_CALL(mockDispatch, registerCallback(_, _)) .WillRepeatedly(Invoke(mVSyncCallbackRegisterRecorder.getInvocable())); EXPECT_CALL(mockDispatch, schedule(_, _)) @@ -189,10 +189,9 @@ void EventThreadTest::createThread() { }; mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); - mThread = - std::make_unique<impl::EventThread>(/*std::move(source), */ "EventThreadTest", - *mVsyncSchedule, mTokenManager.get(), throttleVsync, - getVsyncPeriod, kWorkDuration, kReadyDuration); + mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule, + mTokenManager.get(), throttleVsync, + getVsyncPeriod, kWorkDuration, kReadyDuration); // EventThread should register itself as VSyncSource callback. EXPECT_TRUE(mVSyncCallbackRegisterRecorder.waitForCall().has_value()); diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp index 1cd9e49051..f695b096a7 100644 --- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp @@ -29,9 +29,7 @@ #include "TestableSurfaceFlinger.h" #include "fake/FakeClock.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" #include "mock/MockFrameTimeline.h" -#include "mock/MockVsyncController.h" namespace android { @@ -47,7 +45,6 @@ using testing::UnorderedElementsAre; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; using gui::LayerMetadata; struct TestableFpsListener : public gui::BnFpsListener { @@ -77,7 +74,6 @@ protected: static constexpr uint32_t LAYER_FLAGS = 0; static constexpr int32_t PRIORITY_UNSET = -1; - void setupScheduler(); sp<Layer> createBufferStateLayer(LayerMetadata metadata); TestableSurfaceFlinger mFlinger; @@ -102,7 +98,7 @@ FpsReporterTest::FpsReporterTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); + mFlinger.setupMockScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFpsListener = sp<TestableFpsListener>::make(); @@ -120,33 +116,6 @@ sp<Layer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) { return sp<Layer>::make(args); } -void FpsReporterTest::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)); -} - namespace { TEST_F(FpsReporterTest, callsListeners) { diff --git a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp index ac63a0edbd..1c9aee7443 100644 --- a/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameRateSelectionPriorityTest.cpp @@ -24,8 +24,6 @@ #include "Layer.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" -#include "mock/MockVsyncController.h" namespace android { @@ -38,8 +36,6 @@ using testing::SetArgPointee; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; - /** * This class covers all the test that are related to refresh rate selection. */ @@ -56,7 +52,6 @@ protected: static constexpr uint32_t LAYER_FLAGS = 0; static constexpr int32_t PRIORITY_UNSET = -1; - void setupScheduler(); sp<Layer> createBufferStateLayer(); sp<Layer> createEffectLayer(); @@ -76,7 +71,7 @@ RefreshRateSelectionTest::RefreshRateSelectionTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); + mFlinger.setupMockScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); } @@ -108,37 +103,8 @@ void RefreshRateSelectionTest::commitTransaction(Layer* layer) { layer->commitTransaction(c); } -void RefreshRateSelectionTest::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)); -} - namespace { -/* ------------------------------------------------------------------------ - * Test cases - */ + TEST_F(RefreshRateSelectionTest, testPriorityOnBufferStateLayers) { mParent = createBufferStateLayer(); mChild = createBufferStateLayer(); diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp index 29aa7171ba..1b5c6e70f8 100644 --- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp +++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp @@ -25,15 +25,13 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" -#include "mock/MockVsyncController.h" namespace android { using testing::_; using testing::Mock; using testing::Return; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using gui::GameMode; using gui::LayerMetadata; @@ -43,7 +41,7 @@ public: 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()); - setupScheduler(); + mFlinger.setupMockScheduler(); setupComposer(); } @@ -59,33 +57,6 @@ public: return sp<Layer>::make(args); } - 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)); - } - void setupComposer() { mComposer = new Hwc2::mock::Composer(); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp index 763426a9f8..ddf3363244 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp @@ -17,12 +17,9 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "FrontEnd/LayerHandle.h" #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" -#include "Layer.h" #include "LayerHierarchyTest.h" -#include "gui/SurfaceComposerClient.h" #define UPDATE_AND_VERIFY(HIERARCHY) \ ({ \ @@ -207,7 +204,8 @@ TEST_F(LayerHierarchyTest, reparentFromRelativeParentWithSetLayer) { reparentRelativeLayer(11, 2); UPDATE_AND_VERIFY(hierarchyBuilder); - reparentRelativeLayer(11, UNASSIGNED_LAYER_ID); + // This calls setLayer + removeRelativeZ(11); UPDATE_AND_VERIFY(hierarchyBuilder); std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; @@ -263,6 +261,37 @@ TEST_F(LayerHierarchyTest, relativeChildMovesOffscreenIsNotTraversable) { EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); } +TEST_F(LayerHierarchyTest, reparentRelativeLayer) { + 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); + + reparentLayer(11, 1); + UPDATE_AND_VERIFY(hierarchyBuilder); + 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); + + setZ(11, 0); + UPDATE_AND_VERIFY(hierarchyBuilder); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 2}; + EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); + expectedTraversalPath = {}; + EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); +} + // mirror tests TEST_F(LayerHierarchyTest, canTraverseMirrorLayer) { LayerHierarchyBuilder hierarchyBuilder(mLifecycleManager.getLayers()); @@ -387,7 +416,7 @@ TEST_F(LayerHierarchyTest, childMovesOffscreenWhenRelativeParentDies) { EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); // remove relative parent so layer becomes onscreen again - reparentRelativeLayer(11, UNASSIGNED_LAYER_ID); + removeRelativeZ(11); UPDATE_AND_VERIFY(hierarchyBuilder); expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13}; @@ -437,10 +466,11 @@ TEST_F(LayerHierarchyTest, backgroundLayersAreBehindParentLayer) { updateBackgroundColor(1, 0.5); UPDATE_AND_VERIFY(hierarchyBuilder); - - std::vector<uint32_t> expectedTraversalPath = {1, 1222, 11, 111, 12, 121, 122, 1221, 13, 2}; + auto bgLayerId = LayerCreationArgs::getInternalLayerId(1); + std::vector<uint32_t> expectedTraversalPath = {1, bgLayerId, 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}; + expectedTraversalPath = {bgLayerId, 1, 11, 111, 12, 121, 122, 1221, 13, 2}; EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath); expectedTraversalPath = {}; EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath); diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h index 852cb91b2c..5b3c7efe92 100644 --- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h +++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h @@ -17,11 +17,10 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "FrontEnd/LayerHandle.h" +#include "Client.h" // temporarily needed for LayerCreationArgs +#include "FrontEnd/LayerCreationArgs.h" #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" -#include "Layer.h" -#include "gui/SurfaceComposerClient.h" namespace android::surfaceflinger::frontend { @@ -51,20 +50,21 @@ protected: createLayer(1221, 122); } - LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, wp<IBinder> parent, - wp<IBinder> mirror) { - LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + LayerCreationArgs createArgs(uint32_t id, bool canBeRoot, uint32_t parentId, + uint32_t layerIdToMirror) { + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; args.addToRoot = canBeRoot; - args.parentHandle = parent; - args.mirrorLayerHandle = mirror; + args.parentId = parentId; + args.layerIdToMirror = layerIdToMirror; return args; } - LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStack) { - LayerCreationArgs args(nullptr, nullptr, "testlayer", 0, {}, std::make_optional(id)); + LayerCreationArgs createDisplayMirrorArgs(uint32_t id, ui::LayerStack layerStackToMirror) { + LayerCreationArgs args(std::make_optional(id)); + args.name = "testlayer"; args.addToRoot = true; - args.parentHandle.clear(); - args.layerStackToMirror = layerStack; + args.layerStackToMirror = layerStackToMirror; return args; } @@ -90,17 +90,14 @@ protected: } virtual 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))); + createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/UNASSIGNED_LAYER_ID, + /*mirror=*/UNASSIGNED_LAYER_ID))); mLifecycleManager.addLayers(std::move(layers)); } void createDisplayMirrorLayer(uint32_t id, ui::LayerStack layerStack) { - sp<LayerHandle> handle = sp<LayerHandle>::make(id); - mHandles[id] = handle; std::vector<std::unique_ptr<RequestedLayerState>> layers; layers.emplace_back(std::make_unique<RequestedLayerState>( createDisplayMirrorArgs(/*id=*/id, layerStack))); @@ -108,62 +105,56 @@ protected: } virtual 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))); + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/UNASSIGNED_LAYER_ID))); mLifecycleManager.addLayers(std::move(layers)); } - void reparentLayer(uint32_t id, uint32_t newParentId) { + std::vector<TransactionState> reparentLayerTransaction(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().parentId = newParentId; transactions.back().states.front().state.what = layer_state_t::eReparent; - transactions.back().states.front().state.surface = mHandles[id]; - mLifecycleManager.applyTransactions(transactions); + transactions.back().states.front().relativeParentId = UNASSIGNED_LAYER_ID; + transactions.back().states.front().layerId = id; + return transactions; } - void reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { + void reparentLayer(uint32_t id, uint32_t newParentId) { + mLifecycleManager.applyTransactions(reparentLayerTransaction(id, newParentId)); + } + + std::vector<TransactionState> relativeLayerTransaction(uint32_t id, uint32_t relativeParentId) { std::vector<TransactionState> transactions; transactions.emplace_back(); transactions.back().states.push_back({}); + transactions.back().states.front().relativeParentId = relativeParentId; + transactions.back().states.front().state.what = layer_state_t::eRelativeLayerChanged; + transactions.back().states.front().layerId = id; + return transactions; + } - 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 reparentRelativeLayer(uint32_t id, uint32_t relativeParentId) { + mLifecycleManager.applyTransactions(relativeLayerTransaction(id, relativeParentId)); } - virtual 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]; + void removeRelativeZ(uint32_t id) { + 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().layerId = id; + mLifecycleManager.applyTransactions(transactions); + } - sp<LayerHandle> handle = sp<LayerHandle>::make(id); - mHandles[id] = handle; + virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) { std::vector<std::unique_ptr<RequestedLayerState>> layers; layers.emplace_back(std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentHandle, - /*mirror=*/mHandles[layerToMirror]))); + createArgs(/*id=*/id, /*canBeRoot=*/false, /*parent=*/parentId, + /*mirror=*/layerIdToMirror))); mLifecycleManager.addLayers(std::move(layers)); } @@ -172,8 +163,8 @@ protected: 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]; + transactions.back().states.front().state.bgColor.a = alpha; + transactions.back().states.front().layerId = id; mLifecycleManager.applyTransactions(transactions); } @@ -196,16 +187,19 @@ protected: mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); } - void setZ(uint32_t id, int32_t z) { + std::vector<TransactionState> setZTransaction(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.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; transactions.back().states.front().state.z = z; - mLifecycleManager.applyTransactions(transactions); + return transactions; + } + + void setZ(uint32_t id, int32_t z) { + mLifecycleManager.applyTransactions(setZTransaction(id, z)); } void setCrop(uint32_t id, const Rect& crop) { @@ -214,8 +208,7 @@ protected: transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eCropChanged; - transactions.back().states.front().state.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; transactions.back().states.front().state.crop = crop; mLifecycleManager.applyTransactions(transactions); } @@ -228,8 +221,7 @@ protected: transactions.back().states.front().state.what = layer_state_t::eFlagsChanged; transactions.back().states.front().state.flags = flags; transactions.back().states.front().state.mask = mask; - transactions.back().states.front().state.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; mLifecycleManager.applyTransactions(transactions); } @@ -239,8 +231,7 @@ protected: transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eAlphaChanged; - transactions.back().states.front().state.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; transactions.back().states.front().state.color.a = static_cast<half>(alpha); mLifecycleManager.applyTransactions(transactions); } @@ -257,8 +248,7 @@ protected: transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eColorChanged; transactions.back().states.front().state.color.rgb = rgb; - transactions.back().states.front().state.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; mLifecycleManager.applyTransactions(transactions); } @@ -268,14 +258,27 @@ protected: transactions.back().states.push_back({}); transactions.back().states.front().state.what = layer_state_t::eLayerStackChanged; - transactions.back().states.front().state.surface = mHandles[id]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(id); + transactions.back().states.front().layerId = id; transactions.back().states.front().state.layerStack = ui::LayerStack::fromValue(layerStack); mLifecycleManager.applyTransactions(transactions); } + void setTouchableRegion(uint32_t id, Region region) { + std::vector<TransactionState> transactions; + transactions.emplace_back(); + transactions.back().states.push_back({}); + + transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged; + transactions.back().states.front().layerId = id; + transactions.back().states.front().state.windowInfoHandle = + sp<gui::WindowInfoHandle>::make(); + auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo(); + inputInfo->touchableRegion = region; + inputInfo->token = sp<BBinder>::make(); + mLifecycleManager.applyTransactions(transactions); + } + LayerLifecycleManager mLifecycleManager; - std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; }; } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 8397f8d0ce..b7672768b4 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -112,7 +112,8 @@ protected: Fps desiredRefreshRate, int numFrames) { LayerHistory::Summary summary; for (int i = 0; i < numFrames; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += frameRate.getPeriodNsecs(); summary = summarizeLayerHistory(time); @@ -155,7 +156,8 @@ TEST_F(LayerHistoryTest, singleLayerNoVoteDefaultCompatibility) { EXPECT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */); EXPECT_TRUE(summarizeLayerHistory(time).empty()); @@ -177,7 +179,8 @@ TEST_F(LayerHistoryTest, singleLayerMinVoteDefaultCompatibility) { EXPECT_TRUE(summarizeLayerHistory(time).empty()); EXPECT_EQ(0, activeLayerCount()); - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); history().setDefaultFrameRateCompatibility(layer.get(), true /* contentDetectionEnabled */); auto summary = summarizeLayerHistory(time); @@ -205,7 +208,8 @@ TEST_F(LayerHistoryTest, oneLayer) { // Max returned if active layers have insufficient history. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -214,7 +218,8 @@ TEST_F(LayerHistoryTest, oneLayer) { // Max is returned since we have enough history but there is no timestamp votes. for (int i = 0; i < 10; i++) { - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -232,7 +237,8 @@ TEST_F(LayerHistoryTest, oneInvisibleLayer) { nsecs_t time = systemTime(); - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); auto summary = summarizeLayerHistory(time); ASSERT_EQ(1, summarizeLayerHistory(time).size()); // Layer is still considered inactive so we expect to get Min @@ -240,7 +246,8 @@ TEST_F(LayerHistoryTest, oneInvisibleLayer) { EXPECT_EQ(1, activeLayerCount()); EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false)); - history().record(layer.get(), 0, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), 0, time, + LayerHistory::LayerUpdateType::Buffer); summary = summarizeLayerHistory(time); EXPECT_TRUE(summarizeLayerHistory(time).empty()); @@ -257,7 +264,8 @@ TEST_F(LayerHistoryTest, explicitTimestamp) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -280,7 +288,8 @@ TEST_F(LayerHistoryTest, oneLayerNoVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -307,7 +316,8 @@ TEST_F(LayerHistoryTest, oneLayerMinVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -335,7 +345,8 @@ TEST_F(LayerHistoryTest, oneLayerMaxVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; } @@ -363,7 +374,8 @@ TEST_F(LayerHistoryTest, oneLayerExplicitVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -395,7 +407,8 @@ TEST_F(LayerHistoryTest, oneLayerExplicitExactVote) { nsecs_t time = systemTime(); for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; } @@ -441,7 +454,8 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer1 is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer1->getSequence(), layer1->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); summary = summarizeLayerHistory(time); } @@ -453,13 +467,15 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer2 is frequent and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; summary = summarizeLayerHistory(time); } // layer1 is still active but infrequent. - history().record(layer1.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer1->getSequence(), layer1->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(2, summary.size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summary[0].vote); @@ -472,7 +488,8 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer1 is no longer active. // layer2 is frequent and has low refresh rate. for (int i = 0; i < 2 * PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; summary = summarizeLayerHistory(time); } @@ -488,10 +505,12 @@ TEST_F(LayerHistoryTest, multipleLayers) { constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD; for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) { if (i % RATIO == 0) { - history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); } - history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; summary = summarizeLayerHistory(time); } @@ -504,7 +523,8 @@ TEST_F(LayerHistoryTest, multipleLayers) { EXPECT_EQ(2, frequentLayerCount(time)); // layer3 becomes recently active. - history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); summary = summarizeLayerHistory(time); ASSERT_EQ(2, summary.size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summary[0].vote); @@ -530,7 +550,8 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer2 still has low refresh rate. // layer3 becomes inactive. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer2.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer2->getSequence(), layer2->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += LO_FPS_PERIOD; summary = summarizeLayerHistory(time); } @@ -551,7 +572,8 @@ TEST_F(LayerHistoryTest, multipleLayers) { // layer3 becomes active and has high refresh rate. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE + FREQUENT_LAYER_WINDOW_SIZE + 1; i++) { - history().record(layer3.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer3->getSequence(), layer3->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; summary = summarizeLayerHistory(time); } @@ -582,7 +604,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { // the very first updates makes the layer frequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); EXPECT_EQ(1, layerCount()); @@ -593,7 +616,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { } // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); EXPECT_EQ(1, layerCount()); @@ -607,7 +631,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { // Now even if we post a quick few frame we should stay infrequent for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); @@ -618,7 +643,8 @@ TEST_F(LayerHistoryTest, inactiveLayers) { } // More quick frames will get us to frequent again - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += HI_FPS_PERIOD; EXPECT_EQ(1, layerCount()); @@ -645,9 +671,10 @@ TEST_F(LayerHistoryTest, invisibleExplicitLayer) { nsecs_t time = systemTime(); // Post a buffer to the layers to make them active - history().record(explicitVisiblelayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); - history().record(explicitInvisiblelayer.get(), time, time, - LayerHistory::LayerUpdateType::Buffer); + history().record(explicitVisiblelayer->getSequence(), explicitVisiblelayer->getLayerProps(), + time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(explicitInvisiblelayer->getSequence(), explicitInvisiblelayer->getLayerProps(), + time, time, LayerHistory::LayerUpdateType::Buffer); EXPECT_EQ(2, layerCount()); ASSERT_EQ(1, summarizeLayerHistory(time).size()); @@ -673,7 +700,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { // layer is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); } @@ -684,7 +712,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(0, animatingLayerCount(time)); // another update with the same cadence keep in infrequent - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); ASSERT_EQ(1, summarizeLayerHistory(time).size()); @@ -694,7 +723,8 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(0, animatingLayerCount(time)); // an update as animation will immediately vote for Max - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::AnimationTX); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::AnimationTX); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); ASSERT_EQ(1, summarizeLayerHistory(time).size()); @@ -719,7 +749,8 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { // Fill up the window with frequent updates for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += (60_Hz).getPeriodNsecs(); EXPECT_EQ(1, layerCount()); @@ -731,7 +762,8 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Heuristic, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(60_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate); @@ -741,9 +773,11 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { // posting more infrequent buffer should make the layer infrequent time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += (MAX_FREQUENT_LAYER_PERIOD_NS + 1ms).count(); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -751,7 +785,8 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { EXPECT_EQ(0, animatingLayerCount(time)); // posting another buffer should keep the layer infrequent - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -759,8 +794,10 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { EXPECT_EQ(0, animatingLayerCount(time)); // posting more buffers would mean starting of an animation, so making the layer frequent - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -769,7 +806,8 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { // posting a buffer after long inactivity should retain the layer as active time += std::chrono::nanoseconds(3s).count(); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -778,7 +816,8 @@ TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { // posting another buffer should keep the layer frequent time += (60_Hz).getPeriodNsecs(); - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1, summarizeLayerHistory(time).size()); EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); EXPECT_EQ(1, activeLayerCount()); @@ -801,7 +840,8 @@ TEST_F(LayerHistoryTest, getFramerate) { // layer is active but infrequent. for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { - history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); } @@ -869,10 +909,10 @@ TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) { const nsecs_t startTime = systemTime(); const std::chrono::nanoseconds heuristicUpdateDelta = 41'666'667ns; - history().record(heuristicLayer.get(), startTime, startTime, - LayerHistory::LayerUpdateType::Buffer); - history().record(infrequentLayer.get(), startTime, startTime, - LayerHistory::LayerUpdateType::Buffer); + history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), startTime, + startTime, LayerHistory::LayerUpdateType::Buffer); + history().record(infrequentLayer->getSequence(), heuristicLayer->getLayerProps(), startTime, + startTime, LayerHistory::LayerUpdateType::Buffer); nsecs_t time = startTime; nsecs_t lastInfrequentUpdate = startTime; @@ -880,14 +920,15 @@ TEST_P(LayerHistoryTestParameterized, HeuristicLayerWithInfrequentLayer) { int infrequentLayerUpdates = 0; while (infrequentLayerUpdates <= totalInfrequentLayerUpdates) { time += heuristicUpdateDelta.count(); - history().record(heuristicLayer.get(), time, time, LayerHistory::LayerUpdateType::Buffer); + history().record(heuristicLayer->getSequence(), heuristicLayer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); if (time - lastInfrequentUpdate >= infrequentUpdateDelta.count()) { ALOGI("submitting infrequent frame [%d/%d]", infrequentLayerUpdates, totalInfrequentLayerUpdates); lastInfrequentUpdate = time; - history().record(infrequentLayer.get(), time, time, - LayerHistory::LayerUpdateType::Buffer); + history().record(infrequentLayer->getSequence(), infrequentLayer->getLayerProps(), time, + time, LayerHistory::LayerUpdateType::Buffer); infrequentLayerUpdates++; } diff --git a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp index 89440a689c..14b8e4bbac 100644 --- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp @@ -17,25 +17,14 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "FrontEnd/LayerHandle.h" #include "FrontEnd/LayerLifecycleManager.h" -#include "Layer.h" -#include "gui/SurfaceComposerClient.h" +#include "LayerHierarchyTest.h" +#include "TransactionState.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 \ @@ -66,69 +55,24 @@ public: std::unordered_set<uint32_t> mActualLayersDestroyed; }; -class LayerLifecycleManagerTest : public testing::Test { +class LayerLifecycleManagerTest : public LayerHierarchyTestBase { protected: std::unique_ptr<RequestedLayerState> rootLayer(uint32_t id) { - return std::make_unique<RequestedLayerState>( - createArgs(/*id=*/id, /*canBeRoot=*/true, /*parent=*/nullptr, /*mirror=*/nullptr)); + return std::make_unique<RequestedLayerState>(createArgs(/*id=*/id, /*canBeRoot=*/true, + /*parent=*/UNASSIGNED_LAYER_ID, + /*mirror=*/UNASSIGNED_LAYER_ID)); } 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; + parentId, + /*mirror=*/UNASSIGNED_LAYER_ID)); } RequestedLayerState* getRequestedLayerState(LayerLifecycleManager& lifecycleManager, uint32_t layerId) { return lifecycleManager.getLayerFromId(layerId); } - - std::unordered_map<uint32_t, sp<LayerHandle>> mHandles; }; TEST_F(LayerLifecycleManagerTest, addLayers) { @@ -153,16 +97,7 @@ TEST_F(LayerLifecycleManagerTest, updateLayerStates) { 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(); + lifecycleManager.applyTransactions(setZTransaction(1, 2)); auto& managedLayers = lifecycleManager.getLayers(); ASSERT_EQ(managedLayers.size(), 1u); @@ -177,11 +112,12 @@ TEST_F(LayerLifecycleManagerTest, updateLayerStates) { EXPECT_FALSE(managedLayers.front()->changes.test(RequestedLayerState::Changes::Z)); // apply transactions that do not affect the hierarchy + std::vector<TransactionState> transactions; 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; + transactions.back().states.front().layerId = 1; lifecycleManager.applyTransactions(transactions); EXPECT_FALSE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); lifecycleManager.commitChanges(); @@ -232,7 +168,7 @@ TEST_F(LayerLifecycleManagerTest, offscreenLayerIsDestroyed) { listener->expectLayersAdded({1, 2, 3}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID)); lifecycleManager.commitChanges(); listener->expectLayersAdded({}); listener->expectLayersDestroyed({}); @@ -257,7 +193,7 @@ TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithHandleIsNotDestroyed) { listener->expectLayersAdded({1, 2, 3, 4}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID)); lifecycleManager.onHandlesDestroyed({3}); lifecycleManager.commitChanges(); listener->expectLayersAdded({}); @@ -278,7 +214,7 @@ TEST_F(LayerLifecycleManagerTest, offscreenChildLayerWithoutHandleIsDestroyed) { listener->expectLayersAdded({1, 2, 3, 4}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({reparentLayer(3, UNASSIGNED_LAYER_ID)}); + lifecycleManager.applyTransactions(reparentLayerTransaction(3, UNASSIGNED_LAYER_ID)); lifecycleManager.onHandlesDestroyed({3, 4}); lifecycleManager.commitChanges(); listener->expectLayersAdded({}); @@ -300,9 +236,9 @@ TEST_F(LayerLifecycleManagerTest, reparentingDoesNotAffectRelativeZ) { listener->expectLayersAdded({1, 2, 3, 4}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({makeRelative(4, 1)}); + lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1)); EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); - lifecycleManager.applyTransactions({reparentLayer(4, 2)}); + lifecycleManager.applyTransactions(reparentLayerTransaction(4, 2)); EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); lifecycleManager.commitChanges(); @@ -325,9 +261,9 @@ TEST_F(LayerLifecycleManagerTest, reparentingToNullRemovesRelativeZ) { listener->expectLayersAdded({1, 2, 3, 4}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({makeRelative(4, 1)}); + lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1)); EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); - lifecycleManager.applyTransactions({reparentLayer(4, UNASSIGNED_LAYER_ID)}); + lifecycleManager.applyTransactions(reparentLayerTransaction(4, UNASSIGNED_LAYER_ID)); EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); lifecycleManager.commitChanges(); @@ -350,9 +286,9 @@ TEST_F(LayerLifecycleManagerTest, setZRemovesRelativeZ) { listener->expectLayersAdded({1, 2, 3, 4}); listener->expectLayersDestroyed({}); - lifecycleManager.applyTransactions({makeRelative(4, 1)}); + lifecycleManager.applyTransactions(relativeLayerTransaction(4, 1)); EXPECT_TRUE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); - lifecycleManager.applyTransactions({setLayer(4, 1)}); + lifecycleManager.applyTransactions(setZTransaction(4, 1)); EXPECT_FALSE(getRequestedLayerState(lifecycleManager, 4)->isRelativeOf); lifecycleManager.commitChanges(); @@ -372,10 +308,9 @@ TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) { 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.bgColor.a = 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.back().states.front().layerId = 1; lifecycleManager.applyTransactions(transactions); auto& managedLayers = lifecycleManager.getLayers(); @@ -383,9 +318,10 @@ TEST_F(LayerLifecycleManagerTest, canAddBackgroundLayer) { EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); lifecycleManager.commitChanges(); - listener->expectLayersAdded({1, 2}); + auto bgLayerId = LayerCreationArgs::getInternalLayerId(1); + listener->expectLayersAdded({1, bgLayerId}); listener->expectLayersDestroyed({}); - EXPECT_EQ(getRequestedLayerState(lifecycleManager, 2)->color.a, 0.5_hf); + EXPECT_EQ(getRequestedLayerState(lifecycleManager, bgLayerId)->color.a, 0.5_hf); } TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { @@ -400,15 +336,14 @@ TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { 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.bgColor.a = 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.back().states.front().layerId = 1; transactions.emplace_back(); transactions.back().states.push_back({}); - transactions.back().states.front().state.bgColorAlpha = 0; + transactions.back().states.front().state.bgColor.a = 0; transactions.back().states.front().state.what = layer_state_t::eBackgroundColorChanged; - transactions.back().states.front().state.surface = handle; + transactions.back().states.front().layerId = 1; lifecycleManager.applyTransactions(transactions); @@ -417,8 +352,9 @@ TEST_F(LayerLifecycleManagerTest, canDestroyBackgroundLayer) { EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); lifecycleManager.commitChanges(); - listener->expectLayersAdded({1, 2}); - listener->expectLayersDestroyed({2}); + auto bgLayerId = LayerCreationArgs::getInternalLayerId(1); + listener->expectLayersAdded({1, bgLayerId}); + listener->expectLayersDestroyed({bgLayerId}); } TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { @@ -433,10 +369,9 @@ TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { 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.bgColor.a = 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.back().states.front().layerId = 1; transactions.emplace_back(); lifecycleManager.applyTransactions(transactions); lifecycleManager.onHandlesDestroyed({1}); @@ -446,8 +381,9 @@ TEST_F(LayerLifecycleManagerTest, onParentDestroyDestroysBackgroundLayer) { EXPECT_TRUE(lifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Hierarchy)); lifecycleManager.commitChanges(); - listener->expectLayersAdded({1, 2}); - listener->expectLayersDestroyed({1, 2}); + auto bgLayerId = LayerCreationArgs::getInternalLayerId(1); + listener->expectLayersAdded({1, bgLayerId}); + listener->expectLayersDestroyed({1, bgLayerId}); } } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index aa6a14ed17..b8c47817ca 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -17,11 +17,9 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> -#include "FrontEnd/LayerHandle.h" #include "FrontEnd/LayerHierarchy.h" #include "FrontEnd/LayerLifecycleManager.h" #include "FrontEnd/LayerSnapshotBuilder.h" -#include "Layer.h" #include "LayerHierarchyTest.h" #define UPDATE_AND_VERIFY(BUILDER, ...) \ @@ -75,14 +73,14 @@ protected: mHierarchyBuilder.update(mLifecycleManager.getLayers(), mLifecycleManager.getDestroyedLayers()); } - LayerSnapshotBuilder::Args args{ - .root = mHierarchyBuilder.getHierarchy(), - .layerLifecycleManager = mLifecycleManager, - .includeMetadata = false, - .displays = mFrontEndDisplayInfos, - .displayChanges = hasDisplayChanges, - .globalShadowSettings = globalShadowSettings, - }; + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = hasDisplayChanges, + .globalShadowSettings = globalShadowSettings, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; actualBuilder.update(args); // rebuild layer snapshots from scratch and verify that it matches the updated state. @@ -100,6 +98,9 @@ protected: } LayerSnapshot* getSnapshot(uint32_t layerId) { return mSnapshotBuilder.getSnapshot(layerId); } + LayerSnapshot* getSnapshot(const LayerHierarchy::TraversalPath path) { + return mSnapshotBuilder.getSnapshot(path); + } LayerHierarchyBuilder mHierarchyBuilder{{}}; LayerSnapshotBuilder mSnapshotBuilder; @@ -111,23 +112,25 @@ const std::vector<uint32_t> LayerSnapshotTest::STARTING_ZORDER = {1, 11, 111 122, 1221, 13, 2}; TEST_F(LayerSnapshotTest, buildSnapshot) { - LayerSnapshotBuilder::Args args{ - .root = mHierarchyBuilder.getHierarchy(), - .layerLifecycleManager = mLifecycleManager, - .includeMetadata = false, - .displays = mFrontEndDisplayInfos, - .globalShadowSettings = globalShadowSettings, - }; + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; LayerSnapshotBuilder builder(args); } TEST_F(LayerSnapshotTest, updateSnapshot) { - LayerSnapshotBuilder::Args args{ - .root = mHierarchyBuilder.getHierarchy(), - .layerLifecycleManager = mLifecycleManager, - .includeMetadata = false, - .displays = mFrontEndDisplayInfos, - .globalShadowSettings = globalShadowSettings, + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .globalShadowSettings = globalShadowSettings, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {} + }; LayerSnapshotBuilder builder; @@ -263,7 +266,7 @@ TEST_F(LayerSnapshotTest, GameMode) { transactions.back().states.front().state.what = layer_state_t::eMetadataChanged; transactions.back().states.front().state.metadata = LayerMetadata(); transactions.back().states.front().state.metadata.setInt32(METADATA_GAME_MODE, 42); - transactions.back().states.front().state.surface = mHandles[1]; + transactions.back().states.front().layerId = 1; transactions.back().states.front().state.layerId = static_cast<int32_t>(1); mLifecycleManager.applyTransactions(transactions); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); @@ -292,8 +295,7 @@ TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) { ANATIVEWINDOW_FRAME_RATE_EXACT; transactions.back().states.front().state.changeFrameRateStrategy = ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS; - transactions.back().states.front().state.surface = mHandles[11]; - transactions.back().states.front().state.layerId = static_cast<int32_t>(11); + transactions.back().states.front().layerId = 11; mLifecycleManager.applyTransactions(transactions); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); @@ -320,7 +322,7 @@ TEST_F(LayerSnapshotTest, NoLayerVoteForParentWithChildVotes) { // └── 2 // ROOT (DISPLAY 1) // └── 3 (mirrors display 0) -TEST_F(LayerSnapshotTest, displayMirrorRespects) { +TEST_F(LayerSnapshotTest, displayMirrorRespectsLayerSkipScreenshotFlag) { setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); setLayerStack(3, 1); @@ -329,4 +331,96 @@ TEST_F(LayerSnapshotTest, displayMirrorRespects) { UPDATE_AND_VERIFY(mSnapshotBuilder, expected); } +// ROOT (DISPLAY 0) +// ├── 1 +// │ ├── 11 +// │ │ └── 111 +// │ └── 13 +// └── 2 +// ROOT (DISPLAY 3) +// └── 3 (mirrors display 0) +TEST_F(LayerSnapshotTest, mirrorLayerGetsCorrectLayerStack) { + reparentLayer(12, UNASSIGNED_LAYER_ID); + createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); + setLayerStack(3, 3); + createDisplayMirrorLayer(4, ui::LayerStack::fromValue(0)); + setLayerStack(4, 4); + + std::vector<uint32_t> expected = {4, 1, 11, 111, 13, 2, 3, 1, 11, + 111, 13, 2, 1, 11, 111, 13, 2}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 3})->outputFilter.layerStack.id, 3u); + EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootId = 4})->outputFilter.layerStack.id, 4u); +} + +// ROOT (DISPLAY 0) +// ├── 1 (crop 50x50) +// │ ├── 11 +// │ │ └── 111 +// │ └── 13 +// └── 2 +// ROOT (DISPLAY 3) +// └── 3 (mirrors display 0) (crop 100x100) +TEST_F(LayerSnapshotTest, mirrorLayerTouchIsCroppedByMirrorRoot) { + reparentLayer(12, UNASSIGNED_LAYER_ID); + createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); + setLayerStack(3, 3); + setCrop(1, Rect{50, 50}); + setCrop(3, Rect{100, 100}); + setCrop(111, Rect{200, 200}); + Region touch{Rect{0, 0, 1000, 1000}}; + setTouchableRegion(111, touch); + std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 13, 2}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + EXPECT_TRUE(getSnapshot({.id = 111})->inputInfo.touchableRegion.hasSameRects(touch)); + Region touchCroppedByMirrorRoot{Rect{0, 0, 50, 50}}; + EXPECT_TRUE(getSnapshot({.id = 111, .mirrorRootId = 3}) + ->inputInfo.touchableRegion.hasSameRects(touchCroppedByMirrorRoot)); +} + +TEST_F(LayerSnapshotTest, canRemoveDisplayMirror) { + setFlags(12, layer_state_t::eLayerSkipScreenshot, layer_state_t::eLayerSkipScreenshot); + createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); + setLayerStack(3, 1); + std::vector<uint32_t> expected = {3, 1, 11, 111, 13, 2, 1, 11, 111, 12, 121, 122, 1221, 13, 2}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + destroyLayerHandle(3); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); +} + +TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterMirroring) { + size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size(); + createDisplayMirrorLayer(3, ui::LayerStack::fromValue(0)); + setLayerStack(3, 1); + std::vector<uint32_t> expected = {3, 1, 11, 111, 12, 121, 122, 1221, 13, 2, + 1, 11, 111, 12, 121, 122, 1221, 13, 2}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + destroyLayerHandle(3); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size()); +} + +// Rel z doesn't create duplicate snapshots but this is for completeness +TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterRelZ) { + size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size(); + reparentRelativeLayer(13, 11); + UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 13, 111, 12, 121, 122, 1221, 2}); + setZ(13, 0); + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(startingNumSnapshots, mSnapshotBuilder.getSnapshots().size()); +} + +TEST_F(LayerSnapshotTest, cleanUpUnreachableSnapshotsAfterLayerDestruction) { + size_t startingNumSnapshots = mSnapshotBuilder.getSnapshots().size(); + destroyLayerHandle(2); + destroyLayerHandle(122); + + std::vector<uint32_t> expected = {1, 11, 111, 12, 121, 122, 1221, 13}; + UPDATE_AND_VERIFY(mSnapshotBuilder, expected); + + EXPECT_LE(startingNumSnapshots - 2, mSnapshotBuilder.getSnapshots().size()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp index ee42e19c34..803e807d7b 100644 --- a/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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,15 +16,8 @@ #include "LayerTestUtils.h" -#include "mock/MockEventThread.h" - namespace android { -using testing::_; -using testing::Return; - -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; - sp<Layer> BufferStateLayerFactory::createLayer(TestableSurfaceFlinger& flinger) { sp<Client> client; LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, @@ -44,36 +37,7 @@ std::string PrintToStringParamName( } BaseLayerTest::BaseLayerTest() { - setupScheduler(); -} - -void BaseLayerTest::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), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - TestableSurfaceFlinger::kTwoDisplayModes); + mFlinger.setupMockScheduler(); } } // namespace android diff --git a/services/surfaceflinger/tests/unittests/LayerTestUtils.h b/services/surfaceflinger/tests/unittests/LayerTestUtils.h index ab446fafeb..0773d9081e 100644 --- a/services/surfaceflinger/tests/unittests/LayerTestUtils.h +++ b/services/surfaceflinger/tests/unittests/LayerTestUtils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -63,8 +63,6 @@ class BaseLayerTest : public ::testing::TestWithParam<std::shared_ptr<LayerFacto protected: BaseLayerTest(); - void setupScheduler(); - TestableSurfaceFlinger mFlinger; }; diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 7aa5201f2f..8f1b450b06 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -67,12 +67,12 @@ struct MockTokenManager : frametimeline::TokenManager { struct MessageQueueTest : testing::Test { void SetUp() override { - EXPECT_CALL(mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); + EXPECT_CALL(*mVSyncDispatch, registerCallback(_, "sf")).WillOnce(Return(mCallbackToken)); EXPECT_NO_FATAL_FAILURE(mEventQueue.initVsync(mVSyncDispatch, mTokenManager, kDuration)); - EXPECT_CALL(mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); + EXPECT_CALL(*mVSyncDispatch, unregisterCallback(mCallbackToken)).Times(1); } - mock::VSyncDispatch mVSyncDispatch; + std::shared_ptr<mock::VSyncDispatch> mVSyncDispatch = std::make_shared<mock::VSyncDispatch>(); MockTokenManager mTokenManager; TestableMessageQueue mEventQueue; @@ -90,7 +90,7 @@ TEST_F(MessageQueueTest, commit) { .earliestVsync = 0}; EXPECT_FALSE(mEventQueue.getScheduledFrameTime()); - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); @@ -103,13 +103,13 @@ TEST_F(MessageQueueTest, commitTwice) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count()); - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); @@ -122,7 +122,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); ASSERT_TRUE(mEventQueue.getScheduledFrameTime()); @@ -149,7 +149,7 @@ TEST_F(MessageQueueTest, commitTwiceWithCallback) { .readyDuration = 0, .earliestVsync = kPresentTime.ns()}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } @@ -161,7 +161,7 @@ TEST_F(MessageQueueTest, commitWithDurationChange) { .readyDuration = 0, .earliestVsync = 0}; - EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); + EXPECT_CALL(*mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0)); EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame()); } diff --git a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp index aafc323a9b..d08e12c7a4 100644 --- a/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/OneShotTimerTest.cpp @@ -224,7 +224,8 @@ TEST_F(OneShotTimerTest, DISABLED_timeoutCallbackExecutionTest) { EXPECT_FALSE(mResetTimerCallback.waitForUnexpectedCall().has_value()); } -TEST_F(OneShotTimerTest, noCallbacksAfterStopAndResetTest) { +// TODO(b/186417847) This test is flaky. Reenable once fixed. +TEST_F(OneShotTimerTest, DISABLED_noCallbacksAfterStopAndResetTest) { fake::FakeClock* clock = new fake::FakeClock(); mIdleTimer = std::make_unique<scheduler::OneShotTimer>("TestTimer", 1ms, mResetTimerCallback.getInvocable(), diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index f4d052d359..63ed87b846 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1455,6 +1455,24 @@ TEST_P(RefreshRateSelectorTest, lr.name = "ExplicitExactOrMultiple 29.97 Hz"; EXPECT_EQ(kModeId60Frac, selector.getBestFrameRateMode(layers)->getId()); } + + // Test that 29.97 will choose 30 if 59.94 is not supported + { + auto selector = createSelector(makeModes(kMode30, kMode60), kModeId60); + + lr.desiredRefreshRate = 29.97_Hz; + lr.name = "ExplicitExactOrMultiple 29.97 Hz"; + EXPECT_EQ(kModeId30, selector.getBestFrameRateMode(layers)->getId()); + } + + // Test that 59.94 will choose 60 if 59.94 is not supported + { + auto selector = createSelector(makeModes(kMode60, kMode30Frac, kMode30), kModeId60); + + lr.desiredRefreshRate = 59.94_Hz; + lr.name = "ExplicitExactOrMultiple 59.94 Hz"; + EXPECT_EQ(kModeId60, selector.getBestFrameRateMode(layers)->getId()); + } } TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_ExplicitExact_WithFractionalRefreshRates) { @@ -2981,6 +2999,12 @@ TEST_P(RefreshRateSelectorTest, noLowerFrameRateOnMinVote) { layers[0].name = "Test layer"; layers[0].vote = LayerVoteType::Min; EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); + + constexpr FpsRanges kCappedAt60 = {{30_Hz, 90_Hz}, {30_Hz, 60_Hz}}; + EXPECT_EQ(SetPolicyResult::Changed, + selector.setDisplayManagerPolicy( + {DisplayModeId(kModeId60), kCappedAt60, kCappedAt60})); + EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, selector.getBestScoredFrameRate(layers).frameRateMode); } TEST_P(RefreshRateSelectorTest, frameRateIsCappedByPolicy) { diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 4b15385fa8..dc76b4c90f 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <ftl/fake_guard.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <log/log.h> @@ -122,12 +123,6 @@ TEST_F(SchedulerTest, invalidConnectionHandle) { EXPECT_CALL(*mEventThread, onHotplugReceived(_, _)).Times(0); mScheduler->onHotplugReceived(handle, kDisplayId1, false); - EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(0); - mScheduler->onScreenAcquired(handle); - - EXPECT_CALL(*mEventThread, onScreenReleased()).Times(0); - mScheduler->onScreenReleased(handle); - std::string output; EXPECT_CALL(*mEventThread, dump(_)).Times(0); mScheduler->dump(handle, output); @@ -147,12 +142,6 @@ TEST_F(SchedulerTest, validConnectionHandle) { EXPECT_CALL(*mEventThread, onHotplugReceived(kDisplayId1, false)).Times(1); mScheduler->onHotplugReceived(mConnectionHandle, kDisplayId1, false); - EXPECT_CALL(*mEventThread, onScreenAcquired()).Times(1); - mScheduler->onScreenAcquired(mConnectionHandle); - - EXPECT_CALL(*mEventThread, onScreenReleased()).Times(1); - mScheduler->onScreenReleased(mConnectionHandle); - std::string output("dump"); EXPECT_CALL(*mEventThread, dump(output)).Times(1); mScheduler->dump(mConnectionHandle, output); @@ -172,11 +161,12 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup // recordLayerHistory should be a noop ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); + mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; - mScheduler->setDisplayPowerMode(kPowerModeOn); + FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn)); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); @@ -196,7 +186,8 @@ TEST_F(SchedulerTest, updateDisplayModes) { kDisplay1Mode60->getId())); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); + mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, + LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(1u, mScheduler->getNumActiveLayers()); } @@ -245,10 +236,11 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { const sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger()); EXPECT_CALL(*layer, isVisible()).WillOnce(Return(true)); - mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); + mScheduler->recordLayerHistory(layer->getSequence(), layer->getLayerProps(), 0, + LayerHistory::LayerUpdateType::Buffer); constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; - mScheduler->setDisplayPowerMode(kPowerModeOn); + FTL_FAKE_GUARD(kMainThreadContext, mScheduler->setDisplayPowerMode(kDisplayId1, kPowerModeOn)); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp index 6adcd5259d..44ab569bd1 100644 --- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp +++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp @@ -380,8 +380,10 @@ TEST_P(SetFrameRateTest, SetOnParentActivatesTree) { commitTransaction(); auto& history = mFlinger.mutableScheduler().mutableLayerHistory(); - history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); - history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer); + history.record(parent->getSequence(), parent->getLayerProps(), 0, 0, + LayerHistory::LayerUpdateType::Buffer); + history.record(child->getSequence(), child->getLayerProps(), 0, 0, + LayerHistory::LayerUpdateType::Buffer); const auto selectorPtr = mFlinger.mutableScheduler().refreshRateSelector(); const auto summary = history.summarize(*selectorPtr, 0); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 43af595ca7..e17654602b 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -50,7 +50,7 @@ public: mFlinger.configureAndCommit(); mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this) - .setDisplayModes(kModes, kModeId60, std::move(selectorPtr)) + .setRefreshRateSelector(std::move(selectorPtr)) .inject(); // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy @@ -100,7 +100,7 @@ void DisplayModeSwitchingTest::setupScheduler( ResyncCallback()))); auto vsyncController = std::make_unique<mock::VsyncController>(); - auto vsyncTracker = std::make_unique<mock::VSyncTracker>(); + auto vsyncTracker = std::make_shared<mock::VSyncTracker>(); EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); EXPECT_CALL(*vsyncTracker, currentPeriod()) @@ -109,8 +109,8 @@ void DisplayModeSwitchingTest::setupScheduler( EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - std::move(selectorPtr)); + std::move(selectorPtr), + TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp); } TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) { @@ -119,7 +119,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRe ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); - mFlinger.onActiveDisplayChanged(*mDisplay); + mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), mock::createDisplayModeSpecs(kModeId90.value(), false, 0, @@ -159,7 +159,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefres ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); - mFlinger.onActiveDisplayChanged(*mDisplay); + mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), mock::createDisplayModeSpecs(kModeId90.value(), true, 0, @@ -195,7 +195,7 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); - mFlinger.onActiveDisplayChanged(*mDisplay); + mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), mock::createDisplayModeSpecs(kModeId90.value(), false, 0, @@ -238,7 +238,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value()); ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60); - mFlinger.onActiveDisplayChanged(*mDisplay); + mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0, @@ -284,9 +284,43 @@ TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefresh ASSERT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K); } -TEST_F(DisplayModeSwitchingTest, multiDisplay) { +MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { + if (!arg->getDesiredActiveMode()) { + *result_listener << "No desired active mode"; + return false; + } + + if (arg->getDesiredActiveMode()->modeOpt->modePtr->getId() != modeId) { + *result_listener << "Unexpected desired active mode " << modeId; + return false; + } + + if (!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) { + *result_listener << "VsyncModulator did not shift to early phase"; + return false; + } + + return true; +} + +MATCHER_P(ModeSettledTo, modeId, "") { + if (const auto desiredOpt = arg->getDesiredActiveMode()) { + *result_listener << "Unsettled desired active mode " + << desiredOpt->modeOpt->modePtr->getId(); + return false; + } + ftl::FakeGuard guard(kMainThreadContext); + if (arg->getActiveMode().modePtr->getId() != modeId) { + *result_listener << "Settled to unexpected active mode " << modeId; + return false; + } + + return true; +} + +TEST_F(DisplayModeSwitchingTest, multiDisplay) { constexpr HWDisplayId kInnerDisplayHwcId = PrimaryDisplayVariant::HWC_DISPLAY_ID; constexpr HWDisplayId kOuterDisplayHwcId = kInnerDisplayHwcId + 1; @@ -309,13 +343,13 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) { const auto& innerDisplay = mDisplay; - EXPECT_FALSE(innerDisplay->getDesiredActiveMode()); - EXPECT_FALSE(outerDisplay->getDesiredActiveMode()); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); - EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId60); - EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120); + mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay); - mFlinger.onActiveDisplayChanged(*innerDisplay); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), @@ -327,12 +361,8 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) { mock::createDisplayModeSpecs(kModeId60.value(), false, 0.f, 120.f))); - // Transition on the inner display. - ASSERT_TRUE(innerDisplay->getDesiredActiveMode()); - EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); - - // No transition on the outer display. - EXPECT_FALSE(outerDisplay->getDesiredActiveMode()); + EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; EXPECT_CALL(*mComposer, @@ -342,31 +372,18 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) { mFlinger.commit(); - // Transition on the inner display. - ASSERT_TRUE(innerDisplay->getDesiredActiveMode()); - EXPECT_EQ(innerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId90); - - // No transition on the outer display. - EXPECT_FALSE(outerDisplay->getDesiredActiveMode()); + EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); mFlinger.commit(); - // Transition on the inner display. - EXPECT_FALSE(innerDisplay->getDesiredActiveMode()); - EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90); - - // No transition on the outer display. - EXPECT_FALSE(outerDisplay->getDesiredActiveMode()); - EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId120); - - mFlinger.onActiveDisplayChanged(*outerDisplay); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); - // No transition on the inner display. - EXPECT_FALSE(innerDisplay->getDesiredActiveMode()); + mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay); - // Transition on the outer display. - ASSERT_TRUE(outerDisplay->getDesiredActiveMode()); - EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); + EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); EXPECT_CALL(*mComposer, setActiveConfigWithConstraints(kOuterDisplayHwcId, @@ -375,22 +392,13 @@ TEST_F(DisplayModeSwitchingTest, multiDisplay) { mFlinger.commit(); - // No transition on the inner display. - EXPECT_FALSE(innerDisplay->getDesiredActiveMode()); - - // Transition on the outer display. - ASSERT_TRUE(outerDisplay->getDesiredActiveMode()); - EXPECT_EQ(outerDisplay->getDesiredActiveMode()->modeOpt->modePtr->getId(), kModeId60); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); + EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); mFlinger.commit(); - // No transition on the inner display. - EXPECT_FALSE(innerDisplay->getDesiredActiveMode()); - EXPECT_EQ(innerDisplay->getActiveMode().modePtr->getId(), kModeId90); - - // Transition on the outer display. - EXPECT_FALSE(outerDisplay->getDesiredActiveMode()); - EXPECT_EQ(outerDisplay->getActiveMode().modePtr->getId(), kModeId60); + EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); + EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60)); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp new file mode 100644 index 0000000000..a2c54ac621 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HdrOutputControlTest.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <gtest/gtest.h> +#include <gui/AidlStatusUtil.h> +#include <private/gui/ComposerService.h> +#include <private/gui/ComposerServiceAIDL.h> + +#include "DisplayTransactionTestHelpers.h" + +namespace android { + +using aidl::android::hardware::graphics::common::HdrConversionCapability; +using aidl::android::hardware::graphics::common::HdrConversionStrategy; +using GuiHdrConversionStrategyTag = gui::HdrConversionStrategy::Tag; +using gui::aidl_utils::statusTFromBinderStatus; + +TEST(HdrOutputControlTest, testGetHdrOutputConversionSupport) { + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); + + bool hdrOutputConversionSupport; + binder::Status status = sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport); + + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); +} + +TEST(HdrOutputControlTest, testGetHdrConversionCapabilities) { + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); + + bool hdrOutputConversionSupport; + binder::Status getSupportStatus = + sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus)); + + std::vector<gui::HdrConversionCapability> capabilities; + binder::Status status = sf->getHdrConversionCapabilities(&capabilities); + + if (hdrOutputConversionSupport) { + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); + } else { + ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status)); + } +} + +TEST(HdrOutputControlTest, testSetHdrConversionStrategy) { + sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService()); + + bool hdrOutputConversionSupport; + binder::Status getSupportStatus = + sf->getHdrOutputConversionSupport(&hdrOutputConversionSupport); + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(getSupportStatus)); + + std::vector<HdrConversionStrategy> strategies = + {HdrConversionStrategy(std::in_place_index<static_cast<size_t>( + GuiHdrConversionStrategyTag::passthrough)>), + HdrConversionStrategy(std::in_place_index<static_cast<size_t>( + GuiHdrConversionStrategyTag::autoAllowedHdrTypes)>), + HdrConversionStrategy(std::in_place_index<static_cast<size_t>( + GuiHdrConversionStrategyTag::forceHdrConversion)>)}; + int32_t outPreferredHdrOutputType = 0; + + for (HdrConversionStrategy strategy : strategies) { + binder::Status status = sf->setHdrConversionStrategy(&strategy, &outPreferredHdrOutputType); + + if (hdrOutputConversionSupport) { + ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status)); + } else { + ASSERT_EQ(INVALID_OPERATION, statusTFromBinderStatus(status)); + } + } +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp index f553a23f3c..fc5f2b0b90 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_InitializeDisplaysTest.cpp @@ -22,9 +22,9 @@ namespace android { namespace { -class OnInitializeDisplaysTest : public DisplayTransactionTest {}; +class InitializeDisplaysTest : public DisplayTransactionTest {}; -TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { +TEST_F(InitializeDisplaysTest, commitsPrimaryDisplay) { using Case = SimplePrimaryDisplayCase; // -------------------------------------------------------------------- @@ -44,12 +44,15 @@ TEST_F(OnInitializeDisplaysTest, onInitializeDisplaysSetsUpPrimaryDisplay) { // We expect a scheduled commit for the display transaction. EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1); - EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(static_cast<mock::VSyncTracker&>( + mFlinger.scheduler()->getVsyncSchedule()->getTracker()), + nextAnticipatedVSyncTimeFrom(_)) + .WillRepeatedly(Return(0)); // -------------------------------------------------------------------- // Invocation - mFlinger.onInitializeDisplays(); + FTL_FAKE_GUARD(kMainThreadContext, mFlinger.initializeDisplays()); // -------------------------------------------------------------------- // Postconditions diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp new file mode 100644 index 0000000000..e38f56e65f --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_MultiDisplayPacesetterTest.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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" + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +namespace android { +namespace { + +struct MultiDisplayPacesetterTest : DisplayTransactionTest { + static constexpr bool kWithMockScheduler = false; + MultiDisplayPacesetterTest() : DisplayTransactionTest(kWithMockScheduler) {} +}; + +TEST_F(MultiDisplayPacesetterTest, foldable) { + injectMockScheduler(InnerDisplayVariant::DISPLAY_ID::get()); + + // Inject inner and outer displays with uninitialized power modes. + sp<DisplayDevice> innerDisplay, outerDisplay; + constexpr bool kInitPowerMode = false; + { + InnerDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); + auto injector = InnerDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setPowerMode(std::nullopt); + injector.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()); + innerDisplay = injector.inject(); + } + { + OuterDisplayVariant::injectHwcDisplay<kInitPowerMode>(this); + auto injector = OuterDisplayVariant::makeFakeExistingDisplayInjector(this); + injector.setPowerMode(std::nullopt); + outerDisplay = injector.inject(); + } + + // When the device boots, the inner display should be the pacesetter. + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId()); + + // ...and should still be after powering on. + mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId()); + + // The outer display should become the pacesetter after folding. + mFlinger.setPowerModeInternal(innerDisplay, PowerMode::OFF); + mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId()); + + // The inner display should become the pacesetter after unfolding. + mFlinger.setPowerModeInternal(outerDisplay, PowerMode::OFF); + mFlinger.setPowerModeInternal(innerDisplay, PowerMode::ON); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId()); + + // The inner display should stay the pacesetter if both are powered on. + // TODO(b/255635821): The pacesetter should depend on the displays' refresh rates. + mFlinger.setPowerModeInternal(outerDisplay, PowerMode::ON); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), innerDisplay->getPhysicalId()); + + // The outer display should become the pacesetter if designated. + mFlinger.scheduler()->setPacesetterDisplay(outerDisplay->getPhysicalId()); + ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), outerDisplay->getPhysicalId()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index 622717f290..7839ef0dbb 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -28,9 +28,7 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockEventThread.h" #include "mock/MockTimeStats.h" -#include "mock/MockVsyncController.h" #include "mock/system/window/MockNativeWindow.h" using namespace android; @@ -53,8 +51,6 @@ class SurfaceFlingerPowerHintTest : public Test { public: void SetUp() override; - void setupScheduler(); - protected: TestableSurfaceFlinger mFlinger; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); @@ -68,7 +64,7 @@ protected: }; void SurfaceFlingerPowerHintTest::SetUp() { - setupScheduler(); + mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); mComposer = new Hwc2::mock::Composer(); mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); @@ -96,39 +92,11 @@ void SurfaceFlingerPowerHintTest::SetUp() { .setDisplaySurface(mDisplaySurface) .setNativeWindow(mNativeWindow) .setPowerMode(hal::PowerMode::ON) + .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) + .skipRegisterDisplay() .inject(); } -void SurfaceFlingerPowerHintTest::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), - TestableSurfaceFlinger::SchedulerCallbackImpl::kNoOp, - TestableSurfaceFlinger::kTwoDisplayModes); -} - TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index a0aaa684f9..7754c21805 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -59,54 +59,45 @@ struct DozeNotSupportedVariant { }; struct EventThreadBaseSupportedVariant { - static void setupVsyncAndEventThreadNoCallExpectations(DisplayTransactionTest* test) { - // The callback should not be notified to toggle VSYNC. - EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_)).Times(0); - - // The event thread should not be notified. - EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(0); - EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(0); + static void setupVsyncNoCallExpectations(DisplayTransactionTest* test) { + // Expect no change to hardware nor synthetic VSYNC. + EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, _)).Times(0); + EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(_)).Times(0); } }; struct EventThreadNotSupportedVariant : public EventThreadBaseSupportedVariant { - static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { - // These calls are only expected for the primary display. - - // Instead expect no calls. - setupVsyncAndEventThreadNoCallExpectations(test); + static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { + setupVsyncNoCallExpectations(test); } - static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { - // These calls are only expected for the primary display. - - // Instead expect no calls. - setupVsyncAndEventThreadNoCallExpectations(test); + static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { + setupVsyncNoCallExpectations(test); } }; struct EventThreadIsSupportedVariant : public EventThreadBaseSupportedVariant { - static void setupAcquireAndEnableVsyncCallExpectations(DisplayTransactionTest* test) { - // The callback should be notified to enable VSYNC. - EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(true)).Times(1); - - // The event thread should be notified that the screen was acquired. - EXPECT_CALL(*test->mEventThread, onScreenAcquired()).Times(1); + static void setupEnableVsyncCallExpectations(DisplayTransactionTest* test) { + // Expect to enable hardware VSYNC and disable synthetic VSYNC. + EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, true)).Times(1); + EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(false)).Times(1); } - static void setupReleaseAndDisableVsyncCallExpectations(DisplayTransactionTest* test) { - // The callback should be notified to disable VSYNC. - EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(false)).Times(1); - - // The event thread should not be notified that the screen was released. - EXPECT_CALL(*test->mEventThread, onScreenReleased()).Times(1); + static void setupDisableVsyncCallExpectations(DisplayTransactionTest* test) { + // Expect to disable hardware VSYNC and enable synthetic VSYNC. + EXPECT_CALL(test->mFlinger.mockSchedulerCallback(), setVsyncEnabled(_, false)).Times(1); + EXPECT_CALL(*test->mEventThread, enableSyntheticVsync(true)).Times(1); } }; struct DispSyncIsSupportedVariant { static void setupResetModelCallExpectations(DisplayTransactionTest* test) { - EXPECT_CALL(*test->mVsyncController, startPeriodTransition(DEFAULT_VSYNC_PERIOD)).Times(1); - EXPECT_CALL(*test->mVSyncTracker, resetModel()).Times(1); + auto vsyncSchedule = test->mFlinger.scheduler()->getVsyncSchedule(); + EXPECT_CALL(static_cast<mock::VsyncController&>(vsyncSchedule->getController()), + startPeriodTransition(DEFAULT_VSYNC_PERIOD, false)) + .Times(1); + EXPECT_CALL(static_cast<mock::VSyncTracker&>(vsyncSchedule->getTracker()), resetModel()) + .Times(1); } }; @@ -133,7 +124,7 @@ struct TransitionOffToOnVariant : public TransitionVariantCommon<PowerMode::OFF, template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -148,7 +139,7 @@ struct TransitionOffToDozeSuspendVariant template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); - Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupRepaintEverythingCallExpectations(test); } @@ -160,7 +151,7 @@ struct TransitionOffToDozeSuspendVariant struct TransitionOnToOffVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::OFF> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::EventThread::setupDisableVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -173,7 +164,7 @@ struct TransitionDozeSuspendToOffVariant : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::OFF> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::OFF); } @@ -185,7 +176,7 @@ struct TransitionDozeSuspendToOffVariant struct TransitionOnToDozeVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } }; @@ -194,7 +185,7 @@ struct TransitionDozeSuspendToDozeVariant : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::DOZE> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE); } @@ -203,7 +194,7 @@ struct TransitionDozeSuspendToDozeVariant struct TransitionDozeToOnVariant : public TransitionVariantCommon<PowerMode::DOZE, PowerMode::ON> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } }; @@ -212,7 +203,7 @@ struct TransitionDozeSuspendToOnVariant : public TransitionVariantCommon<PowerMode::DOZE_SUSPEND, PowerMode::ON> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupAcquireAndEnableVsyncCallExpectations(test); + Case::EventThread::setupEnableVsyncCallExpectations(test); Case::DispSync::setupResetModelCallExpectations(test); Case::setupComposerCallExpectations(test, IComposerClient::PowerMode::ON); } @@ -222,7 +213,7 @@ struct TransitionOnToDozeSuspendVariant : public TransitionVariantCommon<PowerMode::ON, PowerMode::DOZE_SUSPEND> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupReleaseAndDisableVsyncCallExpectations(test); + Case::EventThread::setupDisableVsyncCallExpectations(test); Case::setupComposerCallExpectations(test, Case::Doze::ACTUAL_POWER_MODE_FOR_DOZE_SUSPEND); } }; @@ -231,7 +222,7 @@ struct TransitionOnToUnknownVariant : public TransitionVariantCommon<PowerMode::ON, static_cast<PowerMode>(POWER_MODE_LEET)> { template <typename Case> static void setupCallExpectations(DisplayTransactionTest* test) { - Case::EventThread::setupVsyncAndEventThreadNoCallExpectations(test); + Case::EventThread::setupVsyncNoCallExpectations(test); Case::setupNoComposerPowerModeCallExpectations(test); } }; @@ -262,8 +253,9 @@ struct DisplayPowerCase { return display; } - static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, bool enabled) { - test->mFlinger.scheduler()->setInitialHwVsyncEnabled(enabled); + static void setInitialHwVsyncEnabled(DisplayTransactionTest* test, PhysicalDisplayId id, + bool enabled) { + test->mFlinger.scheduler()->setInitialHwVsyncEnabled(id, enabled); } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { @@ -329,9 +321,12 @@ void SetPowerModeInternalTest::transitionDisplayCommon() { Case::Doze::setupComposerCallExpectations(this); auto display = Case::injectDisplayWithInitialPowerMode(this, Case::Transition::INITIAL_POWER_MODE); - Case::setInitialHwVsyncEnabled(this, - PowerModeInitialVSyncEnabled< - Case::Transition::INITIAL_POWER_MODE>::value); + auto displayId = display->getId(); + if (auto physicalDisplayId = PhysicalDisplayId::tryCast(displayId)) { + Case::setInitialHwVsyncEnabled(this, *physicalDisplayId, + PowerModeInitialVSyncEnabled< + Case::Transition::INITIAL_POWER_MODE>::value); + } // -------------------------------------------------------------------- // Call Expectations @@ -484,38 +479,5 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownExternalDispla transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToUnknownVariant>>(); } -// TODO(b/262417075) -TEST_F(SetPowerModeInternalTest, DISABLED_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_UpdateLayerMetadataSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp index fed6a1ae56..0e5f1ea789 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_UpdateLayerMetadataSnapshotTest.cpp @@ -3,47 +3,17 @@ #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(); } + SurfaceFlingerUpdateLayerMetadataSnapshotTest() { mFlinger.setupMockScheduler(); } 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}; diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index bd3f3cae98..f1a5fc46bf 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -37,19 +37,16 @@ class TestableScheduler : public Scheduler, private ICompositor { public: TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) : TestableScheduler(std::make_unique<mock::VsyncController>(), - std::make_unique<mock::VSyncTracker>(), std::move(selectorPtr), + std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr), /* modulatorPtr */ nullptr, callback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, - std::unique_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, + std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) { - mVsyncSchedule = std::unique_ptr<VsyncSchedule>( - new VsyncSchedule(std::move(tracker), std::make_unique<mock::VSyncDispatch>(), - std::move(controller))); - const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); - registerDisplay(displayId, std::move(selectorPtr)); + registerDisplay(displayId, std::move(selectorPtr), std::move(controller), + std::move(tracker)); ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) { // Execute task to prevent broken promise exception on destruction. @@ -66,15 +63,28 @@ public: return Scheduler::createConnection(std::move(eventThread)); } - auto refreshRateSelector() { return leaderSelectorPtr(); } + auto refreshRateSelector() { return pacesetterSelectorPtr(); } const auto& refreshRateSelectors() const NO_THREAD_SAFETY_ANALYSIS { return mRefreshRateSelectors; } void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { + registerDisplay(displayId, std::move(selectorPtr), + std::make_unique<mock::VsyncController>(), + std::make_shared<mock::VSyncTracker>()); + } + + void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr, + std::unique_ptr<VsyncController> controller, + std::shared_ptr<VSyncTracker> tracker) { ftl::FakeGuard guard(kMainThreadContext); - Scheduler::registerDisplay(displayId, std::move(selectorPtr)); + Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr), + std::shared_ptr<VsyncSchedule>( + new VsyncSchedule(displayId, std::move(tracker), + std::make_shared< + mock::VSyncDispatch>(), + std::move(controller)))); } void unregisterDisplay(PhysicalDisplayId displayId) { @@ -82,16 +92,16 @@ public: Scheduler::unregisterDisplay(displayId); } - std::optional<PhysicalDisplayId> leaderDisplayId() const NO_THREAD_SAFETY_ANALYSIS { - return mLeaderDisplayId; + std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS { + return mPacesetterDisplayId; } - void setLeaderDisplay(PhysicalDisplayId displayId) { + void setPacesetterDisplay(PhysicalDisplayId displayId) { ftl::FakeGuard guard(kMainThreadContext); - Scheduler::setLeaderDisplay(displayId); + Scheduler::setPacesetterDisplay(displayId); } - auto& mutableVsyncModulator() { return *mVsyncModulator; } + auto& mutableAppConnectionHandle() { return mAppConnectionHandle; } auto& mutableLayerHistory() { return mLayerHistory; } size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS { @@ -150,10 +160,11 @@ public: Scheduler::onNonPrimaryDisplayModeChanged(handle, mode); } - void setInitialHwVsyncEnabled(bool enabled) { - std::lock_guard<std::mutex> lock(mVsyncSchedule->mHwVsyncLock); - mVsyncSchedule->mHwVsyncState = enabled ? VsyncSchedule::HwVsyncState::Enabled - : VsyncSchedule::HwVsyncState::Disabled; + void setInitialHwVsyncEnabled(PhysicalDisplayId id, bool enabled) { + auto schedule = getVsyncSchedule(id); + std::lock_guard<std::mutex> lock(schedule->mHwVsyncLock); + schedule->mHwVsyncState = enabled ? VsyncSchedule::HwVsyncState::Enabled + : VsyncSchedule::HwVsyncState::Disabled; } private: diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 89a661f108..fc9e653ba4 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -28,6 +28,7 @@ #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/DisplaySurface.h> #include <ftl/fake_guard.h> +#include <ftl/match.h> #include <gui/ScreenCaptureResults.h> #include <ui/DynamicDisplayInfo.h> @@ -47,14 +48,12 @@ #include "TestableScheduler.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/DisplayHardware/MockDisplayMode.h" +#include "mock/MockEventThread.h" #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" namespace android { - -class EventThread; - namespace renderengine { class RenderEngine; @@ -151,6 +150,11 @@ public: CreateCompositionEngineFunction mCreateCompositionEngine; }; +struct MockSchedulerOptions { + PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0); + bool useNiceMock = false; +}; + } // namespace surfaceflinger::test class TestableSurfaceFlinger { @@ -189,38 +193,31 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; - static constexpr struct OneDisplayMode { - } kOneDisplayMode; - - static constexpr struct TwoDisplayModes { - } kTwoDisplayModes; + struct DefaultDisplayMode { + // The ID of the injected RefreshRateSelector and its default display mode. + PhysicalDisplayId displayId; + }; - using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>; + using RefreshRateSelectorPtr = scheduler::Scheduler::RefreshRateSelectorPtr; - using DisplayModesVariant = - std::variant<OneDisplayMode, TwoDisplayModes, RefreshRateSelectorPtr>; + using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>; void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::unique_ptr<scheduler::VSyncTracker> vsyncTracker, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - DisplayModesVariant modesVariant = kOneDisplayMode, bool useNiceMock = false) { - 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)); - - if (std::holds_alternative<TwoDisplayModes>(modesVariant)) { - constexpr DisplayModeId kModeId90{1}; - modes.try_emplace(kModeId90, mock::createDisplayMode(kModeId90, 90_Hz)); - } - - selectorPtr = std::make_shared<scheduler::RefreshRateSelector>(modes, kModeId60); - } + RefreshRateSelectorPtr selectorPtr = ftl::match( + modesVariant, + [](DefaultDisplayMode arg) { + constexpr DisplayModeId kModeId60{0}; + return std::make_shared<scheduler::RefreshRateSelector>( + makeModes(mock::createDisplayMode(arg.displayId, kModeId60, 60_Hz)), + kModeId60); + }, + [](RefreshRateSelectorPtr selectorPtr) { return selectorPtr; }); const auto fps = selectorPtr->getActiveMode().fps; mFlinger->mVsyncConfiguration = mFactory.createVsyncConfiguration(fps); @@ -253,13 +250,47 @@ public: std::move(modulatorPtr), callback); } - mScheduler->initVsync(mScheduler->getVsyncSchedule().getDispatch(), *mTokenManager, 0ms); + mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); + + mScheduler->mutableAppConnectionHandle() = + mScheduler->createConnection(std::move(appEventThread)); - mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); + mFlinger->mAppConnectionHandle = mScheduler->mutableAppConnectionHandle(); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); resetScheduler(mScheduler); } + void setupMockScheduler(test::MockSchedulerOptions options = {}) { + using testing::_; + using testing::Return; + + auto eventThread = makeMock<mock::EventThread>(options.useNiceMock); + auto sfEventThread = makeMock<mock::EventThread>(options.useNiceMock); + + 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 = makeMock<mock::VsyncController>(options.useNiceMock); + auto vsyncTracker = makeSharedMock<mock::VSyncTracker>(options.useNiceMock); + + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(*vsyncTracker, currentPeriod()) + .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); + EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); + setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), + std::move(sfEventThread), DefaultDisplayMode{options.displayId}, + SchedulerCallbackImpl::kNoOp, options.useNiceMock); + } + void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } scheduler::TestableScheduler& mutableScheduler() { return *mScheduler; } @@ -388,10 +419,7 @@ public: return mFlinger->setDisplayStateLocked(s); } - // Allow reading display state without locking, as if called on the SF main thread. - auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS { - return mFlinger->onInitializeDisplays(); - } + void initializeDisplays() FTL_FAKE_GUARD(kMainThreadContext) { mFlinger->initializeDisplays(); } auto notifyPowerBoost(int32_t boostId) { return mFlinger->notifyPowerBoost(boostId); } @@ -474,10 +502,11 @@ public: return mFlinger->setDesiredDisplayModeSpecs(displayToken, specs); } - void onActiveDisplayChanged(const DisplayDevice& activeDisplay) { + void onActiveDisplayChanged(const DisplayDevice* inactiveDisplayPtr, + const DisplayDevice& activeDisplay) { Mutex::Autolock lock(mFlinger->mStateLock); ftl::FakeGuard guard(kMainThreadContext); - mFlinger->onActiveDisplayChangedLocked(nullptr, activeDisplay); + mFlinger->onActiveDisplayChangedLocked(inactiveDisplayPtr, activeDisplay); } auto createLayer(LayerCreationArgs& args, const sp<IBinder>& parentHandle, @@ -773,16 +802,16 @@ public: return mFlinger.mutableDisplays().get(mDisplayToken)->get(); } - // 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 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) { + auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId) { mDisplayModes = std::move(modes); mCreationArgs.activeModeId = activeModeId; + mCreationArgs.refreshRateSelector = nullptr; + return *this; + } + + auto& setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) { + mDisplayModes = selectorPtr->displayModes(); + mCreationArgs.activeModeId = selectorPtr->getActiveMode().modePtr->getId(); mCreationArgs.refreshRateSelector = std::move(selectorPtr); return *this; } @@ -824,6 +853,11 @@ public: return *this; } + auto& skipRegisterDisplay() { + mRegisterDisplay = false; + return *this; + } + sp<DisplayDevice> inject() NO_THREAD_SAFETY_ANALYSIS { const auto displayId = mCreationArgs.compositionDisplay->getDisplayId(); @@ -887,7 +921,7 @@ public: ui::ColorModes(), std::nullopt); - if (mFlinger.scheduler()) { + if (mFlinger.scheduler() && mRegisterDisplay) { mFlinger.scheduler()->registerDisplay(physicalId, display->holdRefreshRateSelector()); } @@ -906,11 +940,22 @@ public: sp<BBinder> mDisplayToken = sp<BBinder>::make(); DisplayDeviceCreationArgs mCreationArgs; DisplayModes mDisplayModes; + bool mRegisterDisplay = true; const std::optional<ui::DisplayConnectionType> mConnectionType; const std::optional<hal::HWDisplayId> mHwcDisplayId; }; private: + template <typename T> + static std::unique_ptr<T> makeMock(bool useNiceMock) { + return useNiceMock ? std::make_unique<testing::NiceMock<T>>() : std::make_unique<T>(); + } + + template <typename T> + static std::shared_ptr<T> makeSharedMock(bool useNiceMock) { + return useNiceMock ? std::make_shared<testing::NiceMock<T>>() : std::make_shared<T>(); + } + static constexpr VsyncId kVsyncId{123}; surfaceflinger::test::Factory mFactory; diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 859f702fe7..d4e2357b6b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -33,15 +33,12 @@ #include "FrontEnd/TransactionHandler.h" #include "TestableSurfaceFlinger.h" #include "TransactionState.h" -#include "mock/MockEventThread.h" -#include "mock/MockVsyncController.h" namespace android { using testing::_; using testing::Return; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; using frontend::TransactionHandler; constexpr nsecs_t TRANSACTION_TIMEOUT = s2ns(5); @@ -52,7 +49,9 @@ public: ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); + mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); + mFlinger.setupMockScheduler(); + mFlinger.flinger()->addTransactionReadyFilters(); } ~TransactionApplicationTest() { @@ -61,38 +60,8 @@ public: ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } - 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()))); - - EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); - EXPECT_CALL(*mVSyncTracker, currentPeriod()) - .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD)); - - mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); - 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; - mock::VsyncController* mVsyncController = new mock::VsyncController(); - mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker(); - struct TransactionInfo { Vector<ComposerState> states; Vector<DisplayState> displays; @@ -230,7 +199,7 @@ public: void modulateVsync() { static_cast<void>( - mFlinger.mutableScheduler().mutableVsyncModulator().onRefreshRateChangeInitiated()); + mFlinger.mutableScheduler().vsyncModulator().onRefreshRateChangeInitiated()); } bool mHasListenerCallbacks = false; diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 1173d1c876..764d19be0b 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -28,15 +28,13 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" -#include "mock/MockVsyncController.h" namespace android { using testing::_; using testing::Mock; using testing::Return; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using PresentState = frametimeline::SurfaceFrame::PresentState; class TransactionFrameTracerTest : public testing::Test { @@ -45,7 +43,7 @@ public: 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()); - setupScheduler(); + mFlinger.setupMockScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); } @@ -68,33 +66,6 @@ public: layer->commitTransaction(c); } - 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)); - } - TestableSurfaceFlinger mFlinger; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index ae03db43a7..e2c64917dc 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -28,15 +28,13 @@ #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" -#include "mock/MockVsyncController.h" namespace android { using testing::_; using testing::Mock; using testing::Return; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using PresentState = frametimeline::SurfaceFrame::PresentState; class TransactionSurfaceFrameTest : public testing::Test { @@ -45,7 +43,7 @@ public: 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()); - setupScheduler(); + mFlinger.setupMockScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); } @@ -67,33 +65,6 @@ public: layer->commitTransaction(c); } - 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)); - } - TestableSurfaceFlinger mFlinger; renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp index da87f1db17..108151ec65 100644 --- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp +++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp @@ -25,7 +25,6 @@ #include "TestableSurfaceFlinger.h" #include "TunnelModeEnabledReporter.h" #include "mock/DisplayHardware/MockComposer.h" -#include "mock/MockEventThread.h" namespace android { @@ -36,8 +35,6 @@ using testing::Return; using android::Hwc2::IComposer; using android::Hwc2::IComposerClient; -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; - constexpr int DEFAULT_SIDEBAND_STREAM = 51; struct TestableTunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener { @@ -61,8 +58,6 @@ protected: static constexpr uint32_t HEIGHT = 100; static constexpr uint32_t LAYER_FLAGS = 0; - void setupScheduler(); - void setupComposer(uint32_t virtualDisplayCount); sp<Layer> createBufferStateLayer(LayerMetadata metadata); TestableSurfaceFlinger mFlinger; @@ -80,7 +75,7 @@ TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() { ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - setupScheduler(); + mFlinger.setupMockScheduler(); mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>()); mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter; mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false); @@ -100,33 +95,6 @@ sp<Layer> TunnelModeEnabledReporterTest::createBufferStateLayer(LayerMetadata me return sp<Layer>::make(args); } -void TunnelModeEnabledReporterTest::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)); -} - namespace { TEST_F(TunnelModeEnabledReporterTest, callsAddedListeners) { diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 47c2deef51..41866a1c50 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -54,7 +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 setRenderRate(Fps) final {} void dump(std::string&) const final {} private: @@ -92,7 +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 setRenderRate(Fps) final {} void dump(std::string&) const final {} private: @@ -109,7 +109,8 @@ struct VSyncDispatchRealtimeTest : testing::Test { class RepeatingCallbackReceiver { public: - RepeatingCallbackReceiver(VSyncDispatch& dispatch, nsecs_t workload, nsecs_t readyDuration) + RepeatingCallbackReceiver(std::shared_ptr<VSyncDispatch> dispatch, nsecs_t workload, + nsecs_t readyDuration) : mWorkload(workload), mReadyDuration(readyDuration), mCallback( @@ -166,9 +167,10 @@ private: }; TEST_F(VSyncDispatchRealtimeTest, triple_alarm) { - FixedRateIdealStubTracker tracker; - VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold, - mVsyncMoveThreshold); + auto tracker = std::make_shared<FixedRateIdealStubTracker>(); + auto dispatch = + std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker, + mDispatchGroupThreshold, mVsyncMoveThreshold); static size_t constexpr num_clients = 3; std::array<RepeatingCallbackReceiver, num_clients> @@ -195,14 +197,15 @@ TEST_F(VSyncDispatchRealtimeTest, triple_alarm) { // starts at 333hz, slides down to 43hz TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) { auto next_vsync_interval = toNs(3ms); - VRRStubTracker tracker(next_vsync_interval); - VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold, - mVsyncMoveThreshold); + auto tracker = std::make_shared<VRRStubTracker>(next_vsync_interval); + auto dispatch = + std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker, + mDispatchGroupThreshold, mVsyncMoveThreshold); RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms)); auto const on_each_frame = [&](nsecs_t last_known) { - tracker.set_interval(next_vsync_interval += toNs(1ms), last_known); + tracker->set_interval(next_vsync_interval += toNs(1ms), last_known); }; std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); }); @@ -213,9 +216,10 @@ TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) { // starts at 333hz, jumps to 200hz at frame 10 TEST_F(VSyncDispatchRealtimeTest, fixed_jump) { - VRRStubTracker tracker(toNs(3ms)); - VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold, - mVsyncMoveThreshold); + auto tracker = std::make_shared<VRRStubTracker>(toNs(3ms)); + auto dispatch = + std::make_shared<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker, + mDispatchGroupThreshold, mVsyncMoveThreshold); RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms), toNs(5ms)); @@ -223,7 +227,7 @@ TEST_F(VSyncDispatchRealtimeTest, fixed_jump) { auto constexpr jump_frame_at = 10u; auto const on_each_frame = [&](nsecs_t last_known) { if (jump_frame_counter++ == jump_frame_at) { - tracker.set_interval(toNs(5ms), last_known); + tracker->set_interval(toNs(5ms), last_known); } }; std::thread eventThread([&] { cb_receiver.repeatedly_schedule(mIterations, on_each_frame); }); diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 14a2860378..7af1da6a31 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -55,7 +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_METHOD(void, setRenderRate, (Fps), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -116,13 +116,14 @@ private: class CountingCallback { public: - CountingCallback(VSyncDispatch& dispatch) - : mDispatch(dispatch), - mToken(dispatch.registerCallback(std::bind(&CountingCallback::counter, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3), - "test")) {} - ~CountingCallback() { mDispatch.unregisterCallback(mToken); } + CountingCallback(std::shared_ptr<VSyncDispatch> dispatch) + : mDispatch(std::move(dispatch)), + mToken(mDispatch->registerCallback(std::bind(&CountingCallback::counter, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3), + "test")) {} + ~CountingCallback() { mDispatch->unregisterCallback(mToken); } operator VSyncDispatch::CallbackToken() const { return mToken; } @@ -132,7 +133,7 @@ public: mReadyTime.push_back(readyTime); } - VSyncDispatch& mDispatch; + std::shared_ptr<VSyncDispatch> mDispatch; VSyncDispatch::CallbackToken mToken; std::vector<nsecs_t> mCalls; std::vector<nsecs_t> mWakeupTime; @@ -141,12 +142,12 @@ public: class PausingCallback { public: - PausingCallback(VSyncDispatch& dispatch, std::chrono::milliseconds pauseAmount) - : mDispatch(dispatch), - mToken(dispatch.registerCallback(std::bind(&PausingCallback::pause, this, - std::placeholders::_1, - std::placeholders::_2), - "test")), + PausingCallback(std::shared_ptr<VSyncDispatch> dispatch, std::chrono::milliseconds pauseAmount) + : mDispatch(std::move(dispatch)), + mToken(mDispatch->registerCallback(std::bind(&PausingCallback::pause, this, + std::placeholders::_1, + std::placeholders::_2), + "test")), mRegistered(true), mPauseAmount(pauseAmount) {} ~PausingCallback() { unregister(); } @@ -181,12 +182,12 @@ public: void unregister() { if (mRegistered) { - mDispatch.unregisterCallback(mToken); + mDispatch->unregisterCallback(mToken); mRegistered = false; } } - VSyncDispatch& mDispatch; + std::shared_ptr<VSyncDispatch> mDispatch; VSyncDispatch::CallbackToken mToken; bool mRegistered = true; @@ -231,22 +232,26 @@ protected: static nsecs_t constexpr mDispatchGroupThreshold = 5; nsecs_t const mPeriod = 1000; nsecs_t const mVsyncMoveThreshold = 300; - NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; - VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, - mVsyncMoveThreshold}; + std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker = + std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod); + std::shared_ptr<VSyncDispatch> mDispatch = + std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker, + mDispatchGroupThreshold, mVsyncMoveThreshold); }; TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); EXPECT_CALL(mMockClock, alarmCancel()); { - VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold, - mVsyncMoveThreshold}; + std::shared_ptr<VSyncDispatch> mDispatch = + std::make_shared<VSyncDispatchTimerQueue>(createTimeKeeper(), mStubTracker, + mDispatchGroupThreshold, + mVsyncMoveThreshold); CountingCallback cb(mDispatch); - const auto result = mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 1000}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -257,10 +262,10 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); @@ -277,14 +282,15 @@ TEST_F(VSyncDispatchTimerQueueTest, updateAlarmSettingFuture) { EXPECT_CALL(mMockClock, alarmAt(_, 700)).InSequence(seq); CountingCallback cb(mDispatch); - auto result = mDispatch.schedule(cb, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = intended}); + auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); - result = mDispatch.update(cb, + result = + mDispatch->update(cb, {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(700, *result); @@ -302,17 +308,17 @@ TEST_F(VSyncDispatchTimerQueueTest, updateDoesntSchedule) { CountingCallback cb(mDispatch); const auto result = - mDispatch.update(cb, - {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); + mDispatch->update(cb, + {.workDuration = 300, .readyDuration = 0, .earliestVsync = intended}); EXPECT_FALSE(result.has_value()); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithAdjustmentToTrueVsync) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)).WillOnce(Return(1150)); EXPECT_CALL(mMockClock, alarmAt(_, 1050)); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); @@ -323,15 +329,15 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingAdjustmentPast) { auto const now = 234; mMockClock.advanceBy(234); auto const workDuration = 10 * mPeriod; - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + workDuration)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + workDuration)) .WillOnce(Return(mPeriod * 11)); EXPECT_CALL(mMockClock, alarmAt(_, mPeriod)); CountingCallback cb(mDispatch); - const auto result = mDispatch.schedule(cb, - {.workDuration = workDuration, - .readyDuration = 0, - .earliestVsync = mPeriod}); + const auto result = mDispatch->schedule(cb, + {.workDuration = workDuration, + .readyDuration = 0, + .earliestVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod, *result); } @@ -341,12 +347,13 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancel) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = - mDispatch.schedule(cb, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); - EXPECT_EQ(mDispatch.cancel(cb), CancelResult::Cancelled); + EXPECT_EQ(mDispatch->cancel(cb), CancelResult::Cancelled); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { @@ -354,13 +361,14 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLate) { EXPECT_CALL(mMockClock, alarmCancel()); CountingCallback cb(mDispatch); - const auto result = - mDispatch.schedule(cb, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); mMockClock.advanceBy(950); - EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); + EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate); } TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { @@ -368,15 +376,16 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmCancelTooLateWhenRunning) { EXPECT_CALL(mMockClock, alarmCancel()); PausingCallback cb(mDispatch, std::chrono::duration_cast<std::chrono::milliseconds>(1s)); - const auto result = - mDispatch.schedule(cb, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); std::thread pausingThread([&] { mMockClock.advanceToNextCallback(); }); EXPECT_TRUE(cb.waitForPause()); - EXPECT_EQ(mDispatch.cancel(cb), CancelResult::TooLate); + EXPECT_EQ(mDispatch->cancel(cb), CancelResult::TooLate); cb.unpause(); pausingThread.join(); } @@ -389,9 +398,10 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { PausingCallback cb(mDispatch, 50ms); cb.stashResource(resource); - const auto result = - mDispatch.schedule(cb, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = mPeriod}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod - 100, *result); @@ -408,7 +418,7 @@ TEST_F(VSyncDispatchTimerQueueTest, unregisterSynchronizes) { } TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) .Times(4) .WillOnce(Return(1055)) .WillOnce(Return(1063)) @@ -423,8 +433,8 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); - mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); advanceToNextCallback(); advanceToNextCallback(); @@ -436,7 +446,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicTwoAlarmSetting) { } TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) .Times(4) .WillOnce(Return(1000)) .WillOnce(Return(2000)) @@ -450,21 +460,21 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { CountingCallback cb(mDispatch); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 0}); + 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}); + 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}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); advanceToNextCallback(); @@ -473,7 +483,7 @@ TEST_F(VSyncDispatchTimerQueueTest, noCloseCallbacksAfterPeriodChange) { } TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) .Times(4) .WillOnce(Return(10000)) .WillOnce(Return(1000)) @@ -488,10 +498,10 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsFaroutTimeoutWhenCancellingCloseOne) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); - mDispatch.schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); - mDispatch.cancel(cb1); + mDispatch->schedule(cb0, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = mPeriod * 10}); + mDispatch->schedule(cb1, {.workDuration = 250, .readyDuration = 0, .earliestVsync = mPeriod}); + mDispatch->cancel(cb1); } TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { @@ -502,9 +512,9 @@ TEST_F(VSyncDispatchTimerQueueTest, noUnnecessaryRearmsWhenRescheduling) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 300, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } @@ -517,9 +527,9 @@ TEST_F(VSyncDispatchTimerQueueTest, necessaryRearmsWhenModifying) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } @@ -537,10 +547,10 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, - {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, + {.workDuration = closeOffset, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); ASSERT_THAT(cb0.mCalls.size(), Eq(1)); @@ -548,9 +558,11 @@ TEST_F(VSyncDispatchTimerQueueTest, modifyIntoGroup) { ASSERT_THAT(cb1.mCalls.size(), Eq(1)); EXPECT_THAT(cb1.mCalls[0], Eq(mPeriod)); - mDispatch.schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); - mDispatch.schedule(cb1, - {.workDuration = notCloseOffset, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb0, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb1, + {.workDuration = notCloseOffset, + .readyDuration = 0, + .earliestVsync = 2000}); advanceToNextCallback(); ASSERT_THAT(cb1.mCalls.size(), Eq(2)); EXPECT_THAT(cb1.mCalls[1], Eq(2000)); @@ -570,32 +582,32 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenEndingAndDoesntCancel) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); - EXPECT_EQ(mDispatch.cancel(cb0), CancelResult::Cancelled); + EXPECT_EQ(mDispatch->cancel(cb0), CancelResult::Cancelled); } TEST_F(VSyncDispatchTimerQueueTest, setAlarmCallsAtCorrectTimeWithChangingVsync) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) .Times(3) .WillOnce(Return(950)) .WillOnce(Return(1975)) .WillOnce(Return(2950)); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 920}); mMockClock.advanceBy(850); EXPECT_THAT(cb.mCalls.size(), Eq(1)); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1900}); mMockClock.advanceBy(900); EXPECT_THAT(cb.mCalls.size(), Eq(1)); mMockClock.advanceBy(125); EXPECT_THAT(cb.mCalls.size(), Eq(2)); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2900}); mMockClock.advanceBy(975); EXPECT_THAT(cb.mCalls.size(), Eq(3)); } @@ -606,48 +618,48 @@ TEST_F(VSyncDispatchTimerQueueTest, callbackReentrancy) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); VSyncDispatch::CallbackToken tmp; - tmp = mDispatch.registerCallback( + tmp = mDispatch->registerCallback( [&](auto, auto, auto) { - mDispatch.schedule(tmp, - {.workDuration = 100, - .readyDuration = 0, - .earliestVsync = 2000}); + mDispatch->schedule(tmp, + {.workDuration = 100, + .readyDuration = 0, + .earliestVsync = 2000}); }, "o.o"); - mDispatch.schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); } TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) { VSyncDispatch::CallbackToken tmp; std::optional<nsecs_t> lastTarget; - tmp = mDispatch.registerCallback( + tmp = mDispatch->registerCallback( [&](auto timestamp, auto, auto) { auto result = - mDispatch.schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp - mVsyncMoveThreshold}); - EXPECT_TRUE(result.has_value()); - EXPECT_EQ(mPeriod + timestamp - 400, *result); - result = mDispatch.schedule(tmp, + mDispatch->schedule(tmp, {.workDuration = 400, .readyDuration = 0, - .earliestVsync = timestamp}); + .earliestVsync = timestamp - mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod + timestamp - 400, *result); - result = mDispatch.schedule(tmp, - {.workDuration = 400, - .readyDuration = 0, - .earliestVsync = timestamp + mVsyncMoveThreshold}); + result = mDispatch->schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp}); + EXPECT_TRUE(result.has_value()); + EXPECT_EQ(mPeriod + timestamp - 400, *result); + result = mDispatch->schedule(tmp, + {.workDuration = 400, + .readyDuration = 0, + .earliestVsync = timestamp + mVsyncMoveThreshold}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(mPeriod + timestamp - 400, *result); lastTarget = timestamp; }, "oo"); - mDispatch.schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(tmp, {.workDuration = 999, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); EXPECT_THAT(lastTarget, Eq(1000)); @@ -663,16 +675,16 @@ TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) { EXPECT_CALL(mMockClock, alarmAt(_, 1900)).InSequence(seq); CountingCallback cb(mDispatch); - mDispatch.schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 0, .readyDuration = 0, .earliestVsync = 1000}); mMockClock.advanceBy(750); - mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); - mDispatch.schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 50, .readyDuration = 0, .earliestVsync = 2000}); mMockClock.advanceBy(800); - mDispatch.schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); } TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { @@ -685,12 +697,12 @@ TEST_F(VSyncDispatchTimerQueueTest, lateModifications) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); - mDispatch.schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); - mDispatch.schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 200, .readyDuration = 0, .earliestVsync = 2000}); + mDispatch->schedule(cb1, {.workDuration = 150, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); advanceToNextCallback(); @@ -702,8 +714,8 @@ TEST_F(VSyncDispatchTimerQueueTest, doesntCancelPriorValidTimerForFutureMod) { CountingCallback cb0(mDispatch); CountingCallback cb1(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 20000}); } TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { @@ -713,29 +725,30 @@ TEST_F(VSyncDispatchTimerQueueTest, setsTimerAfterCancellation) { EXPECT_CALL(mMockClock, alarmAt(_, 900)).InSequence(seq); CountingCallback cb0(mDispatch); - mDispatch.schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.cancel(cb0); - mDispatch.schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->cancel(cb0); + mDispatch->schedule(cb0, {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); } TEST_F(VSyncDispatchTimerQueueTest, makingUpIdsError) { VSyncDispatch::CallbackToken token(100); - EXPECT_FALSE(mDispatch - .schedule(token, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) - .has_value()); - EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error)); + EXPECT_FALSE( + mDispatch + ->schedule(token, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}) + .has_value()); + EXPECT_THAT(mDispatch->cancel(token), Eq(CancelResult::Error)); } TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { CountingCallback cb0(mDispatch); auto result = - mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); - result = mDispatch.schedule(cb0, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb0, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -745,14 +758,14 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); auto result = - mDispatch.schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - result = mDispatch.schedule(cb, - {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, + {.workDuration = 800, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1200, *result); advanceToNextCallback(); @@ -760,19 +773,19 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule } TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1002)); CountingCallback cb(mDispatch); auto result = - mDispatch.schedule(cb, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); mMockClock.advanceBy(400); - result = mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(602, *result); } @@ -780,13 +793,13 @@ TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedul TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) { CountingCallback cb0(mDispatch); auto result = - mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); advanceToNextCallback(); - result = mDispatch.schedule(cb0, - {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb0, + {.workDuration = 1100, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); } @@ -797,13 +810,13 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { EXPECT_CALL(mMockClock, alarmAt(_, 1100)).InSequence(seq); CountingCallback cb0(mDispatch); auto result = - mDispatch.schedule(cb0, - {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb0, + {.workDuration = 500, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(500, *result); advanceToNextCallback(); - result = mDispatch.schedule(cb0, - {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb0, + {.workDuration = 1900, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1100, *result); } @@ -813,13 +826,13 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) CountingCallback cb(mDispatch); auto result = - mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch.schedule(cb, - {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb, + {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); @@ -865,16 +878,16 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb2(mDispatch); auto result = - mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); mMockClock.advanceBy(80); @@ -893,16 +906,16 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsSchedulingIfTimerReschedulingIsImminent CountingCallback cb(mDispatch); auto result = - mDispatch.schedule(cb, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - result = mDispatch.schedule(cb, - {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb, + {.workDuration = 370, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1630, *result); mMockClock.advanceBy(80); @@ -919,19 +932,19 @@ TEST_F(VSyncDispatchTimerQueueTest, skipsRearmingWhenNotNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.cancel(cb2), CancelResult::Cancelled); + EXPECT_EQ(mDispatch->cancel(cb2), CancelResult::Cancelled); mMockClock.advanceBy(80); @@ -948,19 +961,19 @@ TEST_F(VSyncDispatchTimerQueueTest, rearmsWhenCancelledAndIsNextScheduled) { CountingCallback cb2(mDispatch); auto result = - mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch.schedule(cb2, - {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); + result = mDispatch->schedule(cb2, + {.workDuration = 100, .readyDuration = 0, .earliestVsync = 2000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(1900, *result); mMockClock.setLag(100); mMockClock.advanceBy(620); - EXPECT_EQ(mDispatch.cancel(cb1), CancelResult::Cancelled); + EXPECT_EQ(mDispatch->cancel(cb1), CancelResult::Cancelled); EXPECT_THAT(cb1.mCalls.size(), Eq(0)); EXPECT_THAT(cb2.mCalls.size(), Eq(0)); @@ -975,21 +988,21 @@ TEST_F(VSyncDispatchTimerQueueTest, laggedTimerGroupsCallbacksWithinLag) { CountingCallback cb2(mDispatch); Sequence seq; - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) .InSequence(seq) .WillOnce(Return(1000)); EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000)) .InSequence(seq) .WillOnce(Return(1000)); auto result = - mDispatch.schedule(cb1, - {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb1, + {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(600, *result); - result = mDispatch.schedule(cb2, - {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); + result = mDispatch->schedule(cb2, + {.workDuration = 390, .readyDuration = 0, .earliestVsync = 1000}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(610, *result); @@ -1011,10 +1024,10 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { EXPECT_CALL(mMockClock, alarmAt(_, 900)); CountingCallback cb(mDispatch); - const auto result = mDispatch.schedule(cb, - {.workDuration = 70, - .readyDuration = 30, - .earliestVsync = intended}); + const auto result = mDispatch->schedule(cb, + {.workDuration = 70, + .readyDuration = 30, + .earliestVsync = intended}); EXPECT_TRUE(result.has_value()); EXPECT_EQ(900, *result); advanceToNextCallback(); @@ -1033,8 +1046,8 @@ TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { CountingCallback cb(mDispatch); - mDispatch.schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); - mDispatch.schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 400, .readyDuration = 0, .earliestVsync = 1000}); + mDispatch->schedule(cb, {.workDuration = 1400, .readyDuration = 0, .earliestVsync = 1000}); advanceToNextCallback(); @@ -1052,7 +1065,8 @@ class VSyncDispatchTimerQueueEntryTest : public testing::Test { protected: nsecs_t const mPeriod = 1000; nsecs_t const mVsyncMoveThreshold = 200; - NiceMock<MockVSyncTracker> mStubTracker{mPeriod}; + std::shared_ptr<NiceMock<MockVSyncTracker>> mStubTracker = + std::make_shared<NiceMock<MockVSyncTracker>>(mPeriod); }; TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) { @@ -1070,7 +1084,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) { EXPECT_FALSE(entry.wakeupTime()); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); @@ -1084,7 +1098,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) auto const duration = 500; auto const now = 8750; - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(now + duration)) .Times(1) .WillOnce(Return(10000)); VSyncDispatchTimerQueueEntry entry( @@ -1092,7 +1106,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, stateSchedulingReallyLongWakeupLatency) EXPECT_FALSE(entry.wakeupTime()); EXPECT_TRUE(entry.schedule({.workDuration = 500, .readyDuration = 0, .earliestVsync = 994}, - mStubTracker, now) + *mStubTracker.get(), now) .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); @@ -1115,7 +1129,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { mVsyncMoveThreshold); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); @@ -1137,7 +1151,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) { } TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(_)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(_)) .Times(2) .WillOnce(Return(1000)) .WillOnce(Return(1020)); @@ -1146,17 +1160,17 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, updateCallback) { "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_FALSE(entry.wakeupTime()); - entry.update(mStubTracker, 0); + entry.update(*mStubTracker.get(), 0); EXPECT_FALSE(entry.wakeupTime()); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); auto wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(wakeup, Eq(900)); - entry.update(mStubTracker, 0); + entry.update(*mStubTracker.get(), 0); wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); EXPECT_THAT(*wakeup, Eq(920)); @@ -1166,9 +1180,9 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); - entry.update(mStubTracker, 0); + entry.update(*mStubTracker.get(), 0); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); @@ -1179,24 +1193,24 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); entry.executing(); // 1000 is executing // had 1000 not been executing, this could have been scheduled for time 800. EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1950)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 1001}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_THAT(*entry.wakeupTime(), Eq(1800)); EXPECT_THAT(*entry.readyTime(), Eq(2000)); @@ -1208,24 +1222,24 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); Sequence seq; - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(500)) .InSequence(seq) .WillOnce(Return(1000)); - EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold)) + EXPECT_CALL(*mStubTracker.get(), nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold)) .InSequence(seq) .WillOnce(Return(2000)); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); entry.executing(); // 1000 is executing EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); } @@ -1233,16 +1247,16 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) { VSyncDispatchTimerQueueEntry entry( "test", [](auto, auto, auto) {}, mVsyncMoveThreshold); EXPECT_TRUE(entry.schedule({.workDuration = 100, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_TRUE(entry.schedule({.workDuration = 200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_TRUE(entry.schedule({.workDuration = 50, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); EXPECT_TRUE(entry.schedule({.workDuration = 1200, .readyDuration = 0, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); } @@ -1255,7 +1269,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, storesPendingUpdatesUntilUpdate) { entry.addPendingWorkloadUpdate( {.workDuration = effectualOffset, .readyDuration = 0, .earliestVsync = 400}); EXPECT_TRUE(entry.hasPendingWorkloadUpdate()); - entry.update(mStubTracker, 0); + entry.update(*mStubTracker.get(), 0); EXPECT_FALSE(entry.hasPendingWorkloadUpdate()); EXPECT_THAT(*entry.wakeupTime(), Eq(mPeriod - effectualOffset)); } @@ -1276,7 +1290,7 @@ TEST_F(VSyncDispatchTimerQueueEntryTest, runCallbackWithReadyDuration) { mVsyncMoveThreshold); EXPECT_TRUE(entry.schedule({.workDuration = 70, .readyDuration = 30, .earliestVsync = 500}, - mStubTracker, 0) + *mStubTracker.get(), 0) .has_value()); auto const wakeup = entry.wakeupTime(); ASSERT_TRUE(wakeup); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 48d39cf3f5..43d683d0fa 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -47,6 +47,8 @@ std::vector<nsecs_t> generateVsyncTimestamps(size_t count, nsecs_t period, nsecs return vsyncs; } +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; @@ -55,7 +57,7 @@ struct VSyncPredictorTest : testing::Test { static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{mPeriod, kHistorySize, kMinimumSamplesForPrediction, + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; }; @@ -376,7 +378,8 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { - VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction, + kOutlierTolerancePercent}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -554,7 +557,7 @@ TEST_F(VSyncPredictorTest, robustToDuplicateTimestamps_60hzRealTraceData) { EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError)); } -TEST_F(VSyncPredictorTest, setDivisorIsRespected) { +TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); @@ -563,7 +566,7 @@ TEST_F(VSyncPredictorTest, setDivisorIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setDivisor(3); + tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -574,7 +577,7 @@ TEST_F(VSyncPredictorTest, setDivisorIsRespected) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod)); } -TEST_F(VSyncPredictorTest, setDivisorOfDivosorIsInPhase) { +TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { auto last = mNow; for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); @@ -583,18 +586,44 @@ TEST_F(VSyncPredictorTest, setDivisorOfDivosorIsInPhase) { tracker.addVsyncTimestamp(mNow); } - tracker.setDivisor(4); + const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); + + tracker.setRenderRate(refreshRate / 4); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setDivisor(2); + tracker.setRenderRate(refreshRate / 2); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5 * mPeriod), Eq(mNow + 7 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); + + tracker.setRenderRate(refreshRate / 6); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); +} + +TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { + 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.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); + + 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 + 2 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 5 * mPeriod)); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 1fb2709f8d..122192b036 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -50,7 +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_METHOD(void, setRenderRate, (Fps), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; @@ -91,13 +91,15 @@ std::shared_ptr<android::FenceTime> generateSignalledFenceWithTime(nsecs_t time) return ft; } +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + class VSyncReactorTest : public testing::Test { protected: VSyncReactorTest() : mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()), mMockClock(std::make_shared<NiceMock<MockClock>>()), - mReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, kPendingLimit, - false /* supportKernelIdleTimer */) { + mReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, + kPendingLimit, false /* supportKernelIdleTimer */) { ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow)); ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period)); } @@ -192,7 +194,7 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -205,7 +207,7 @@ TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) { TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(10000, std::nullopt, &periodFlushed)); @@ -224,7 +226,7 @@ TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) { TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { nsecs_t sampleTime = 0; nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -232,7 +234,7 @@ TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(period); + mReactor.startPeriodTransition(period, false); EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); } @@ -242,13 +244,13 @@ TEST_F(VSyncReactorTest, changingToAThirdPeriodWillWaitForLastPeriod) { nsecs_t const secondPeriod = 5000; nsecs_t const thirdPeriod = 2000; - mReactor.startPeriodTransition(secondPeriod); + mReactor.startPeriodTransition(secondPeriod, false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); - mReactor.startPeriodTransition(thirdPeriod); + mReactor.startPeriodTransition(thirdPeriod, false); EXPECT_TRUE( mReactor.addHwVsyncTimestamp(sampleTime += secondPeriod, std::nullopt, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -289,14 +291,14 @@ TEST_F(VSyncReactorTest, reportedBadTimestampFromPredictorWillReactivateHwVSyncP TEST_F(VSyncReactorTest, presentFenceAdditionDoesNotInterruptConfirmationProcess) { nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0))); } TEST_F(VSyncReactorTest, setPeriodCalledFirstTwoEventsNewPeriod) { nsecs_t const newPeriod = 5000; EXPECT_CALL(*mMockTracker, setPeriod(_)).Times(0); - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); bool periodFlushed = true; EXPECT_TRUE(mReactor.addHwVsyncTimestamp(5000, std::nullopt, &periodFlushed)); @@ -321,7 +323,7 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); auto time = 0; auto constexpr numTimestampSubmissions = 10; @@ -346,7 +348,7 @@ TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) { bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); auto time = 0; // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps. @@ -363,7 +365,7 @@ TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsH auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); time += period; mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed); @@ -379,7 +381,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTracker) { auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); static auto constexpr numSamplesWithNewPeriod = 4; Sequence seq; @@ -406,7 +408,7 @@ TEST_F(VSyncReactorTest, hwVsyncturnsOffOnConfirmationWhenTrackerDoesntRequest) auto time = 0; bool periodFlushed = false; nsecs_t const newPeriod = 4000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -426,7 +428,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { nsecs_t const newPeriod1 = 4000; nsecs_t const newPeriod2 = 7000; - mReactor.startPeriodTransition(newPeriod1); + mReactor.startPeriodTransition(newPeriod1, false); Sequence seq; EXPECT_CALL(*mMockTracker, needsMoreSamples()) @@ -445,7 +447,7 @@ TEST_F(VSyncReactorTest, hwVsyncIsRequestedForTrackerMultiplePeriodChanges) { EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); - mReactor.startPeriodTransition(newPeriod2); + mReactor.startPeriodTransition(newPeriod2, false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod1, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time += newPeriod2, std::nullopt, &periodFlushed)); @@ -458,7 +460,7 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { mReactor.setIgnorePresentFences(true); nsecs_t const newPeriod = 5000; - mReactor.startPeriodTransition(newPeriod); + mReactor.startPeriodTransition(newPeriod, false); EXPECT_TRUE(mReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); @@ -472,8 +474,9 @@ TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) { TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Create a reactor which supports the kernel idle timer - auto idleReactor = VSyncReactor(std::make_unique<ClockWrapper>(mMockClock), *mMockTracker, - kPendingLimit, true /* supportKernelIdleTimer */); + auto idleReactor = + VSyncReactor(DEFAULT_DISPLAY_ID, std::make_unique<ClockWrapper>(mMockClock), + *mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */); bool periodFlushed = true; EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4); @@ -481,7 +484,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // First, set the same period, which should only be confirmed when we receive two // matching callbacks - idleReactor.startPeriodTransition(10000); + idleReactor.startPeriodTransition(10000, false); EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(0, 0, &periodFlushed)); EXPECT_FALSE(periodFlushed); // Correct period but incorrect timestamp delta @@ -494,7 +497,7 @@ TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) { // Then, set a new period, which should be confirmed as soon as we receive a callback // reporting the new period nsecs_t const newPeriod = 5000; - idleReactor.startPeriodTransition(newPeriod); + idleReactor.startPeriodTransition(newPeriod, false); // Incorrect timestamp delta and period EXPECT_TRUE(idleReactor.addHwVsyncTimestamp(20000, 10000, &periodFlushed)); EXPECT_FALSE(periodFlushed); diff --git a/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp new file mode 100644 index 0000000000..4010fa6a5b --- /dev/null +++ b/services/surfaceflinger/tests/unittests/VsyncScheduleTest.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <ftl/fake_guard.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <log/log.h> + +#include <scheduler/Fps.h> +#include "Scheduler/VsyncSchedule.h" +#include "ThreadContext.h" +#include "mock/MockSchedulerCallback.h" +#include "mock/MockVSyncDispatch.h" +#include "mock/MockVSyncTracker.h" +#include "mock/MockVsyncController.h" + +using testing::_; + +namespace android { + +constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + +class VsyncScheduleTest : public testing::Test { +protected: + VsyncScheduleTest(); + ~VsyncScheduleTest() override; + + scheduler::mock::SchedulerCallback mCallback; + const std::unique_ptr<scheduler::VsyncSchedule> mVsyncSchedule = + std::unique_ptr<scheduler::VsyncSchedule>( + new scheduler::VsyncSchedule(DEFAULT_DISPLAY_ID, + std::make_shared<mock::VSyncTracker>(), + std::make_shared<mock::VSyncDispatch>(), + std::make_unique<mock::VsyncController>())); + + mock::VsyncController& getController() { + return *static_cast<mock::VsyncController*>(&mVsyncSchedule->getController()); + } +}; + +VsyncScheduleTest::VsyncScheduleTest() { + 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()); +} + +VsyncScheduleTest::~VsyncScheduleTest() { + 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 { + +using namespace testing; + +TEST_F(VsyncScheduleTest, InitiallyDisallowed) { + ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); +} + +TEST_F(VsyncScheduleTest, EnableDoesNothingWhenDisallowed) { + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + + mVsyncSchedule->enableHardwareVsync(mCallback); +} + +TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed) { + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + + mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */); +} + +TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisallowed2) { + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + + mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */); +} + +TEST_F(VsyncScheduleTest, MakeAllowed) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); +} + +TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + + mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */); +} + +TEST_F(VsyncScheduleTest, DisableDoesNothingWhenDisabled2) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + + mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */); +} + +TEST_F(VsyncScheduleTest, EnableWorksWhenDisabled) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + + mVsyncSchedule->enableHardwareVsync(mCallback); +} + +TEST_F(VsyncScheduleTest, EnableWorksOnce) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + + mVsyncSchedule->enableHardwareVsync(mCallback); + + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + mVsyncSchedule->enableHardwareVsync(mCallback); +} + +TEST_F(VsyncScheduleTest, AllowedIsSticky) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); +} + +TEST_F(VsyncScheduleTest, EnableDisable) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + + mVsyncSchedule->enableHardwareVsync(mCallback); + + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false)); + mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */); +} + +TEST_F(VsyncScheduleTest, EnableDisable2) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + + mVsyncSchedule->enableHardwareVsync(mCallback); + + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false)); + mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */); +} + +TEST_F(VsyncScheduleTest, StartPeriodTransition) { + // Note: startPeriodTransition is only called when hardware vsyncs are + // allowed. + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + + const Period period = (60_Hz).getPeriod(); + + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + + mVsyncSchedule->startPeriodTransition(mCallback, period, false); +} + +TEST_F(VsyncScheduleTest, StartPeriodTransitionAlreadyEnabled) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + mVsyncSchedule->enableHardwareVsync(mCallback); + + const Period period = (60_Hz).getPeriod(); + + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + EXPECT_CALL(getController(), startPeriodTransition(period.ns(), false)); + + mVsyncSchedule->startPeriodTransition(mCallback, period, false); +} + +TEST_F(VsyncScheduleTest, StartPeriodTransitionForce) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + + const Period period = (60_Hz).getPeriod(); + + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, true)); + EXPECT_CALL(getController(), startPeriodTransition(period.ns(), true)); + + mVsyncSchedule->startPeriodTransition(mCallback, period, true); +} + +TEST_F(VsyncScheduleTest, AddResyncSampleDisallowed) { + const Period period = (60_Hz).getPeriod(); + const auto timestamp = TimePoint::now(); + + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0); + + mVsyncSchedule->addResyncSample(mCallback, timestamp, period); +} + +TEST_F(VsyncScheduleTest, AddResyncSampleDisabled) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + const Period period = (60_Hz).getPeriod(); + const auto timestamp = TimePoint::now(); + + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + EXPECT_CALL(getController(), addHwVsyncTimestamp(_, _, _)).Times(0); + + mVsyncSchedule->addResyncSample(mCallback, timestamp, period); +} + +TEST_F(VsyncScheduleTest, AddResyncSampleReturnsTrue) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + mVsyncSchedule->enableHardwareVsync(mCallback); + + const Period period = (60_Hz).getPeriod(); + const auto timestamp = TimePoint::now(); + + EXPECT_CALL(mCallback, setVsyncEnabled(_, _)).Times(0); + EXPECT_CALL(getController(), + addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _)) + .WillOnce(Return(true)); + + mVsyncSchedule->addResyncSample(mCallback, timestamp, period); +} + +TEST_F(VsyncScheduleTest, AddResyncSampleReturnsFalse) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + mVsyncSchedule->enableHardwareVsync(mCallback); + + const Period period = (60_Hz).getPeriod(); + const auto timestamp = TimePoint::now(); + + EXPECT_CALL(mCallback, setVsyncEnabled(DEFAULT_DISPLAY_ID, false)); + EXPECT_CALL(getController(), + addHwVsyncTimestamp(timestamp.ns(), std::optional<nsecs_t>(period.ns()), _)) + .WillOnce(Return(false)); + + mVsyncSchedule->addResyncSample(mCallback, timestamp, period); +} + +TEST_F(VsyncScheduleTest, PendingState) FTL_FAKE_GUARD(kMainThreadContext) { + ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState()); + mVsyncSchedule->setPendingHardwareVsyncState(true); + ASSERT_TRUE(mVsyncSchedule->getPendingHardwareVsyncState()); + + mVsyncSchedule->setPendingHardwareVsyncState(false); + ASSERT_FALSE(mVsyncSchedule->getPendingHardwareVsyncState()); +} + +TEST_F(VsyncScheduleTest, DisableDoesNotMakeAllowed) { + ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); + mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */); + ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); +} + +TEST_F(VsyncScheduleTest, DisallowMakesNotAllowed) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + mVsyncSchedule->disableHardwareVsync(mCallback, true /* disallow */); + ASSERT_FALSE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); +} + +TEST_F(VsyncScheduleTest, StillAllowedAfterDisable) { + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(true /* makeAllowed */)); + mVsyncSchedule->disableHardwareVsync(mCallback, false /* disallow */); + ASSERT_TRUE(mVsyncSchedule->isHardwareVsyncAllowed(false /* makeAllowed */)); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index f28b8d8a7e..5dc3490eb8 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -175,6 +175,7 @@ public: Error(aidl::android::hardware::graphics::composer3::OverlayProperties*)); MOCK_METHOD1(onHotplugConnect, void(Display)); MOCK_METHOD1(onHotplugDisconnect, void(Display)); + MOCK_METHOD(Error, setRefreshRateChangedCallbackDebugEnabled, (Display, bool)); }; } // namespace Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index f8567bd636..8d57049219 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -29,27 +29,28 @@ public: EventThread(); ~EventThread() override; - MOCK_CONST_METHOD2(createEventConnection, - sp<EventThreadConnection>(ResyncCallback, EventRegistrationFlags)); - MOCK_METHOD0(onScreenReleased, void()); - MOCK_METHOD0(onScreenAcquired, void()); - MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); - MOCK_METHOD1(onModeChanged, void(const scheduler::FrameRateMode &)); - MOCK_METHOD2(onFrameRateOverridesChanged, - void(PhysicalDisplayId, std::vector<FrameRateOverride>)); - MOCK_CONST_METHOD1(dump, void(std::string&)); - MOCK_METHOD2(setDuration, - void(std::chrono::nanoseconds workDuration, - std::chrono::nanoseconds readyDuration)); - MOCK_METHOD1(registerDisplayEventConnection, - status_t(const sp<android::EventThreadConnection> &)); - MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &)); - MOCK_METHOD1(requestNextVsync, void(const sp<android::EventThreadConnection> &)); + MOCK_METHOD(sp<EventThreadConnection>, createEventConnection, + (ResyncCallback, EventRegistrationFlags), (const, override)); + MOCK_METHOD(void, enableSyntheticVsync, (bool), (override)); + MOCK_METHOD(void, onHotplugReceived, (PhysicalDisplayId, bool), (override)); + MOCK_METHOD(void, onModeChanged, (const scheduler::FrameRateMode&), (override)); + MOCK_METHOD(void, onFrameRateOverridesChanged, + (PhysicalDisplayId, std::vector<FrameRateOverride>), (override)); + MOCK_METHOD(void, dump, (std::string&), (const, override)); + MOCK_METHOD(void, setDuration, + (std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration), + (override)); + MOCK_METHOD(status_t, registerDisplayEventConnection, + (const sp<android::EventThreadConnection>&), (override)); + MOCK_METHOD(void, setVsyncRate, (uint32_t, const sp<android::EventThreadConnection>&), + (override)); + MOCK_METHOD(void, requestNextVsync, (const sp<android::EventThreadConnection>&), (override)); MOCK_METHOD(VsyncEventData, getLatestVsyncEventData, - (const sp<android::EventThreadConnection> &), (const)); - MOCK_METHOD1(requestLatestConfig, void(const sp<android::EventThreadConnection> &)); - MOCK_METHOD1(pauseVsyncCallback, void(bool)); - MOCK_METHOD0(getEventThreadConnectionCount, size_t()); + (const sp<android::EventThreadConnection>&), (const, override)); + MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&)); + MOCK_METHOD(void, pauseVsyncCallback, (bool)); + MOCK_METHOD(size_t, getEventThreadConnectionCount, (), (override)); + MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 7d4b159e5e..a8eca2192f 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -18,19 +18,19 @@ #include <gmock/gmock.h> -#include "Scheduler/Scheduler.h" +#include "Scheduler/ISchedulerCallback.h" namespace android::scheduler::mock { struct SchedulerCallback final : ISchedulerCallback { - MOCK_METHOD(void, setVsyncEnabled, (bool), (override)); + MOCK_METHOD(void, setVsyncEnabled, (PhysicalDisplayId, bool), (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 setVsyncEnabled(PhysicalDisplayId, bool) override {} void requestDisplayModes(std::vector<display::DisplayModeRequest>) override {} void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index 6893154259..dcf25e18a8 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -34,7 +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_METHOD(void, setRenderRate, (Fps), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 4ef91dacb2..69ec60acd4 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -28,12 +28,12 @@ public: ~VsyncController() override; MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override)); - MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*)); - MOCK_METHOD1(startPeriodTransition, void(nsecs_t)); - MOCK_METHOD1(setIgnorePresentFences, void(bool)); + MOCK_METHOD(bool, addHwVsyncTimestamp, (nsecs_t, std::optional<nsecs_t>, bool*), (override)); + MOCK_METHOD(void, startPeriodTransition, (nsecs_t, bool), (override)); + MOCK_METHOD(void, setIgnorePresentFences, (bool), (override)); MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override)); - MOCK_CONST_METHOD1(dump, void(std::string&)); + MOCK_METHOD(void, dump, (std::string&), (const, override)); }; } // namespace android::mock |