diff options
134 files changed, 4833 insertions, 1406 deletions
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl index 491c629278..a1d8ce5962 100644 --- a/aidl/gui/android/view/LayerMetadataKey.aidl +++ b/aidl/gui/android/view/LayerMetadataKey.aidl @@ -25,4 +25,5 @@ enum LayerMetadataKey { METADATA_MOUSE_CURSOR = 4, METADATA_ACCESSIBILITY_ID = 5, METADATA_OWNER_PID = 6, + METADATA_DEQUEUE_TIME = 7, } diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index bfcc058c1b..ba25a5a603 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -39,8 +39,13 @@ struct DumpstateInfo { std::string calling_package; }; -static binder::Status exception(uint32_t code, const std::string& msg) { - MYLOGE("%s (%d) ", msg.c_str(), code); +static binder::Status exception(uint32_t code, const std::string& msg, + const std::string& extra_msg = "") { + if (extra_msg.empty()) { + MYLOGE("%s (%d) ", msg.c_str(), code); + } else { + MYLOGE("%s %s (%d) ", msg.c_str(), extra_msg.c_str(), code); + } return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } @@ -60,7 +65,7 @@ static binder::Status exception(uint32_t code, const std::string& msg) { } // namespace -DumpstateService::DumpstateService() : ds_(nullptr) { +DumpstateService::DumpstateService() : ds_(nullptr), calling_uid_(-1), calling_package_() { } char const* DumpstateService::getServiceName() { @@ -131,6 +136,10 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, ds_->SetOptions(std::move(options)); ds_->listener_ = listener; + // Track caller info for cancellation purposes. + calling_uid_ = calling_uid; + calling_package_ = calling_package; + DumpstateInfo* ds_info = new DumpstateInfo(); ds_info->ds = ds_; ds_info->calling_uid = calling_uid; @@ -149,8 +158,20 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, return binder::Status::ok(); } -binder::Status DumpstateService::cancelBugreport() { +binder::Status DumpstateService::cancelBugreport(int32_t calling_uid, + const std::string& calling_package) { std::lock_guard<std::mutex> lock(lock_); + if (calling_uid != calling_uid_ || calling_package != calling_package_) { + // Note: we use a SecurityException to prevent BugreportManagerServiceImpl from killing the + // report in progress (from another caller). + return exception( + binder::Status::EX_SECURITY, + StringPrintf("Cancellation requested by %d/%s does not match report in " + "progress", + calling_uid, calling_package.c_str()), + // Sharing the owner of the BR is a (minor) leak, so leave it out of the app's exception + StringPrintf("started by %d/%s", calling_uid_, calling_package_.c_str())); + } ds_->Cancel(); return binder::Status::ok(); } diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index ac8d3acbb5..3ec8471c04 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -44,8 +44,7 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst const sp<IDumpstateListener>& listener, bool is_screenshot_requested) override; - // No-op - binder::Status cancelBugreport(); + binder::Status cancelBugreport(int32_t calling_uid, const std::string& calling_package); private: // Dumpstate object which contains all the bugreporting logic. @@ -53,6 +52,8 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst // one bugreport. // This service does not own this object. Dumpstate* ds_; + int32_t calling_uid_; + std::string calling_package_; std::mutex lock_; }; diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index eeaa5a3de0..c833d0e6bd 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -124,6 +124,12 @@ CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Re return *this; } +CommandOptions::CommandOptionsBuilder& +CommandOptions::CommandOptionsBuilder::CloseAllFileDescriptorsOnExec() { + values.close_all_fds_on_exec_ = true; + return *this; +} + CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Log( const std::string& message) { values.logging_message_ = message; @@ -137,6 +143,7 @@ CommandOptions CommandOptions::CommandOptionsBuilder::Build() { CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms) : timeout_ms_(timeout_ms), always_(false), + close_all_fds_on_exec_(false), account_mode_(DONT_DROP_ROOT), output_mode_(NORMAL_OUTPUT), logging_message_("") { @@ -157,6 +164,10 @@ bool CommandOptions::Always() const { return values.always_; } +bool CommandOptions::ShouldCloseAllFileDescriptorsOnExec() const { + return values.close_all_fds_on_exec_; +} + PrivilegeMode CommandOptions::PrivilegeMode() const { return values.account_mode_; } @@ -277,7 +288,8 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri MYLOGI(logging_message.c_str(), command_string.c_str()); } - bool silent = (options.OutputMode() == REDIRECT_TO_STDERR); + bool silent = (options.OutputMode() == REDIRECT_TO_STDERR || + options.ShouldCloseAllFileDescriptorsOnExec()); bool redirecting_to_fd = STDOUT_FILENO != fd; if (PropertiesHelper::IsDryRun() && !options.Always()) { @@ -314,7 +326,27 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri return -1; } - if (silent) { + if (options.ShouldCloseAllFileDescriptorsOnExec()) { + int devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_RDONLY)); + TEMP_FAILURE_RETRY(dup2(devnull_fd, STDIN_FILENO)); + close(devnull_fd); + devnull_fd = TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY)); + TEMP_FAILURE_RETRY(dup2(devnull_fd, STDOUT_FILENO)); + TEMP_FAILURE_RETRY(dup2(devnull_fd, STDERR_FILENO)); + close(devnull_fd); + // This is to avoid leaking FDs that, accidentally, have not been + // marked as O_CLOEXEC. Leaking FDs across exec can cause failures + // when execing a process that has a SELinux auto_trans rule. + // Here we assume that the dumpstate process didn't open more than + // 1000 FDs. In theory we could iterate through /proc/self/fd/, but + // doing that in a fork-safe way is too complex and not worth it + // (opendir()/readdir() do heap allocations and take locks). + for (int i = 0; i < 1000; i++) { + if (i != STDIN_FILENO && i!= STDOUT_FILENO && i != STDERR_FILENO) { + close(i); + } + } + } else if (silent) { // Redirects stdout to stderr TEMP_FAILURE_RETRY(dup2(STDERR_FILENO, STDOUT_FILENO)); } else if (redirecting_to_fd) { diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index b099443e32..b00c46e0db 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -80,6 +80,7 @@ class CommandOptions { int64_t timeout_ms_; bool always_; + bool close_all_fds_on_exec_; PrivilegeMode account_mode_; OutputMode output_mode_; std::string logging_message_; @@ -112,6 +113,13 @@ class CommandOptions { CommandOptionsBuilder& DropRoot(); /* Sets the command's OutputMode as `REDIRECT_TO_STDERR` */ CommandOptionsBuilder& RedirectStderr(); + /* Closes all file descriptors before exec-ing the target process. This + * includes also stdio pipes, which are dup-ed on /dev/null. It prevents + * leaking opened FDs to the target process, which in turn can hit + * selinux denials in presence of auto_trans rules. + */ + CommandOptionsBuilder& CloseAllFileDescriptorsOnExec(); + /* When not empty, logs a message before executing the command. * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */ CommandOptionsBuilder& Log(const std::string& message); @@ -130,6 +138,8 @@ class CommandOptions { int64_t TimeoutInMs() const; /* Checks whether the command should always be run, even on dry-run mode. */ bool Always() const; + /* Checks whether all FDs should be closed prior to the exec() calls. */ + bool ShouldCloseAllFileDescriptorsOnExec() const; /** Gets the PrivilegeMode of the command. */ PrivilegeMode PrivilegeMode() const; /** Gets the OutputMode of the command. */ diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index ba008bb27e..0793f0b95f 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -1,4 +1,4 @@ -/** +/* * Copyright (c) 2016, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,9 +19,9 @@ package android.os; import android.os.IDumpstateListener; /** - * Binder interface for the currently running dumpstate process. - * {@hide} - */ + * Binder interface for the currently running dumpstate process. + * {@hide} + */ interface IDumpstate { // NOTE: If you add to or change these modes, please also change the corresponding enums @@ -49,10 +49,10 @@ interface IDumpstate { // Default mode. const int BUGREPORT_MODE_DEFAULT = 6; - /* + /** * Starts a bugreport in the background. * - *<p>Shows the user a dialog to get consent for sharing the bugreport with the calling + * <p>Shows the user a dialog to get consent for sharing the bugreport with the calling * application. If they deny {@link IDumpstateListener#onError} will be called. If they * consent and bugreport generation is successful artifacts will be copied to the given fds and * {@link IDumpstateListener#onFinished} will be called. If there @@ -71,8 +71,15 @@ interface IDumpstate { int bugreportMode, IDumpstateListener listener, boolean isScreenshotRequested); - /* + /** * Cancels the bugreport currently in progress. + * + * <p>The caller must match the original caller of {@link #startBugreport} in order for the + * report to actually be cancelled. A {@link SecurityException} is reported if a mismatch is + * detected. + * + * @param callingUid UID of the original application that requested the cancellation. + * @param callingPackage package of the original application that requested the cancellation. */ - void cancelBugreport(); + void cancelBugreport(int callingUid, @utf8InCpp String callingPackage); } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index afa0b4de70..4a4a510f3b 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/mount.h> #include <sys/poll.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -174,6 +175,7 @@ void add_mountinfo(); #define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log" #define LINKERCONFIG_DIR "/linkerconfig" #define PACKAGE_DEX_USE_LIST "/data/system/package-dex-usage.list" +#define SYSTEM_TRACE_SNAPSHOT "/data/misc/perfetto-traces/bugreport/systrace.pftrace" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -227,7 +229,6 @@ static const std::string DUMP_INCIDENT_REPORT_TASK = "INCIDENT REPORT"; static const std::string DUMP_HALS_TASK = "DUMP HALS"; static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; -static const std::string DUMP_APP_INFOS_TASK = "DUMP APP INFOS"; namespace android { namespace os { @@ -1053,6 +1054,24 @@ static void DumpIncidentReport() { } } +static void MaybeAddSystemTraceToZip() { + // This function copies into the .zip the system trace that was snapshotted + // by the early call to MaybeSnapshotSystemTrace(), if any background + // tracing was happening. + if (!ds.IsZipping()) { + MYLOGD("Not dumping system trace because it's not a zipped bugreport\n"); + return; + } + if (!ds.has_system_trace_) { + // No background trace was happening at the time dumpstate was invoked. + return; + } + ds.AddZipEntry( + ZIP_ROOT_DIR + SYSTEM_TRACE_SNAPSHOT, + SYSTEM_TRACE_SNAPSHOT); + android::os::UnlinkAndLogOnError(SYSTEM_TRACE_SNAPSHOT); +} + static void DumpVisibleWindowViews() { if (!ds.IsZipping()) { MYLOGD("Not dumping visible views because it's not a zipped bugreport\n"); @@ -1550,7 +1569,7 @@ static void DumpAppInfos(int out_fd = STDOUT_FILENO) { dprintf(out_fd, "========================================================\n"); RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"}, - DUMPSYS_COMPONENTS_OPTIONS, out_fd); + DUMPSYS_COMPONENTS_OPTIONS, 0, out_fd); dprintf(out_fd, "========================================================\n"); dprintf(out_fd, "== Running Application Providers (non-platform)\n"); @@ -1577,7 +1596,6 @@ static Dumpstate::RunStatus dumpstate() { ds.dump_pool_->enqueueTask(DUMP_INCIDENT_REPORT_TASK, &DumpIncidentReport); ds.dump_pool_->enqueueTaskWithFd(DUMP_BOARD_TASK, &Dumpstate::DumpstateBoard, &ds, _1); ds.dump_pool_->enqueueTaskWithFd(DUMP_CHECKINS_TASK, &DumpCheckins, _1); - ds.dump_pool_->enqueueTaskWithFd(DUMP_APP_INFOS_TASK, &DumpAppInfos, _1); } // Dump various things. Note that anything that takes "long" (i.e. several seconds) should @@ -1651,6 +1669,8 @@ static Dumpstate::RunStatus dumpstate() { AddAnrTraceFiles(); + MaybeAddSystemTraceToZip(); + // NOTE: tombstones are always added as separate entries in the zip archive // and are not interspersed with the main report. const bool tombstones_dumped = AddDumps(ds.tombstone_data_.begin(), ds.tombstone_data_.end(), @@ -1730,11 +1750,7 @@ static Dumpstate::RunStatus dumpstate() { RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_CHECKINS_TASK, DumpCheckins); } - if (ds.dump_pool_) { - WAIT_TASK_WITH_CONSENT_CHECK(DUMP_APP_INFOS_TASK, ds.dump_pool_); - } else { - RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK_AND_LOG(DUMP_APP_INFOS_TASK, DumpAppInfos); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpAppInfos); printf("========================================================\n"); printf("== Dropbox crashes\n"); @@ -2163,6 +2179,22 @@ void Dumpstate::DumpstateBoard(int out_fd) { return; } + /* + * mount debugfs for non-user builds which launch with S and unmount it + * after invoking dumpstateBoard_* methods. This is to enable debug builds + * to not have debugfs mounted during runtime. It will also ensure that + * debugfs is only accessed by the dumpstate HAL. + */ + auto api_level = android::base::GetIntProperty("ro.product.first_api_level", 0); + bool mount_debugfs = !PropertiesHelper::IsUserBuild() && api_level >= 31; + + if (mount_debugfs) { + RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"}, + AS_ROOT_20); + RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, + AS_ROOT_20); + } + std::vector<std::string> paths; std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { @@ -2262,6 +2294,10 @@ void Dumpstate::DumpstateBoard(int out_fd) { "there might be racing in content\n", killing_timeout_sec); } + if (mount_debugfs) { + RunCommand("unmount debugfs", {"umount", "/sys/kernel/debug"}, AS_ROOT_20); + } + auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); for (size_t i = 0; i < paths.size(); i++) { struct stat s; @@ -2872,6 +2908,13 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, RunDumpsysCritical(); } MaybeTakeEarlyScreenshot(); + + if (!is_dumpstate_restricted) { + // Snapshot the system trace now (if running) to avoid that dumpstate's + // own activity pushes out interesting data from the trace ring buffer. + // The trace file is added to the zip by MaybeAddSystemTraceToZip(). + MaybeSnapshotSystemTrace(); + } onUiIntensiveBugreportDumpsFinished(calling_uid); MaybeCheckUserConsent(calling_uid, calling_package); if (options_->telephony_only) { @@ -2962,6 +3005,26 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { TakeScreenshot(); } +void Dumpstate::MaybeSnapshotSystemTrace() { + // If a background system trace is happening and is marked as "suitable for + // bugreport" (i.e. bugreport_score > 0 in the trace config), this command + // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) + // case that no trace is ongoing, this command is a no-op. + // Note: this should not be enqueued as we need to freeze the trace before + // dumpstate starts. Otherwise the trace ring buffers will contain mostly + // the dumpstate's own activity which is irrelevant. + int res = RunCommand( + "SERIALIZE PERFETTO TRACE", + {"perfetto", "--save-for-bugreport"}, + CommandOptions::WithTimeout(10) + .DropRoot() + .CloseAllFileDescriptorsOnExec() + .Build()); + has_system_trace_ = res == 0; + // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip + // file in the later stages. +} + void Dumpstate::onUiIntensiveBugreportDumpsFinished(int32_t calling_uid) { if (calling_uid == AID_SHELL || !CalledByApi()) { return; diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 255243f7e3..f83968b59e 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -458,6 +458,11 @@ class Dumpstate { // Whether it should take an screenshot earlier in the process. bool do_early_screenshot_ = false; + // This is set to true when the trace snapshot request in the early call to + // MaybeSnapshotSystemTrace(). When this is true, the later stages of + // dumpstate will append the trace to the zip archive. + bool has_system_trace_ = false; + std::unique_ptr<Progress> progress_; // When set, defines a socket file-descriptor use to report progress to bugreportz @@ -543,6 +548,7 @@ class Dumpstate { RunStatus DumpstateDefaultAfterCritical(); void MaybeTakeEarlyScreenshot(); + void MaybeSnapshotSystemTrace(); void onUiIntensiveBugreportDumpsFinished(int32_t calling_uid); diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index fe6a34a514..0e366cb638 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -319,6 +319,16 @@ TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { */ class BugreportSectionTest : public Test { public: + ZipArchiveHandle handle; + + void SetUp() { + ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0); + } + + void TearDown() { + CloseArchive(handle); + } + static void SetUpTestCase() { ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(), ZippedBugreportGenerationTest::sections.get()); @@ -343,6 +353,19 @@ class BugreportSectionTest : public Test { } FAIL() << sectionName << " not found."; } + + /** + * Whether or not the content of the section is injected by other commands. + */ + bool IsContentInjectedByOthers(const std::string& line) { + // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`. + static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"}; + std::smatch match; + if (std::regex_match(line, match, kCommandHeader)) { + return true; + } + return false; + } }; TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { @@ -400,6 +423,28 @@ TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) { SectionExists("wifi", /* bytes= */ 100000); } +TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) { + // Extract the main entry to a temp file + TemporaryFile tmp_binary; + ASSERT_NE(-1, tmp_binary.fd); + ExtractBugreport(&handle, tmp_binary.fd); + + // Read line by line and identify sections + std::ifstream ifs(tmp_binary.path, std::ifstream::in); + std::string line; + std::string current_section_name; + while (std::getline(ifs, line)) { + std::string section_name; + if (IsSectionStart(line, §ion_name)) { + current_section_name = section_name; + } else if (IsSectionEnd(line)) { + current_section_name = ""; + } else if (!current_section_name.empty()) { + EXPECT_FALSE(IsContentInjectedByOthers(line)); + } + } +} + class DumpstateBinderTest : public Test { protected: void SetUp() override { diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 1327cfd155..a017246184 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -427,7 +427,7 @@ status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::mi << strerror(errno) << std::endl; status = -errno; break; - } else if (rc == 0) { + } else if (rc == 0 || time_left_ms() == 0) { status = TIMED_OUT; break; } diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp index 64bfdf9289..643b3ca26b 100644 --- a/cmds/idlcli/Android.bp +++ b/cmds/idlcli/Android.bp @@ -15,7 +15,7 @@ cc_defaults { name: "idlcli-defaults", shared_libs: [ - "android.hardware.vibrator-ndk_platform", + "android.hardware.vibrator-unstable-ndk_platform", "android.hardware.vibrator@1.0", "android.hardware.vibrator@1.1", "android.hardware.vibrator@1.2", diff --git a/data/etc/android.software.translation.xml b/data/etc/android.software.translation.xml new file mode 100644 index 0000000000..3b361e5cb8 --- /dev/null +++ b/data/etc/android.software.translation.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<permissions> + <feature name="android.software.translation" /> +</permissions> diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index dc6963fef1..2c340473d6 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -52,6 +52,7 @@ <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> <feature name="android.software.secure_lock_screen" /> + <feature name="android.software.translation" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index e878f86243..873b5b7f0b 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml @@ -52,6 +52,7 @@ <feature name="android.software.autofill" /> <feature name="android.software.cant_save_state" /> <feature name="android.software.secure_lock_screen" /> + <feature name="android.software.translation" /> <!-- Feature to specify if the device supports adding device admins. --> <feature name="android.software.device_admin" /> diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index fcc0b9b447..c7a8939766 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -727,7 +727,7 @@ enum { * - {@link ANDROID_IMAGE_DECODER_BAD_PARAMETER}: The AImageDecoder * is null. */ -int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder); +int32_t AImageDecoder_getRepeatCount(AImageDecoder* _Nonnull decoder) __INTRODUCED_IN(31); /** diff --git a/include/android/permission_manager.h b/include/android/permission_manager.h index 7817126eec..2d5d060840 100644 --- a/include/android/permission_manager.h +++ b/include/android/permission_manager.h @@ -64,8 +64,6 @@ enum { PERMISSION_MANAGER_STATUS_SERVICE_UNAVAILABLE = -2, }; -#if __ANDROID_API__ >= 31 - /** * Checks whether the package with the given pid/uid has been granted a permission. * @@ -84,8 +82,6 @@ int32_t APermissionManager_checkPermission(const char* permission, uid_t uid, int32_t* outResult) __INTRODUCED_IN(31); -#endif // __ANDROID_API__ >= 31 - __END_DECLS #endif // ANDROID_PERMISSION_MANAGER_H diff --git a/include/android/thermal.h b/include/android/thermal.h index 83582d6791..eb81534dc7 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -53,7 +53,7 @@ #include <sys/types.h> #if !defined(__INTRODUCED_IN) -#define __INTRODUCED_IN(30) /* Introduced in API level 30 */ +#define __INTRODUCED_IN(__api_level) /* nothing */ #endif #ifdef __cplusplus @@ -181,6 +181,51 @@ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, #endif // __ANDROID_API__ >= 30 +#if __ANDROID_API__ >= 31 + +/** + * Provides an estimate of how much thermal headroom the device currently has before + * hitting severe throttling. + * + * Note that this only attempts to track the headroom of slow-moving sensors, such as + * the skin temperature sensor. This means that there is no benefit to calling this function + * more frequently than about once per second, and attempted to call significantly + * more frequently may result in the function returning {@code NaN}. + * + * In addition, in order to be able to provide an accurate forecast, the system does + * not attempt to forecast until it has multiple temperature samples from which to + * extrapolate. This should only take a few seconds from the time of the first call, + * but during this time, no forecasting will occur, and the current headroom will be + * returned regardless of the value of {@code forecastSeconds}. + * + * The value returned is a non-negative float that represents how much of the thermal envelope + * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is + * (or will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the + * CPU, GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping + * to specific thermal levels beyond that point. This means that values greater than 1.0 + * may correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling. + * + * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any + * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale + * linearly with temperature, though temperature changes over time are typically not linear. + * Negative values will be clamped to 0.0 before returning. + * + * Available since API level 31. + * + * @param manager The manager instance to use. + * Acquired via {@link AThermal_acquireManager}. + * @param forecastSeconds how many seconds into the future to forecast. Given that device + * conditions may change at any time, forecasts from further in the + * future will likely be less accurate than forecasts in the near future. + * @return a value greater than equal to 0.0, where 1.0 indicates the SEVERE throttling threshold, + * as described above. Returns NaN if the device does not support this functionality or + * if this function is called significantly faster than once per second. + */ +float AThermal_getThermalHeadroom(AThermalManager *manager, + int forecastSeconds) __INTRODUCED_IN(31); + +#endif // __ANDROID_API__ >= 31 + #ifdef __cplusplus } #endif diff --git a/include/audiomanager/AudioManager.h b/include/audiomanager/AudioManager.h index 639df7a531..4aa2f60d45 100644 --- a/include/audiomanager/AudioManager.h +++ b/include/audiomanager/AudioManager.h @@ -37,6 +37,7 @@ typedef enum { PLAYER_STATE_STARTED = 2, PLAYER_STATE_PAUSED = 3, PLAYER_STATE_STOPPED = 4, + PLAYER_UPDATE_DEVICE_ID = 5, } player_state_t; // must be kept in sync with definitions in AudioManager.java diff --git a/include/audiomanager/IAudioManager.h b/include/audiomanager/IAudioManager.h index 2f5ccb89e9..7d1f38fb33 100644 --- a/include/audiomanager/IAudioManager.h +++ b/include/audiomanager/IAudioManager.h @@ -49,7 +49,8 @@ public: audio_content_type_t content, const sp<IBinder>& player) = 0; /*oneway*/ virtual status_t playerAttributes(audio_unique_id_t piid, audio_usage_t usage, audio_content_type_t content)= 0; - /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) = 0; + /*oneway*/ virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, + audio_port_handle_t deviceId) = 0; /*oneway*/ virtual status_t releasePlayer(audio_unique_id_t piid) = 0; virtual audio_unique_id_t trackRecorder(const sp<IBinder>& recorder) = 0; /*oneway*/ virtual status_t recorderEvent(audio_unique_id_t riid, recorder_state_t event) = 0; diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp index dae6eebaa5..15bd5c3913 100644 --- a/libs/adbd_auth/adbd_auth.cpp +++ b/libs/adbd_auth/adbd_auth.cpp @@ -282,9 +282,8 @@ public: LOG(FATAL) << "adbd_auth: unhandled packet type?"; } - output_queue_.pop_front(); - ssize_t rc = writev(framework_fd_.get(), iovs, iovcnt); + output_queue_.pop_front(); if (rc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { PLOG(ERROR) << "adbd_auth: failed to write to framework fd"; ReplaceFrameworkFd(unique_fd()); diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index feaea63c38..9ea9732594 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -179,15 +179,26 @@ cc_library { "--header-filter=^.*frameworks/native/libs/binder/.*.h$", ], tidy_checks_as_errors: [ - "*", + // Explicitly list the checks that should not occur in this module. + "abseil-*", + "android-*", + "bugprone-*", + "cert-*", + "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", "-clang-analyzer-core.uninitialized.Assign", - "-clang-analyzer-unix.Malloc,", + "-clang-analyzer-unix.Malloc", "-clang-analyzer-deadcode.DeadStores", "-clang-analyzer-optin.cplusplus.UninitializedObject", + "google-*", + "-google-readability-*", + "-google-runtime-references", + "misc-*", "-misc-no-recursion", "-misc-redundant-expression", "-misc-unused-using-decls", + "performance*", + "portability*", ], } diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 87eab526cc..440c98ccda 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1477,6 +1477,31 @@ data_unsorted: goto data_sorted; } +status_t Parcel::readVectorSizeWithCoarseBoundCheck(int32_t *size) const { + int32_t requestedSize; + const status_t status = readInt32(&requestedSize); + if (status != NO_ERROR) return status; + + // We permit negative sizes, which indicate presence of a nullable vector, + // i.e. a vector embedded in std::optional, std::unique_ptr, or std::shared_ptr. + if (requestedSize > 0) { + // Check if there are fewer bytes than vector elements. + // A lower bound is 1 byte per element, satisfied by some enum and int8_t and uint8_t. + const size_t availableBytes = dataAvail(); + if (static_cast<size_t>(requestedSize) > availableBytes) { + // We have a size that is greater than the number of bytes available. + // On bounds failure we do not 'rewind' position by 4 bytes of the size already read. + ALOGW("%s: rejecting out of bounds vector size (requestedSize):%d " + "Parcel{dataAvail:%zu mDataSize:%zu mDataPos:%zu mDataCapacity:%zu}", + __func__, requestedSize, availableBytes, mDataSize, mDataPos, mDataCapacity); + return BAD_VALUE; + } + } + + *size = requestedSize; + return NO_ERROR; +} + status_t Parcel::read(void* outData, size_t len) const { if (len > INT32_MAX) { @@ -1699,7 +1724,7 @@ status_t Parcel::readDoubleVector(std::vector<double>* val) const { status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { const int32_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { @@ -1721,7 +1746,7 @@ status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { const int32_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { @@ -1742,7 +1767,7 @@ status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { status_t Parcel::readBoolVector(std::vector<bool>* val) const { int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); if (status != OK) { return status; diff --git a/libs/binder/ParcelableHolder.cpp b/libs/binder/ParcelableHolder.cpp index b2b86716d5..2e86b7415a 100644 --- a/libs/binder/ParcelableHolder.cpp +++ b/libs/binder/ParcelableHolder.cpp @@ -37,7 +37,7 @@ status_t ParcelableHolder::writeToParcel(Parcel* p) const { size_t sizePos = p->dataPosition(); RETURN_ON_FAILURE(p->writeInt32(0)); size_t dataStartPos = p->dataPosition(); - RETURN_ON_FAILURE(p->writeUtf8AsUtf16(this->mParcelableName)); + RETURN_ON_FAILURE(p->writeString16(this->mParcelableName)); this->mParcelable->writeToParcel(p); size_t dataSize = p->dataPosition() - dataStartPos; diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 97e282e783..00a14f408d 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -10,6 +10,9 @@ "name": "binderAllocationLimits" }, { + "name": "binderClearBufTest" + }, + { "name": "binderDriverInterfaceTest" }, { diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h index 988508eaba..e2a0f874ff 100644 --- a/libs/binder/include/binder/IInterface.h +++ b/libs/binder/include/binder/IInterface.h @@ -232,7 +232,6 @@ constexpr const char* const kManualInterfaces[] = { "android.gui.IGraphicBufferConsumer", "android.gui.IRegionSamplingListener", "android.gui.ITransactionComposerListener", - "android.gui.IScreenCaptureListener", "android.gui.SensorEventConnection", "android.gui.SensorServer", "android.hardware.ICamera", diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index b49951b448..54c49e4c0c 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -517,6 +517,11 @@ private: void initState(); void scanForFds() const; status_t validateReadData(size_t len) const; + + // Reads an int32 size and does a coarse bounds check against the number + // of available bytes in the Parcel. + status_t readVectorSizeWithCoarseBoundCheck(int32_t *size) const; + void updateWorkSourceRequestHeaderPosition() const; status_t finishFlattenBinder(const sp<IBinder>& binder); @@ -787,6 +792,7 @@ status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) { template<typename T> status_t Parcel::resizeOutVector(std::vector<T>* val) const { int32_t size; + // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -802,6 +808,7 @@ status_t Parcel::resizeOutVector(std::vector<T>* val) const { template<typename T> status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const { int32_t size; + // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -818,6 +825,7 @@ status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const { template<typename T> status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { int32_t size; + // used for allocating 'out' vector args, do not use readVectorSizeWithCoarseBoundCheck() here status_t err = readInt32(&size); if (err != NO_ERROR) { return err; @@ -834,7 +842,7 @@ status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { template<typename T> status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const { int32_t read_size; - status_t err = readInt32(&read_size); + status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); if (err != NO_ERROR) { return err; } @@ -850,7 +858,7 @@ status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const { template<typename T> status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const { int32_t read_size; - status_t err = readInt32(&read_size); + status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); if (err != NO_ERROR) { return err; } @@ -870,7 +878,7 @@ template<typename T> status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val, size_t* size) const { int32_t read_size; - status_t err = readInt32(&read_size); + status_t err = readVectorSizeWithCoarseBoundCheck(&read_size); if (err != NO_ERROR) { return err; } @@ -923,7 +931,7 @@ status_t Parcel::unsafeReadTypedVector( std::vector<T>* val, status_t(Parcel::*read_func)(U*) const) const { int32_t size; - status_t status = this->readInt32(&size); + status_t status = this->readVectorSizeWithCoarseBoundCheck(&size); if (status != OK) { return status; @@ -965,7 +973,7 @@ status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val, status_t(Parcel::*read_func)(T*) const) const { const size_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { @@ -989,7 +997,7 @@ status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val, status_t(Parcel::*read_func)(T*) const) const { const size_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { @@ -1093,7 +1101,7 @@ template<typename T> status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const { const size_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { @@ -1117,7 +1125,7 @@ template<typename T> status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const { const size_t start = dataPosition(); int32_t size; - status_t status = readInt32(&size); + status_t status = readVectorSizeWithCoarseBoundCheck(&size); val->reset(); if (status != OK || size < 0) { diff --git a/libs/binder/include/binder/ParcelableHolder.h b/libs/binder/include/binder/ParcelableHolder.h index 7024a4b07f..9e4475c947 100644 --- a/libs/binder/include/binder/ParcelableHolder.h +++ b/libs/binder/include/binder/ParcelableHolder.h @@ -18,6 +18,7 @@ #include <binder/Parcel.h> #include <binder/Parcelable.h> +#include <utils/String16.h> #include <mutex> #include <optional> #include <tuple> @@ -52,53 +53,59 @@ public: } template <typename T> - bool setParcelable(T&& p) { + status_t setParcelable(T&& p) { using Tt = typename std::decay<T>::type; return setParcelable<Tt>(std::make_shared<Tt>(std::forward<T>(p))); } template <typename T> - bool setParcelable(std::shared_ptr<T> p) { + status_t setParcelable(std::shared_ptr<T> p) { static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable"); if (p && this->getStability() > p->getStability()) { - return false; + return android::BAD_VALUE; } this->mParcelable = p; this->mParcelableName = T::getParcelableDescriptor(); this->mParcelPtr = nullptr; - return true; + return android::OK; } template <typename T> - std::shared_ptr<T> getParcelable() const { + status_t getParcelable(std::shared_ptr<T>* ret) const { static_assert(std::is_base_of<Parcelable, T>::value, "T must be derived from Parcelable"); - const std::string& parcelableDesc = T::getParcelableDescriptor(); + const String16& parcelableDesc = T::getParcelableDescriptor(); if (!this->mParcelPtr) { if (!this->mParcelable || !this->mParcelableName) { ALOGD("empty ParcelableHolder"); - return nullptr; + *ret = nullptr; + return android::OK; } else if (parcelableDesc != *mParcelableName) { ALOGD("extension class name mismatch expected:%s actual:%s", - mParcelableName->c_str(), parcelableDesc.c_str()); - return nullptr; + String8(*mParcelableName).c_str(), String8(parcelableDesc).c_str()); + *ret = nullptr; + return android::BAD_VALUE; } - return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + return android::OK; } this->mParcelPtr->setDataPosition(0); - status_t status = this->mParcelPtr->readUtf8FromUtf16(&this->mParcelableName); + status_t status = this->mParcelPtr->readString16(&this->mParcelableName); if (status != android::OK || parcelableDesc != this->mParcelableName) { this->mParcelableName = std::nullopt; - return nullptr; + *ret = nullptr; + return status; } this->mParcelable = std::make_shared<T>(); status = mParcelable.get()->readFromParcel(this->mParcelPtr.get()); if (status != android::OK) { this->mParcelableName = std::nullopt; this->mParcelable = nullptr; - return nullptr; + *ret = nullptr; + return status; } this->mParcelPtr = nullptr; - return std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + *ret = std::shared_ptr<T>(mParcelable, reinterpret_cast<T*>(mParcelable.get())); + return android::OK; } Stability getStability() const override { return mStability; } @@ -124,7 +131,7 @@ public: private: mutable std::shared_ptr<Parcelable> mParcelable; - mutable std::optional<std::string> mParcelableName; + mutable std::optional<String16> mParcelableName; mutable std::unique_ptr<Parcel> mParcelPtr; Stability mStability; }; diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index bdb74dc989..82f388284c 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -104,15 +104,28 @@ cc_library { "--header-filter=^.*frameworks/native/libs/binder/.*.h$", ], tidy_checks_as_errors: [ - "*", + // Explicitly list the checks that should not occur in this module. + "abseil-*", + "android-*", + "bugprone-*", + "cert-*", + "clang-analyzer-*", "-clang-analyzer-core.CallAndMessage", "-clang-analyzer-core.uninitialized.Assign", - "-clang-analyzer-unix.Malloc,", + "-clang-analyzer-unix.Malloc", "-clang-analyzer-deadcode.DeadStores", "-clang-analyzer-optin.cplusplus.UninitializedObject", + "google-*", + "-google-readability-*", + "-google-runtime-references", + "misc-*", "-misc-no-recursion", + "-misc-non-private-member-variables-in-classes", "-misc-redundant-expression", + "-misc-unused-parameters", "-misc-unused-using-decls", + "performance*", + "portability*", ], } diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 350c6585a2..454fbd05cf 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -301,6 +301,26 @@ AIBinder* AIBinder_Weak_promote(AIBinder_Weak* weakBinder) { return binder.get(); } +AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak) { + if (weak == nullptr) { + return nullptr; + } + + return new AIBinder_Weak{weak->binder}; +} + +bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs) { + if (lhs == nullptr || rhs == nullptr) return lhs < rhs; + + return const_cast<AIBinder*>(lhs)->getBinder() < const_cast<AIBinder*>(rhs)->getBinder(); +} + +bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs) { + if (lhs == nullptr || rhs == nullptr) return lhs < rhs; + + return lhs->binder < rhs->binder; +} + AIBinder_Class::AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index a4f444192d..a1102e2833 100644 --- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -83,7 +83,8 @@ class SharedRefBase { template <class T, class... Args> static std::shared_ptr<T> make(Args&&... args) { T* t = new T(std::forward<Args>(args)...); - return t->template ref<T>(); + // warning: Potential leak of memory pointed to by 't' [clang-analyzer-unix.Malloc] + return t->template ref<T>(); // NOLINT(clang-analyzer-unix.Malloc) } static void operator delete(void* p) { std::free(p); } diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h index e1d6c34d1d..6636a416e2 100644 --- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h @@ -82,34 +82,37 @@ class AParcelableHolder { } template <typename T> - bool setParcelable(const T& p) { + binder_status_t setParcelable(const T& p) { if (this->mStability > T::_aidl_stability) { - return false; + return STATUS_BAD_VALUE; } AParcel_reset(mParcel.get()); AParcel_writeString(mParcel.get(), T::descriptor, strlen(T::descriptor)); p.writeToParcel(mParcel.get()); - return true; + return STATUS_OK; } template <typename T> - std::unique_ptr<T> getParcelable() const { + binder_status_t getParcelable(std::optional<T>* ret) const { const std::string parcelableDesc(T::descriptor); AParcel_setDataPosition(mParcel.get(), 0); if (AParcel_getDataSize(mParcel.get()) == 0) { - return nullptr; + *ret = std::nullopt; + return STATUS_OK; } std::string parcelableDescInParcel; binder_status_t status = AParcel_readString(mParcel.get(), &parcelableDescInParcel); if (status != STATUS_OK || parcelableDesc != parcelableDescInParcel) { - return nullptr; + *ret = std::nullopt; + return status; } - std::unique_ptr<T> ret = std::make_unique<T>(); - status = ret->readFromParcel(this->mParcel.get()); + *ret = std::make_optional<T>(); + status = (*ret)->readFromParcel(this->mParcel.get()); if (status != STATUS_OK) { - return nullptr; + *ret = std::nullopt; + return status; } - return std::move(ret); + return STATUS_OK; } void reset() { AParcel_reset(mParcel.get()); } diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 5e1ed4689d..0ca3a0760c 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -657,6 +657,68 @@ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) __INTRODU */ const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31); +/** + * Whether AIBinder is less than another. + * + * This provides a per-process-unique total ordering of binders determined by + * an underlying allocation address where a null AIBinder* is considered to be + * ordered before all other binders. + * + * AIBinder* pointers themselves actually also create a per-process-unique total + * ordering. However, this ordering is inconsistent with AIBinder_Weak_lt for + * remote binders. + * + * Available since API level 31. + * + * \param lhs comparison object + * \param rhs comparison object + * + * \return whether "lhs < rhs" is true + */ +bool AIBinder_lt(const AIBinder* lhs, const AIBinder* rhs); + +/** + * Clone an AIBinder_Weak. Useful because even if a weak binder promotes to a + * null value, after further binder transactions, it may no longer promote to a + * null value. + * + * Available since API level 31. + * + * \param weak Object to clone + * + * \return clone of the input parameter. This must be deleted with + * AIBinder_Weak_delete. Null if weak input parameter is also null. + */ +AIBinder_Weak* AIBinder_Weak_clone(const AIBinder_Weak* weak); + +/** + * Whether AIBinder_Weak is less than another. + * + * This provides a per-process-unique total ordering of binders which is exactly + * the same as AIBinder_lt. Similarly, a null AIBinder_Weak* is considered to be + * ordered before all other weak references. + * + * If you have many AIBinder_Weak* objects which are all references to distinct + * binder objects which happen to have the same underlying address (as ordered + * by AIBinder_lt), these AIBinder_Weak* objects will retain the same order with + * respect to all other AIBinder_Weak* pointers with different underlying + * addresses and are also guaranteed to have a per-process-unique ordering. That + * is, even though multiple AIBinder* instances may happen to be allocated at + * the same underlying address, this function will still correctly distinguish + * that these are weak pointers to different binder objects. + * + * Unlike AIBinder*, the AIBinder_Weak* addresses themselves have nothing to do + * with the underlying binder. + * + * Available since API level 31. + * + * \param lhs comparison object + * \param rhs comparison object + * + * \return whether "lhs < rhs" is true + */ +bool AIBinder_Weak_lt(const AIBinder_Weak* lhs, const AIBinder_Weak* rhs); + #endif //__ANDROID_API__ >= 31 __END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index e233ffd9b4..9a93bf3c4b 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -122,6 +122,9 @@ LIBBINDER_NDK31 { # introduced=31 AServiceManager_waitForService; # apex llndk AIBinder_Class_getDescriptor; + AIBinder_Weak_clone; + AIBinder_Weak_lt; + AIBinder_lt; AParcel_appendFrom; AParcel_create; AParcel_getDataSize; diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index 87f1d45350..988f7f3c6d 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -157,6 +157,24 @@ cc_test { require_root: true, } +cc_test { + name: "binderClearBufTest", + defaults: ["binder_test_defaults"], + srcs: [ + "binderClearBufTest.cpp", + ], + + shared_libs: [ + "libbase", + "libbinder", + "liblog", + "libutils", + ], + + test_suites: ["general-tests"], + require_root: true, +} + aidl_interface { name: "binderStabilityTestIface", unstable: true, diff --git a/libs/binder/tests/binderClearBufTest.cpp b/libs/binder/tests/binderClearBufTest.cpp new file mode 100644 index 0000000000..a565e72091 --- /dev/null +++ b/libs/binder/tests/binderClearBufTest.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/logging.h> +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/Stability.h> +#include <gtest/gtest.h> + +#include <sys/prctl.h> +#include <thread> + +using namespace android; + +const String16 kServerName = String16("binderClearBuf"); + +std::string hexString(const void* bytes, size_t len) { + if (bytes == nullptr) return "<null>"; + + const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes); + char chars[] = "0123456789abcdef"; + std::string result; + result.resize(len * 2); + + for (size_t i = 0; i < len; i++) { + result[2 * i] = chars[bytes8[i] >> 4]; + result[2 * i + 1] = chars[bytes8[i] & 0xf]; + } + + return result; +} + +class FooBar : public BBinder { + public: + enum { + TRANSACTION_REPEAT_STRING = IBinder::FIRST_CALL_TRANSACTION, + }; + + std::mutex foo; + std::string last; + + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + // not checking data, since there is no hook at the time this test is + // written to check values there are set to zero. Instead, we only check + // the reply parcel. + + switch (code) { + case TRANSACTION_REPEAT_STRING: { + const char* str = data.readCString(); + return reply->writeCString(str == nullptr ? "<null>" : str); + } + } + return BBinder::onTransact(code, data, reply, flags); + } + static std::string RepeatString(const sp<IBinder> binder, + const std::string& repeat, + std::string* outBuffer) { + Parcel data; + data.writeCString(repeat.c_str()); + std::string result; + const uint8_t* lastReply; + size_t lastReplySize; + { + Parcel reply; + binder->transact(TRANSACTION_REPEAT_STRING, data, &reply, FLAG_CLEAR_BUF); + result = reply.readCString(); + lastReply = reply.data(); + lastReplySize = reply.dataSize(); + } + IPCThreadState::self()->flushCommands(); + *outBuffer = hexString(lastReply, lastReplySize); + return result; + } +}; + +TEST(BinderClearBuf, ClearKernelBuffer) { + sp<IBinder> binder = defaultServiceManager()->getService(kServerName); + ASSERT_NE(nullptr, binder); + + std::string replyBuffer; + std::string result = FooBar::RepeatString(binder, "foo", &replyBuffer); + EXPECT_EQ("foo", result); + + // the buffer must have at least some length for the string, but we will + // just check it has some length, to avoid assuming anything about the + // format + EXPECT_GT(replyBuffer.size(), 0); + + for (size_t i = 0; i < replyBuffer.size(); i++) { + EXPECT_EQ(replyBuffer[i], '0') << "reply buffer at " << i; + } +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + + sp<IBinder> server = new FooBar; + android::defaultServiceManager()->addService(kServerName, server); + + IPCThreadState::self()->joinThreadPool(true); + exit(1); // should not reach + } + + // This is not racey. Just giving these services some time to register before we call + // getService which sleeps for much longer. One alternative would be to + // start a threadpool + use waitForService, but we want to have as few + // binder things going on in this test as possible, since we are checking + // memory is zero'd which the kernel has a right to change. + usleep(100000); + + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/fuzzers/Android.bp b/libs/binder/tests/fuzzers/Android.bp index c465bed250..5531296edb 100644 --- a/libs/binder/tests/fuzzers/Android.bp +++ b/libs/binder/tests/fuzzers/Android.bp @@ -69,3 +69,18 @@ cc_fuzz { defaults: ["binder_fuzz_defaults"], srcs: ["TextOutputFuzz.cpp"], } + +cc_fuzz { + name: "binder_bufferedTextOutputFuzz", + include_dirs: [ + "frameworks/native/libs/binder", + ], + defaults: ["binder_fuzz_defaults"], + srcs: ["BufferedTextOutputFuzz.cpp"], +} + +cc_fuzz { + name: "binder_memoryDealerFuzz", + defaults: ["binder_fuzz_defaults"], + srcs: ["MemoryDealerFuzz.cpp"], +} diff --git a/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp new file mode 100644 index 0000000000..09cb2162f7 --- /dev/null +++ b/libs/binder/tests/fuzzers/BufferedTextOutputFuzz.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <commonFuzzHelpers.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <string> +#include <vector> +#include "BufferedTextOutput.h" + +namespace android { + +class FuzzBufferedTextOutput : public BufferedTextOutput { +public: + FuzzBufferedTextOutput(uint32_t flags) : BufferedTextOutput(flags) {} + virtual status_t writeLines(const struct iovec& buf, size_t) { + size_t len = buf.iov_len; + void* tmp_buf = malloc(len); + + if (tmp_buf == NULL) { + return status_t(); + } + + // This will attempt to read data from iov_base to ensure valid params were passed. + memcpy(tmp_buf, buf.iov_base, len); + free(tmp_buf); + return status_t(); + } +}; + +// Fuzzer entry point. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp(data, size); + uint32_t flags = fdp.ConsumeIntegral<uint32_t>(); + size_t push_count = 0; + std::shared_ptr<BufferedTextOutput> bTextOutput(new FuzzBufferedTextOutput(flags)); + + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { + bTextOutput->pushBundle(); + push_count++; + }, + [&]() -> void { + std::string txt = fdp.ConsumeRandomLengthString(fdp.remaining_bytes()); + size_t len = fdp.ConsumeIntegralInRange<size_t>(0, txt.length()); + bTextOutput->print(txt.c_str(), len); + }, + [&]() -> void { + if (push_count == 0) return; + + bTextOutput->popBundle(); + push_count--; + }, + })(); + } + + return 0; +} +} // namespace android diff --git a/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp new file mode 100644 index 0000000000..f9dda8c558 --- /dev/null +++ b/libs/binder/tests/fuzzers/MemoryDealerFuzz.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/MemoryDealer.h> +#include <commonFuzzHelpers.h> +#include <fuzzer/FuzzedDataProvider.h> +#include <string> +#include <unordered_set> + +namespace android { + +static constexpr size_t kMaxBufferSize = 10000; +static constexpr size_t kMaxDealerSize = 1024 * 512; +static constexpr size_t kMaxAllocSize = 1024; + +// Fuzzer entry point. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > kMaxBufferSize) { + return 0; + } + + FuzzedDataProvider fdp(data, size); + size_t dSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxDealerSize); + std::string name = fdp.ConsumeRandomLengthString(fdp.remaining_bytes()); + uint32_t flags = fdp.ConsumeIntegral<uint32_t>(); + sp<MemoryDealer> dealer = new MemoryDealer(dSize, name.c_str(), flags); + + // This is used to track offsets that have been freed already to avoid an expected fatal log. + std::unordered_set<size_t> free_list; + + while (fdp.remaining_bytes() > 0) { + fdp.PickValueInArray<std::function<void()>>({ + [&]() -> void { dealer->getAllocationAlignment(); }, + [&]() -> void { dealer->getMemoryHeap(); }, + [&]() -> void { + size_t offset = fdp.ConsumeIntegral<size_t>(); + + // Offset has already been freed, so return instead. + if (free_list.find(offset) != free_list.end()) return; + + dealer->deallocate(offset); + free_list.insert(offset); + }, + [&]() -> void { + std::string randString = fdp.ConsumeRandomLengthString(fdp.remaining_bytes()); + dealer->dump(randString.c_str()); + }, + [&]() -> void { + size_t allocSize = fdp.ConsumeIntegralInRange<size_t>(0, kMaxAllocSize); + sp<IMemory> allocated = dealer->allocate(allocSize); + // If the allocation was successful, try to write to it + if (allocated != nullptr && allocated->unsecurePointer() != nullptr) { + memset(allocated->unsecurePointer(), 1, allocated->size()); + + // Clear the address from freelist since it has been allocated over again. + free_list.erase(allocated->offset()); + } + }, + })(); + } + + return 0; +} +} // namespace android diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 4209dc5c6b..2e72cc422c 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -56,6 +56,7 @@ static uint32_t gNCpus = 0; static std::vector<std::vector<uint32_t>> gPolicyFreqs; static std::vector<std::vector<uint32_t>> gPolicyCpus; static std::set<uint32_t> gAllFreqs; +static unique_fd gTisTotalMapFd; static unique_fd gTisMapFd; static unique_fd gConcurrentMapFd; static unique_fd gUidLastUpdateMapFd; @@ -129,6 +130,10 @@ static bool initGlobals() { gPolicyCpus.emplace_back(*cpus); } + gTisTotalMapFd = + unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_total_time_in_state_map")}; + if (gTisTotalMapFd < 0) return false; + gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")}; if (gTisMapFd < 0) return false; @@ -239,6 +244,31 @@ std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() { return gPolicyFreqs; } +std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes() { + if (!gInitialized && !initGlobals()) return {}; + + std::vector<std::vector<uint64_t>> out; + uint32_t maxFreqCount = 0; + for (const auto &freqList : gPolicyFreqs) { + if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size(); + out.emplace_back(freqList.size(), 0); + } + + std::vector<uint64_t> vals(gNCpus); + const uint32_t freqCount = maxFreqCount <= MAX_FREQS_FOR_TOTAL ? maxFreqCount : + MAX_FREQS_FOR_TOTAL; + for (uint32_t freqIdx = 0; freqIdx < freqCount; ++freqIdx) { + if (findMapEntry(gTisTotalMapFd, &freqIdx, vals.data())) return {}; + for (uint32_t policyIdx = 0; policyIdx < gNPolicies; ++policyIdx) { + if (freqIdx >= gPolicyFreqs[policyIdx].size()) continue; + for (const auto &cpu : gPolicyCpus[policyIdx]) { + out[policyIdx][freqIdx] += vals[cpu]; + } + } + } + + return out; +} // Retrieve the times in ns that uid spent running at each CPU frequency. // Return contains no value on error, otherwise it contains a vector of vectors using the format: // [[t0_0, t0_1, ...], diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 87a328a5a5..46de66936a 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -23,6 +23,7 @@ namespace android { namespace bpf { bool startTrackingUidTimes(); +std::optional<std::vector<std::vector<uint64_t>>> getTotalCpuFreqTimes(); std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid); std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>> getUidsCpuFreqTimes(); diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp index 519689bb0c..d25b2e9d6f 100644 --- a/libs/cputimeinstate/testtimeinstate.cpp +++ b/libs/cputimeinstate/testtimeinstate.cpp @@ -40,6 +40,12 @@ static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365; using std::vector; +TEST(TimeInStateTest, TotalTimeInState) { + auto times = getTotalCpuFreqTimes(); + ASSERT_TRUE(times.has_value()); + EXPECT_FALSE(times->empty()); +} + TEST(TimeInStateTest, SingleUidTimeInState) { auto times = getUidCpuFreqTimes(0); ASSERT_TRUE(times.has_value()); @@ -186,6 +192,31 @@ TEST(TimeInStateTest, AllUidUpdatedTimeInState) { } } +TEST(TimeInStateTest, TotalAndAllUidTimeInStateConsistent) { + auto allUid = getUidsCpuFreqTimes(); + auto total = getTotalCpuFreqTimes(); + + ASSERT_TRUE(allUid.has_value() && total.has_value()); + + // Check the number of policies. + ASSERT_EQ(allUid->at(0).size(), total->size()); + + for (uint32_t policyIdx = 0; policyIdx < total->size(); ++policyIdx) { + std::vector<uint64_t> totalTimes = total->at(policyIdx); + uint32_t totalFreqsCount = totalTimes.size(); + std::vector<uint64_t> allUidTimes(totalFreqsCount, 0); + for (auto const &[uid, uidTimes]: *allUid) { + for (uint32_t freqIdx = 0; freqIdx < uidTimes[policyIdx].size(); ++freqIdx) { + allUidTimes[std::min(freqIdx, totalFreqsCount - 1)] += uidTimes[policyIdx][freqIdx]; + } + } + + for (uint32_t freqIdx = 0; freqIdx < totalFreqsCount; ++freqIdx) { + ASSERT_LE(allUidTimes[freqIdx], totalTimes[freqIdx]); + } + } +} + TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) { uint64_t zero = 0; auto maps = {getUidsCpuFreqTimes(), getUidsUpdatedCpuFreqTimes(&zero)}; @@ -292,6 +323,22 @@ void TestCheckDelta(uint64_t before, uint64_t after) { ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf()); } +TEST(TimeInStateTest, TotalTimeInStateMonotonic) { + auto before = getTotalCpuFreqTimes(); + ASSERT_TRUE(before.has_value()); + sleep(1); + auto after = getTotalCpuFreqTimes(); + ASSERT_TRUE(after.has_value()); + + for (uint32_t policyIdx = 0; policyIdx < after->size(); ++policyIdx) { + auto timesBefore = before->at(policyIdx); + auto timesAfter = after->at(policyIdx); + for (uint32_t freqIdx = 0; freqIdx < timesAfter.size(); ++freqIdx) { + ASSERT_NO_FATAL_FAILURE(TestCheckDelta(timesBefore[freqIdx], timesAfter[freqIdx])); + } + } +} + TEST(TimeInStateTest, AllUidTimeInStateMonotonic) { auto map1 = getUidsCpuFreqTimes(); ASSERT_TRUE(map1.has_value()); diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index af9ef06b86..38ae353a68 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -38,9 +38,10 @@ filegroup { cc_library_shared { name: "libgui", - vendor_available: false, + vendor_available: true, vndk: { enabled: true, + private: true, }, double_loadable: true, @@ -68,7 +69,6 @@ cc_library_shared { "IGraphicBufferProducer.cpp", "IProducerListener.cpp", "IRegionSamplingListener.cpp", - "IScreenCaptureListener.cpp", "ISurfaceComposer.cpp", "ISurfaceComposerClient.cpp", "ITransactionCompletedListener.cpp", @@ -77,6 +77,7 @@ cc_library_shared { "LayerState.cpp", "OccupancyTracker.cpp", "StreamSplitter.cpp", + "ScreenCaptureResults.cpp", "Surface.cpp", "SurfaceControl.cpp", "SurfaceComposerClient.cpp", @@ -161,6 +162,7 @@ cc_library_static { filegroup { name: "libgui_bufferqueue_sources", srcs: [ + "BatchBufferOps.cpp", "BufferItem.cpp", "BufferQueue.cpp", "BufferQueueConsumer.cpp", @@ -171,7 +173,7 @@ filegroup { "FrameTimestamps.cpp", "GLConsumerUtils.cpp", "HdrMetadata.cpp", - "QueueBufferInputOutput.cpp", + "IGraphicBufferProducerFlattenables.cpp", "bufferqueue/1.0/Conversion.cpp", "bufferqueue/1.0/H2BProducerListener.cpp", "bufferqueue/1.0/WProducerListener.cpp", diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index ee5552f20e..f4b5a26033 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -354,6 +354,16 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh); mAutoRefresh = bufferItem.mAutoRefresh; } + { + std::unique_lock _lock{mTimestampMutex}; + auto dequeueTime = mDequeueTimestamps.find(buffer->getId()); + if (dequeueTime != mDequeueTimestamps.end()) { + Parcel p; + p.writeInt64(dequeueTime->second); + t->setMetadata(mSurfaceControl, METADATA_DEQUEUE_TIME, p); + mDequeueTimestamps.erase(dequeueTime); + } + } auto mergeTransaction = [&t, currentFrameNumber = bufferItem.mFrameNumber]( @@ -371,7 +381,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { mPendingTransactions.end()); if (applyTransaction) { - t->apply(); + t->setApplyToken(mApplyToken).apply(); } BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 @@ -412,6 +422,16 @@ void BLASTBufferQueue::onFrameReplaced(const BufferItem& item) { // Do nothing since we are not storing unacquired buffer items locally. } +void BLASTBufferQueue::onFrameDequeued(const uint64_t bufferId) { + std::unique_lock _lock{mTimestampMutex}; + mDequeueTimestamps[bufferId] = systemTime(); +}; + +void BLASTBufferQueue::onFrameCancelled(const uint64_t bufferId) { + std::unique_lock _lock{mTimestampMutex}; + mDequeueTimestamps.erase(bufferId); +}; + void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) { std::lock_guard _lock{mMutex}; mNextTransaction = t; @@ -600,6 +620,14 @@ public: return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, producerControlledByApp, output); } + + int query(int what, int* value) override { + if (what == NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER) { + *value = 1; + return NO_ERROR; + } + return BufferQueueProducer::query(what, value); + } }; // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer. diff --git a/libs/gui/BatchBufferOps.cpp b/libs/gui/BatchBufferOps.cpp new file mode 100644 index 0000000000..60aceb1bbd --- /dev/null +++ b/libs/gui/BatchBufferOps.cpp @@ -0,0 +1,125 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#define LOG_TAG "IGBPBatchOps" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 + +#include <gui/IGraphicBufferProducer.h> + +namespace android { + +/** + * Default implementation of batched buffer operations. These default + * implementations call into the non-batched version of the same operation. + */ + +status_t IGraphicBufferProducer::requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(slots.size()); + for (int32_t slot : slots) { + RequestBufferOutput& output = outputs->emplace_back(); + output.result = requestBuffer(static_cast<int>(slot), + &output.buffer); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (const DequeueBufferInput& input : inputs) { + DequeueBufferOutput& output = outputs->emplace_back(); + output.result = dequeueBuffer( + &output.slot, + &output.fence, + input.width, + input.height, + input.format, + input.usage, + &output.bufferAge, + input.getTimestamps ? &output.timestamps.emplace() : nullptr); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::detachBuffers( + const std::vector<int32_t>& slots, + std::vector<status_t>* results) { + results->clear(); + results->reserve(slots.size()); + for (int32_t slot : slots) { + results->emplace_back(detachBuffer(slot)); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(buffers.size()); + for (const sp<GraphicBuffer>& buffer : buffers) { + AttachBufferOutput& output = outputs->emplace_back(); + output.result = attachBuffer(&output.slot, buffer); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::queueBuffers( + const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (const QueueBufferInput& input : inputs) { + QueueBufferOutput& output = outputs->emplace_back(); + output.result = queueBuffer(input.slot, input, &output); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) { + results->clear(); + results->reserve(inputs.size()); + for (const CancelBufferInput& input : inputs) { + results->emplace_back() = cancelBuffer(input.slot, input.fence); + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) { + outputs->clear(); + outputs->reserve(inputs.size()); + for (int32_t input : inputs) { + QueryOutput& output = outputs->emplace_back(); + int value{}; + output.result = static_cast<status_t>( + query(static_cast<int>(input), &value)); + output.value = static_cast<int64_t>(value); + } + return NO_ERROR; +} + +} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index ad00939976..c1f9b85229 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -74,6 +74,13 @@ enum { GET_CONSUMER_USAGE, SET_LEGACY_BUFFER_DROP, SET_AUTO_PREROTATION, + REQUEST_BUFFERS, + DEQUEUE_BUFFERS, + DETACH_BUFFERS, + ATTACH_BUFFERS, + QUEUE_BUFFERS, + CANCEL_BUFFERS, + QUERY_MULTIPLE, }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -90,7 +97,7 @@ public: Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); data.writeInt32(bufferIdx); - status_t result =remote()->transact(REQUEST_BUFFER, data, &reply); + status_t result = remote()->transact(REQUEST_BUFFER, data, &reply); if (result != NO_ERROR) { return result; } @@ -107,6 +114,27 @@ public: return result; } + virtual status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) override { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(slots); + status_t result = remote()->transact(REQUEST_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (RequestBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + + return result; + } + virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) { Parcel data, reply; data.writeInterfaceToken( @@ -183,6 +211,29 @@ public: return result; } + virtual status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const auto& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(DEQUEUE_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (auto& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t detachBuffer(int slot) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -195,6 +246,19 @@ public: return result; } + virtual status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(slots); + status_t result = remote()->transact(DETACH_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32Vector(results); + return result; + } + virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) { if (outBuffer == nullptr) { @@ -256,6 +320,39 @@ public: return result; } + virtual status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(buffers); + for (const sp<GraphicBuffer>& buffer : buffers) { + data.write(*buffer.get()); + } + status_t result = remote()->transact(ATTACH_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (AttachBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + if (result == NO_ERROR) { + for (AttachBufferOutput& output : *outputs) { + if (output.result == NO_ERROR && output.slot < 0) { + ALOGE("attachBuffers returned invalid slot %d", + output.slot); + android_errorWriteLog(0x534e4554, "37478824"); + output.result = UNKNOWN_ERROR; + } + } + } + return result; + } + virtual status_t queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) { Parcel data, reply; @@ -278,6 +375,28 @@ public: return result; } + virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const QueueBufferInput& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(QUEUE_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (QueueBufferOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t cancelBuffer(int buf, const sp<Fence>& fence) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -291,6 +410,23 @@ public: return result; } + virtual status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeVectorSize(inputs); + for (const CancelBufferInput& input : inputs) { + data.write(input); + } + status_t result = remote()->transact(CANCEL_BUFFERS, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.readInt32Vector(results); + return result; + } + virtual int query(int what, int* value) { Parcel data, reply; data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); @@ -304,6 +440,25 @@ public: return result; } + virtual status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) { + Parcel data, reply; + data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32Vector(inputs); + status_t result = remote()->transact(QUERY_MULTIPLE, data, &reply); + if (result != NO_ERROR) { + return result; + } + result = reply.resizeOutVector(outputs); + for (QueryOutput& output : *outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply.read(output); + } + return result; + } + virtual status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) { Parcel data, reply; @@ -576,6 +731,12 @@ public: return mBase->requestBuffer(slot, buf); } + status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs) override { + return mBase->requestBuffers(slots, outputs); + } + status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override { return mBase->setMaxDequeuedBufferCount(maxDequeuedBuffers); } @@ -590,10 +751,21 @@ public: return mBase->dequeueBuffer(slot, fence, w, h, format, usage, outBufferAge, outTimestamps); } + status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs) override { + return mBase->dequeueBuffers(inputs, outputs); + } + status_t detachBuffer(int slot) override { return mBase->detachBuffer(slot); } + status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results) override { + return mBase->detachBuffers(slots, results); + } + status_t detachNextBuffer( sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) override { return mBase->detachNextBuffer(outBuffer, outFence); @@ -604,6 +776,12 @@ public: return mBase->attachBuffer(outSlot, buffer); } + status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs) override { + return mBase->attachBuffers(buffers, outputs); + } + status_t queueBuffer( int slot, const QueueBufferInput& input, @@ -611,14 +789,30 @@ public: return mBase->queueBuffer(slot, input, output); } + status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs) override { + return mBase->queueBuffers(inputs, outputs); + } + status_t cancelBuffer(int slot, const sp<Fence>& fence) override { return mBase->cancelBuffer(slot, fence); } + status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results) override { + return mBase->cancelBuffers(inputs, results); + } + int query(int what, int* value) override { return mBase->query(what, value); } + status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs) override { + return mBase->query(inputs, outputs); + } + status_t connect( const sp<IProducerListener>& listener, int api, bool producerControlledByApp, @@ -789,7 +983,7 @@ status_t BnGraphicBufferProducer::onTransact( switch(code) { case REQUEST_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); - int bufferIdx = data.readInt32(); + int bufferIdx = data.readInt32(); sp<GraphicBuffer> buffer; int result = requestBuffer(bufferIdx, &buffer); reply->writeInt32(buffer != nullptr); @@ -799,6 +993,24 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case REQUEST_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> slots; + std::vector<RequestBufferOutput> outputs; + status_t result = data.readInt32Vector(&slots); + if (result != NO_ERROR) { + return result; + } + (void)requestBuffers(slots, &outputs); + result = reply->writeVectorSize(outputs); + for (const RequestBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case SET_MAX_DEQUEUED_BUFFER_COUNT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int maxDequeuedBuffers = data.readInt32(); @@ -841,6 +1053,30 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case DEQUEUE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<DequeueBufferInput> inputs; + std::vector<DequeueBufferOutput> outputs; + status_t result = data.resizeOutVector(&inputs); + if (result != NO_ERROR) { + return result; + } + for (DequeueBufferInput& input : inputs) { + result = data.read(input); + if (result != NO_ERROR) { + return result; + } + } + (void)dequeueBuffers(inputs, &outputs); + result = reply->writeVectorSize(outputs); + for (const DequeueBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case DETACH_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int slot = data.readInt32(); @@ -848,6 +1084,17 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case DETACH_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> slots; + std::vector<status_t> results; + status_t result = data.readInt32Vector(&slots); + if (result != NO_ERROR) { + return result; + } + (void)detachBuffers(slots, &results); + return reply->writeInt32Vector(results); + } case DETACH_NEXT_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<GraphicBuffer> buffer; @@ -878,6 +1125,31 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case ATTACH_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<sp<GraphicBuffer>> buffers; + status_t result = data.resizeOutVector(&buffers); + if (result != NO_ERROR) { + return result; + } + for (sp<GraphicBuffer>& buffer : buffers) { + buffer = new GraphicBuffer(); + result = data.read(*buffer.get()); + if (result != NO_ERROR) { + return result; + } + } + std::vector<AttachBufferOutput> outputs; + (void)attachBuffers(buffers, &outputs); + result = reply->writeVectorSize(outputs); + for (const AttachBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case QUEUE_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); @@ -890,6 +1162,30 @@ status_t BnGraphicBufferProducer::onTransact( return NO_ERROR; } + case QUEUE_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<QueueBufferInput> inputs; + status_t result = data.resizeOutVector(&inputs); + if (result != NO_ERROR) { + return result; + } + for (QueueBufferInput& input : inputs) { + result = data.read(input); + if (result != NO_ERROR) { + return result; + } + } + std::vector<QueueBufferOutput> outputs; + (void)queueBuffers(inputs, &outputs); + result = reply->writeVectorSize(outputs); + for (const QueueBufferOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case CANCEL_BUFFER: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int buf = data.readInt32(); @@ -901,6 +1197,26 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } + case CANCEL_BUFFERS: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<CancelBufferInput> inputs; + status_t result = data.resizeOutVector(&inputs); + for (CancelBufferInput& input : inputs) { + if (result != NO_ERROR) { + return result; + } + result = data.read(input); + } + if (result != NO_ERROR) { + return result; + } + std::vector<status_t> results; + result = cancelBuffers(inputs, &results); + if (result != NO_ERROR) { + return result; + } + return reply->writeInt32Vector(results); + } case QUERY: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); int value = 0; @@ -910,6 +1226,27 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(res); return NO_ERROR; } + case QUERY_MULTIPLE: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + std::vector<int32_t> inputs; + status_t result = data.readInt32Vector(&inputs); + if (result != NO_ERROR) { + return result; + } + std::vector<QueryOutput> outputs; + result = query(inputs, &outputs); + if (result != NO_ERROR) { + return result; + } + result = reply->writeVectorSize(outputs); + for (const QueryOutput& output : outputs) { + if (result != NO_ERROR) { + return result; + } + result = reply->write(output); + } + return result; + } case CONNECT: { CHECK_INTERFACE(IGraphicBufferProducer, data, reply); sp<IProducerListener> listener; @@ -1083,11 +1420,4 @@ status_t BnGraphicBufferProducer::onTransact( return BBinder::onTransact(code, data, reply, flags); } -// ---------------------------------------------------------------------------- - -IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { - parcel.read(*this); -} - - }; // namespace android diff --git a/libs/gui/IGraphicBufferProducerFlattenables.cpp b/libs/gui/IGraphicBufferProducerFlattenables.cpp new file mode 100644 index 0000000000..c8b9b6751d --- /dev/null +++ b/libs/gui/IGraphicBufferProducerFlattenables.cpp @@ -0,0 +1,413 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> +#include <gui/IGraphicBufferProducer.h> + +namespace android { + +constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { + return sizeof(timestamp) + + sizeof(isAutoTimestamp) + + sizeof(dataSpace) + + sizeof(crop) + + sizeof(scalingMode) + + sizeof(transform) + + sizeof(stickyTransform) + + sizeof(getFrameTimestamps) + + sizeof(slot); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { + return minFlattenedSize() + + fence->getFlattenedSize() + + surfaceDamage.getFlattenedSize() + + hdrMetadata.getFlattenedSize(); +} + +size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { + return fence->getFdCount(); +} + +status_t IGraphicBufferProducer::QueueBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const +{ + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, timestamp); + FlattenableUtils::write(buffer, size, isAutoTimestamp); + FlattenableUtils::write(buffer, size, dataSpace); + FlattenableUtils::write(buffer, size, crop); + FlattenableUtils::write(buffer, size, scalingMode); + FlattenableUtils::write(buffer, size, transform); + FlattenableUtils::write(buffer, size, stickyTransform); + FlattenableUtils::write(buffer, size, getFrameTimestamps); + + status_t result = fence->flatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + result = surfaceDamage.flatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); + result = hdrMetadata.flatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize()); + FlattenableUtils::write(buffer, size, slot); + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueueBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) +{ + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, timestamp); + FlattenableUtils::read(buffer, size, isAutoTimestamp); + FlattenableUtils::read(buffer, size, dataSpace); + FlattenableUtils::read(buffer, size, crop); + FlattenableUtils::read(buffer, size, scalingMode); + FlattenableUtils::read(buffer, size, transform); + FlattenableUtils::read(buffer, size, stickyTransform); + FlattenableUtils::read(buffer, size, getFrameTimestamps); + + fence = new Fence(); + status_t result = fence->unflatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + result = surfaceDamage.unflatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); + result = hdrMetadata.unflatten(buffer, size); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::advance(buffer, size, hdrMetadata.getFlattenedSize()); + FlattenableUtils::read(buffer, size, slot); + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// +constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { + return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + + sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount) + + sizeof(result); +} +size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + frameTimestamps.getFlattenedSize(); +} + +size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { + return frameTimestamps.getFdCount(); +} + +status_t IGraphicBufferProducer::QueueBufferOutput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const +{ + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, width); + FlattenableUtils::write(buffer, size, height); + FlattenableUtils::write(buffer, size, transformHint); + FlattenableUtils::write(buffer, size, numPendingBuffers); + FlattenableUtils::write(buffer, size, nextFrameNumber); + FlattenableUtils::write(buffer, size, bufferReplaced); + FlattenableUtils::write(buffer, size, maxBufferCount); + + status_t result = frameTimestamps.flatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::write(buffer, size, result); + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) +{ + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, width); + FlattenableUtils::read(buffer, size, height); + FlattenableUtils::read(buffer, size, transformHint); + FlattenableUtils::read(buffer, size, numPendingBuffers); + FlattenableUtils::read(buffer, size, nextFrameNumber); + FlattenableUtils::read(buffer, size, bufferReplaced); + FlattenableUtils::read(buffer, size, maxBufferCount); + + status_t result = frameTimestamps.unflatten(buffer, size, fds, count); + if (result != NO_ERROR) { + return result; + } + FlattenableUtils::read(buffer, size, result); + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// +constexpr size_t IGraphicBufferProducer::RequestBufferOutput::minFlattenedSize() { + return sizeof(result) + + sizeof(int32_t); // IsBufferNull +} + +size_t IGraphicBufferProducer::RequestBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + (buffer == nullptr ? 0 : buffer->getFlattenedSize()); +} + +size_t IGraphicBufferProducer::RequestBufferOutput::getFdCount() const { + return (buffer == nullptr ? 0 : buffer->getFdCount()); +} + +status_t IGraphicBufferProducer::RequestBufferOutput::flatten( + void*& fBuffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(fBuffer, size, result); + const int32_t isBufferNull = (buffer == nullptr ? 1 : 0); + FlattenableUtils::write(fBuffer, size, isBufferNull); + + if (!isBufferNull) { + status_t status = buffer->flatten(fBuffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +status_t IGraphicBufferProducer::RequestBufferOutput::unflatten( + void const*& fBuffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(fBuffer, size, result); + int32_t isBufferNull = 0; + FlattenableUtils::read(fBuffer, size, isBufferNull); + buffer = new GraphicBuffer(); + if (!isBufferNull) { + status_t status = buffer->unflatten(fBuffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + } + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::DequeueBufferInput::getFlattenedSize() const { + return sizeof(width) + sizeof(height) + sizeof(format) + sizeof(usage) + + sizeof(int32_t/*getTimestamps*/); +} + +status_t IGraphicBufferProducer::DequeueBufferInput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, width); + FlattenableUtils::write(buffer, size, height); + FlattenableUtils::write(buffer, size, format); + FlattenableUtils::write(buffer, size, usage); + const int32_t getTimestampsInt = (getTimestamps ? 1 : 0); + FlattenableUtils::write(buffer, size, getTimestampsInt); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::DequeueBufferInput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, width); + FlattenableUtils::read(buffer, size, height); + FlattenableUtils::read(buffer, size, format); + FlattenableUtils::read(buffer, size, usage); + int32_t getTimestampsInt = 0; + FlattenableUtils::read(buffer, size, getTimestampsInt); + getTimestamps = (getTimestampsInt == 1); + + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +constexpr size_t IGraphicBufferProducer::DequeueBufferOutput::minFlattenedSize() { + return sizeof(result) + sizeof(slot) + sizeof(bufferAge) + sizeof(int32_t/*hasTimestamps*/); +} + +size_t IGraphicBufferProducer::DequeueBufferOutput::getFlattenedSize() const { + return minFlattenedSize() + + fence->getFlattenedSize() + + (timestamps.has_value() ? timestamps->getFlattenedSize() : 0); +} + +size_t IGraphicBufferProducer::DequeueBufferOutput::getFdCount() const { + return fence->getFdCount() + + (timestamps.has_value() ? timestamps->getFdCount() : 0); +} + +status_t IGraphicBufferProducer::DequeueBufferOutput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, slot); + FlattenableUtils::write(buffer, size, bufferAge); + status_t status = fence->flatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return result; + } + const int32_t hasTimestamps = timestamps.has_value() ? 1 : 0; + FlattenableUtils::write(buffer, size, hasTimestamps); + if (timestamps.has_value()) { + status = timestamps->flatten(buffer, size, fds, count); + } + return status; +} + +status_t IGraphicBufferProducer::DequeueBufferOutput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, slot); + FlattenableUtils::read(buffer, size, bufferAge); + + fence = new Fence(); + status_t status = fence->unflatten(buffer, size, fds, count); + if (status != NO_ERROR) { + return status; + } + int32_t hasTimestamps = 0; + FlattenableUtils::read(buffer, size, hasTimestamps); + if (hasTimestamps) { + timestamps.emplace(); + status = timestamps->unflatten(buffer, size, fds, count); + } + return status; +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::AttachBufferOutput::getFlattenedSize() const { + return sizeof(result) + sizeof(slot); +} + +status_t IGraphicBufferProducer::AttachBufferOutput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, slot); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::AttachBufferOutput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, slot); + + return NO_ERROR; +} + +//////////////////////////////////////////////////////////////////////// + +constexpr size_t IGraphicBufferProducer::CancelBufferInput::minFlattenedSize() { + return sizeof(slot); +} + +size_t IGraphicBufferProducer::CancelBufferInput::getFlattenedSize() const { + return minFlattenedSize() + fence->getFlattenedSize(); +} + +size_t IGraphicBufferProducer::CancelBufferInput::getFdCount() const { + return fence->getFdCount(); +} + +status_t IGraphicBufferProducer::CancelBufferInput::flatten( + void*& buffer, size_t& size, int*& fds, size_t& count) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::write(buffer, size, slot); + return fence->flatten(buffer, size, fds, count); +} + +status_t IGraphicBufferProducer::CancelBufferInput::unflatten( + void const*& buffer, size_t& size, int const*& fds, size_t& count) { + if (size < minFlattenedSize()) { + return NO_MEMORY; + } + + FlattenableUtils::read(buffer, size, slot); + + fence = new Fence(); + return fence->unflatten(buffer, size, fds, count); +} + +//////////////////////////////////////////////////////////////////////// + +size_t IGraphicBufferProducer::QueryOutput::getFlattenedSize() const { + return sizeof(result) + sizeof(value); +} + +status_t IGraphicBufferProducer::QueryOutput::flatten(void* buffer, size_t size) const { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::write(buffer, size, result); + FlattenableUtils::write(buffer, size, value); + + return NO_ERROR; +} + +status_t IGraphicBufferProducer::QueryOutput::unflatten(void const* buffer, size_t size) { + if (size < getFlattenedSize()) { + return NO_MEMORY; + } + FlattenableUtils::read(buffer, size, result); + FlattenableUtils::read(buffer, size, value); + + return NO_ERROR; +} + +} // namespace android diff --git a/libs/gui/IScreenCaptureListener.cpp b/libs/gui/IScreenCaptureListener.cpp deleted file mode 100644 index 0635e9cf34..0000000000 --- a/libs/gui/IScreenCaptureListener.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gui/IScreenCaptureListener.h> -#include <gui/LayerState.h> - -namespace android { - -namespace { // Anonymous - -enum class Tag : uint32_t { - ON_SCREEN_CAPTURE_COMPLETE = IBinder::FIRST_CALL_TRANSACTION, - LAST = ON_SCREEN_CAPTURE_COMPLETE, -}; - -} // Anonymous namespace - -class BpScreenCaptureListener : public SafeBpInterface<IScreenCaptureListener> { -public: - explicit BpScreenCaptureListener(const sp<IBinder>& impl) - : SafeBpInterface<IScreenCaptureListener>(impl, "BpScreenCaptureListener") {} - - ~BpScreenCaptureListener() override; - - status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { - Parcel data, reply; - data.writeInterfaceToken(IScreenCaptureListener::getInterfaceDescriptor()); - - SAFE_PARCEL(captureResults.write, data); - return remote()->transact(static_cast<uint32_t>(Tag::ON_SCREEN_CAPTURE_COMPLETE), data, - &reply, IBinder::FLAG_ONEWAY); - } -}; - -// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see -// clang warning -Wweak-vtables) -BpScreenCaptureListener::~BpScreenCaptureListener() = default; - -IMPLEMENT_META_INTERFACE(ScreenCaptureListener, "android.gui.IScreenCaptureListener"); - -status_t BnScreenCaptureListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags) { - auto tag = static_cast<Tag>(code); - switch (tag) { - case Tag::ON_SCREEN_CAPTURE_COMPLETE: { - CHECK_INTERFACE(IScreenCaptureListener, data, reply); - ScreenCaptureResults captureResults; - SAFE_PARCEL(captureResults.read, data); - return onScreenCaptureComplete(captureResults); - } - default: { - return BBinder::onTransact(code, data, reply, flags); - } - } -} - -} // namespace android
\ No newline at end of file diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 30c9b373ef..634d8b7df0 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -17,6 +17,7 @@ #include <android-base/stringprintf.h> #include <binder/Parcel.h> #include <gui/LayerMetadata.h> +#include <inttypes.h> #include "android/view/LayerMetadataKey.h" @@ -113,6 +114,15 @@ void LayerMetadata::setInt32(uint32_t key, int32_t value) { memcpy(data.data(), p.data(), p.dataSize()); } +std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const { + if (!has(key)) return std::nullopt; + const std::vector<uint8_t>& data = mMap.at(key); + if (data.size() < sizeof(int64_t)) return std::nullopt; + Parcel p; + p.setData(data.data(), data.size()); + return p.readInt64(); +} + std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const { if (!has(key)) return std::string(); switch (static_cast<view::LayerMetadataKey>(key)) { @@ -124,6 +134,8 @@ std::string LayerMetadata::itemToString(uint32_t key, const char* separator) con return StringPrintf("taskId%s%d", separator, getInt32(key, 0)); case view::LayerMetadataKey::METADATA_OWNER_PID: return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0)); + case view::LayerMetadataKey::METADATA_DEQUEUE_TIME: + return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key)); default: return StringPrintf("%d%s%dbytes", key, separator, static_cast<int>(mMap.at(key).size())); diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 7d2c7b88df..63be3edf94 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -714,33 +714,4 @@ status_t LayerCaptureArgs::read(const Parcel& input) { return NO_ERROR; } -status_t ScreenCaptureResults::write(Parcel& output) const { - if (buffer != nullptr) { - SAFE_PARCEL(output.writeBool, true); - SAFE_PARCEL(output.write, *buffer); - } else { - SAFE_PARCEL(output.writeBool, false); - } - SAFE_PARCEL(output.writeBool, capturedSecureLayers); - SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(capturedDataspace)); - SAFE_PARCEL(output.writeInt32, result); - return NO_ERROR; -} - -status_t ScreenCaptureResults::read(const Parcel& input) { - bool hasGraphicBuffer; - SAFE_PARCEL(input.readBool, &hasGraphicBuffer); - if (hasGraphicBuffer) { - buffer = new GraphicBuffer(); - SAFE_PARCEL(input.read, *buffer); - } - - SAFE_PARCEL(input.readBool, &capturedSecureLayers); - uint32_t dataspace = 0; - SAFE_PARCEL(input.readUint32, &dataspace); - capturedDataspace = static_cast<ui::Dataspace>(dataspace); - SAFE_PARCEL(input.readInt32, &result); - return NO_ERROR; -} - }; // namespace android diff --git a/libs/gui/QueueBufferInputOutput.cpp b/libs/gui/QueueBufferInputOutput.cpp deleted file mode 100644 index 30f0ef6785..0000000000 --- a/libs/gui/QueueBufferInputOutput.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <inttypes.h> - -#define LOG_TAG "QueueBufferInputOutput" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS -//#define LOG_NDEBUG 0 - -#include <gui/IGraphicBufferProducer.h> - -namespace android { - -constexpr size_t IGraphicBufferProducer::QueueBufferInput::minFlattenedSize() { - return sizeof(timestamp) + - sizeof(isAutoTimestamp) + - sizeof(dataSpace) + - sizeof(crop) + - sizeof(scalingMode) + - sizeof(transform) + - sizeof(stickyTransform) + - sizeof(getFrameTimestamps); -} - -IGraphicBufferProducer::QueueBufferInput::QueueBufferInput(const Parcel& parcel) { - parcel.read(*this); -} - -size_t IGraphicBufferProducer::QueueBufferInput::getFlattenedSize() const { - return minFlattenedSize() + - fence->getFlattenedSize() + - surfaceDamage.getFlattenedSize() + - hdrMetadata.getFlattenedSize(); -} - -size_t IGraphicBufferProducer::QueueBufferInput::getFdCount() const { - return fence->getFdCount(); -} - -status_t IGraphicBufferProducer::QueueBufferInput::flatten( - void*& buffer, size_t& size, int*& fds, size_t& count) const -{ - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, timestamp); - FlattenableUtils::write(buffer, size, isAutoTimestamp); - FlattenableUtils::write(buffer, size, dataSpace); - FlattenableUtils::write(buffer, size, crop); - FlattenableUtils::write(buffer, size, scalingMode); - FlattenableUtils::write(buffer, size, transform); - FlattenableUtils::write(buffer, size, stickyTransform); - FlattenableUtils::write(buffer, size, getFrameTimestamps); - - status_t result = fence->flatten(buffer, size, fds, count); - if (result != NO_ERROR) { - return result; - } - result = surfaceDamage.flatten(buffer, size); - if (result != NO_ERROR) { - return result; - } - FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); - return hdrMetadata.flatten(buffer, size); -} - -status_t IGraphicBufferProducer::QueueBufferInput::unflatten( - void const*& buffer, size_t& size, int const*& fds, size_t& count) -{ - if (size < minFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, timestamp); - FlattenableUtils::read(buffer, size, isAutoTimestamp); - FlattenableUtils::read(buffer, size, dataSpace); - FlattenableUtils::read(buffer, size, crop); - FlattenableUtils::read(buffer, size, scalingMode); - FlattenableUtils::read(buffer, size, transform); - FlattenableUtils::read(buffer, size, stickyTransform); - FlattenableUtils::read(buffer, size, getFrameTimestamps); - - fence = new Fence(); - status_t result = fence->unflatten(buffer, size, fds, count); - if (result != NO_ERROR) { - return result; - } - result = surfaceDamage.unflatten(buffer, size); - if (result != NO_ERROR) { - return result; - } - FlattenableUtils::advance(buffer, size, surfaceDamage.getFlattenedSize()); - return hdrMetadata.unflatten(buffer, size); -} - -//////////////////////////////////////////////////////////////////////// -constexpr size_t IGraphicBufferProducer::QueueBufferOutput::minFlattenedSize() { - return sizeof(width) + sizeof(height) + sizeof(transformHint) + sizeof(numPendingBuffers) + - sizeof(nextFrameNumber) + sizeof(bufferReplaced) + sizeof(maxBufferCount); -} -size_t IGraphicBufferProducer::QueueBufferOutput::getFlattenedSize() const { - return minFlattenedSize() + frameTimestamps.getFlattenedSize(); -} - -size_t IGraphicBufferProducer::QueueBufferOutput::getFdCount() const { - return frameTimestamps.getFdCount(); -} - -status_t IGraphicBufferProducer::QueueBufferOutput::flatten( - void*& buffer, size_t& size, int*& fds, size_t& count) const -{ - if (size < getFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::write(buffer, size, width); - FlattenableUtils::write(buffer, size, height); - FlattenableUtils::write(buffer, size, transformHint); - FlattenableUtils::write(buffer, size, numPendingBuffers); - FlattenableUtils::write(buffer, size, nextFrameNumber); - FlattenableUtils::write(buffer, size, bufferReplaced); - FlattenableUtils::write(buffer, size, maxBufferCount); - - return frameTimestamps.flatten(buffer, size, fds, count); -} - -status_t IGraphicBufferProducer::QueueBufferOutput::unflatten( - void const*& buffer, size_t& size, int const*& fds, size_t& count) -{ - if (size < minFlattenedSize()) { - return NO_MEMORY; - } - - FlattenableUtils::read(buffer, size, width); - FlattenableUtils::read(buffer, size, height); - FlattenableUtils::read(buffer, size, transformHint); - FlattenableUtils::read(buffer, size, numPendingBuffers); - FlattenableUtils::read(buffer, size, nextFrameNumber); - FlattenableUtils::read(buffer, size, bufferReplaced); - FlattenableUtils::read(buffer, size, maxBufferCount); - - return frameTimestamps.unflatten(buffer, size, fds, count); -} - -} // namespace android diff --git a/libs/gui/ScreenCaptureResults.cpp b/libs/gui/ScreenCaptureResults.cpp new file mode 100644 index 0000000000..2b29487268 --- /dev/null +++ b/libs/gui/ScreenCaptureResults.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/ScreenCaptureResults.h> + +namespace android::gui { + +status_t ScreenCaptureResults::writeToParcel(android::Parcel* parcel) const { + if (buffer != nullptr) { + SAFE_PARCEL(parcel->writeBool, true); + SAFE_PARCEL(parcel->write, *buffer); + } else { + SAFE_PARCEL(parcel->writeBool, false); + } + SAFE_PARCEL(parcel->writeBool, capturedSecureLayers); + SAFE_PARCEL(parcel->writeUint32, static_cast<uint32_t>(capturedDataspace)); + SAFE_PARCEL(parcel->writeInt32, result); + return NO_ERROR; +} + +status_t ScreenCaptureResults::readFromParcel(const android::Parcel* parcel) { + bool hasGraphicBuffer; + SAFE_PARCEL(parcel->readBool, &hasGraphicBuffer); + if (hasGraphicBuffer) { + buffer = new GraphicBuffer(); + SAFE_PARCEL(parcel->read, *buffer); + } + + SAFE_PARCEL(parcel->readBool, &capturedSecureLayers); + uint32_t dataspace = 0; + SAFE_PARCEL(parcel->readUint32, &dataspace); + capturedDataspace = static_cast<ui::Dataspace>(dataspace); + SAFE_PARCEL(parcel->readInt32, &result); + return NO_ERROR; +} + +} // namespace android::gui diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 94390aa86c..e82f0cc9e9 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -618,29 +618,31 @@ private: std::mutex mMutex; }; +void Surface::getDequeueBufferInputLocked( + IGraphicBufferProducer::DequeueBufferInput* dequeueInput) { + LOG_ALWAYS_FATAL_IF(dequeueInput == nullptr, "input is null"); + + dequeueInput->width = mReqWidth ? mReqWidth : mUserWidth; + dequeueInput->height = mReqHeight ? mReqHeight : mUserHeight; + + dequeueInput->format = mReqFormat; + dequeueInput->usage = mReqUsage; + + dequeueInput->getTimestamps = mEnableFrameTimestamps; +} + int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { ATRACE_CALL(); ALOGV("Surface::dequeueBuffer"); - uint32_t reqWidth; - uint32_t reqHeight; - PixelFormat reqFormat; - uint64_t reqUsage; - bool enableFrameTimestamps; - + IGraphicBufferProducer::DequeueBufferInput dqInput; { Mutex::Autolock lock(mMutex); if (mReportRemovedBuffers) { mRemovedBuffers.clear(); } - reqWidth = mReqWidth ? mReqWidth : mUserWidth; - reqHeight = mReqHeight ? mReqHeight : mUserHeight; - - reqFormat = mReqFormat; - reqUsage = mReqUsage; - - enableFrameTimestamps = mEnableFrameTimestamps; + getDequeueBufferInputLocked(&dqInput); if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot != BufferItem::INVALID_BUFFER_SLOT) { @@ -658,16 +660,17 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { nsecs_t startTime = systemTime(); FrameEventHistoryDelta frameTimestamps; - status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight, - reqFormat, reqUsage, &mBufferAge, - enableFrameTimestamps ? &frameTimestamps - : nullptr); + status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width, + dqInput.height, dqInput.format, + dqInput.usage, &mBufferAge, + dqInput.getTimestamps ? + &frameTimestamps : nullptr); mLastDequeueDuration = systemTime() - startTime; if (result < 0) { ALOGV("dequeueBuffer: IGraphicBufferProducer::dequeueBuffer" "(%d, %d, %d, %#" PRIx64 ") failed: %d", - reqWidth, reqHeight, reqFormat, reqUsage, result); + dqInput.width, dqInput.height, dqInput.format, dqInput.usage, result); return result; } @@ -696,7 +699,7 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { freeAllBuffers(); } - if (enableFrameTimestamps) { + if (dqInput.getTimestamps) { mFrameEventHistory->applyDelta(frameTimestamps); } @@ -739,6 +742,176 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { return OK; } +int Surface::dequeueBuffers(std::vector<BatchBuffer>* buffers) { + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + + ATRACE_CALL(); + ALOGV("Surface::dequeueBuffers"); + + if (buffers->size() == 0) { + ALOGE("%s: must dequeue at least 1 buffer!", __FUNCTION__); + return BAD_VALUE; + } + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBufferRequested = buffers->size(); + DequeueBufferInput input; + + { + Mutex::Autolock lock(mMutex); + if (mReportRemovedBuffers) { + mRemovedBuffers.clear(); + } + + getDequeueBufferInputLocked(&input); + } // Drop the lock so that we can still touch the Surface while blocking in IGBP::dequeueBuffers + + std::vector<DequeueBufferInput> dequeueInput(numBufferRequested, input); + std::vector<DequeueBufferOutput> dequeueOutput; + + nsecs_t startTime = systemTime(); + + status_t result = mGraphicBufferProducer->dequeueBuffers(dequeueInput, &dequeueOutput); + + mLastDequeueDuration = systemTime() - startTime; + + if (result < 0) { + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, result); + return result; + } + + std::vector<CancelBufferInput> cancelBufferInputs(numBufferRequested); + std::vector<status_t> cancelBufferOutputs; + for (size_t i = 0; i < numBufferRequested; i++) { + cancelBufferInputs[i].slot = dequeueOutput[i].slot; + cancelBufferInputs[i].fence = dequeueOutput[i].fence; + } + + for (const auto& output : dequeueOutput) { + if (output.result < 0) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGV("%s: IGraphicBufferProducer::dequeueBuffers" + "(%d, %d, %d, %#" PRIx64 ") failed: %d", + __FUNCTION__, input.width, input.height, input.format, input.usage, + output.result); + return output.result; + } + + if (output.slot < 0 || output.slot >= NUM_BUFFER_SLOTS) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: IGraphicBufferProducer returned invalid slot number %d", + __FUNCTION__, output.slot); + android_errorWriteLog(0x534e4554, "36991414"); // SafetyNet logging + return FAILED_TRANSACTION; + } + + if (input.getTimestamps && !output.timestamps.has_value()) { + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + ALOGE("%s: no frame timestamp returns!", __FUNCTION__); + return FAILED_TRANSACTION; + } + + // this should never happen + ALOGE_IF(output.fence == nullptr, + "%s: received null Fence! slot=%d", __FUNCTION__, output.slot); + } + + Mutex::Autolock lock(mMutex); + + // Write this while holding the mutex + mLastDequeueStartTime = startTime; + + std::vector<int32_t> requestBufferSlots; + requestBufferSlots.reserve(numBufferRequested); + // handle release all buffers and request buffers + for (const auto& output : dequeueOutput) { + if (output.result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) { + ALOGV("%s: RELEASE_ALL_BUFFERS during batch operation", __FUNCTION__); + freeAllBuffers(); + break; + } + } + + for (const auto& output : dequeueOutput) { + // Collect slots that needs requesting buffer + sp<GraphicBuffer>& gbuf(mSlots[output.slot].buffer); + if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) { + if (mReportRemovedBuffers && (gbuf != nullptr)) { + mRemovedBuffers.push_back(gbuf); + } + requestBufferSlots.push_back(output.slot); + } + } + + // Batch request Buffer + std::vector<RequestBufferOutput> reqBufferOutput; + if (requestBufferSlots.size() > 0) { + result = mGraphicBufferProducer->requestBuffers(requestBufferSlots, &reqBufferOutput); + if (result != NO_ERROR) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed: %d", + __FUNCTION__, result); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return result; + } + + // Check if we have any single failure + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + if (reqBufferOutput[i].result != OK) { + ALOGE("%s: IGraphicBufferProducer::requestBuffers failed at %zu-th buffer, slot %d", + __FUNCTION__, i, requestBufferSlots[i]); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + return reqBufferOutput[i].result; + } + } + + // Fill request buffer results to mSlots + for (size_t i = 0; i < requestBufferSlots.size(); i++) { + mSlots[requestBufferSlots[i]].buffer = reqBufferOutput[i].buffer; + } + } + + for (size_t batchIdx = 0; batchIdx < numBufferRequested; batchIdx++) { + const auto& output = dequeueOutput[batchIdx]; + int slot = output.slot; + sp<GraphicBuffer>& gbuf(mSlots[slot].buffer); + + if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) { + static FenceMonitor hwcReleaseThread("HWC release"); + hwcReleaseThread.queueFence(output.fence); + } + + if (input.getTimestamps) { + mFrameEventHistory->applyDelta(output.timestamps.value()); + } + + if (output.fence->isValid()) { + buffers->at(batchIdx).fenceFd = output.fence->dup(); + if (buffers->at(batchIdx).fenceFd == -1) { + ALOGE("%s: error duping fence: %d", __FUNCTION__, errno); + // dup() should never fail; something is badly wrong. Soldier on + // and hope for the best; the worst that should happen is some + // visible corruption that lasts until the next frame. + } + } else { + buffers->at(batchIdx).fenceFd = -1; + } + + buffers->at(batchIdx).buffer = gbuf.get(); + mDequeuedSlots.insert(slot); + } + return OK; +} + int Surface::cancelBuffer(android_native_buffer_t* buffer, int fenceFd) { ATRACE_CALL(); @@ -769,15 +942,65 @@ int Surface::cancelBuffer(android_native_buffer_t* buffer, return OK; } +int Surface::cancelBuffers(const std::vector<BatchBuffer>& buffers) { + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + ATRACE_CALL(); + ALOGV("Surface::cancelBuffers"); + + if (mSharedBufferMode) { + ALOGE("%s: batch operation is not supported in shared buffer mode!", + __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<CancelBufferInput> cancelBufferInputs(numBuffers); + std::vector<status_t> cancelBufferOutputs; + size_t numBuffersCancelled = 0; + int badSlotResult = 0; + for (size_t i = 0; i < numBuffers; i++) { + int slot = getSlotFromBufferLocked(buffers[i].buffer); + int fenceFd = buffers[i].fenceFd; + if (slot < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + ALOGE("%s: cannot find slot number for cancelled buffer", __FUNCTION__); + badSlotResult = slot; + } else { + sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); + cancelBufferInputs[numBuffersCancelled].slot = slot; + cancelBufferInputs[numBuffersCancelled++].fence = fence; + } + } + cancelBufferInputs.resize(numBuffersCancelled); + mGraphicBufferProducer->cancelBuffers(cancelBufferInputs, &cancelBufferOutputs); + + + for (size_t i = 0; i < numBuffersCancelled; i++) { + mDequeuedSlots.erase(cancelBufferInputs[i].slot); + } + + if (badSlotResult != 0) { + return badSlotResult; + } + return OK; +} + int Surface::getSlotFromBufferLocked( android_native_buffer_t* buffer) const { + if (buffer == nullptr) { + ALOGE("%s: input buffer is null!", __FUNCTION__); + return BAD_VALUE; + } + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (mSlots[i].buffer != nullptr && mSlots[i].buffer->handle == buffer->handle) { return i; } } - ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle); + ALOGE("%s: unknown buffer: %p", __FUNCTION__, buffer->handle); return BAD_VALUE; } @@ -787,42 +1010,22 @@ int Surface::lockBuffer_DEPRECATED(android_native_buffer_t* buffer __attribute__ return OK; } -int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { - ATRACE_CALL(); - ALOGV("Surface::queueBuffer"); - Mutex::Autolock lock(mMutex); - int64_t timestamp; +void Surface::getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, + nsecs_t timestamp, IGraphicBufferProducer::QueueBufferInput* out) { bool isAutoTimestamp = false; - if (mTimestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { + if (timestamp == NATIVE_WINDOW_TIMESTAMP_AUTO) { timestamp = systemTime(SYSTEM_TIME_MONOTONIC); isAutoTimestamp = true; ALOGV("Surface::queueBuffer making up timestamp: %.2f ms", timestamp / 1000000.0); - } else { - timestamp = mTimestamp; - } - int i = getSlotFromBufferLocked(buffer); - if (i < 0) { - if (fenceFd >= 0) { - close(fenceFd); - } - return i; } - if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { - if (fenceFd >= 0) { - close(fenceFd); - } - return OK; - } - // Make sure the crop rectangle is entirely inside the buffer. Rect crop(Rect::EMPTY_RECT); mCrop.intersect(Rect(buffer->width, buffer->height), &crop); sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE); - IGraphicBufferProducer::QueueBufferOutput output; IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp, static_cast<android_dataspace>(mDataSpace), crop, mScalingMode, mTransform ^ mStickyTransform, fence, mStickyTransform, @@ -893,15 +1096,12 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { input.setSurfaceDamage(flippedRegion); } + *out = input; +} - nsecs_t now = systemTime(); - status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); - mLastQueueDuration = systemTime() - now; - if (err != OK) { - ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err); - } - - mDequeuedSlots.erase(i); +void Surface::onBufferQueuedLocked(int slot, sp<Fence> fence, + const IGraphicBufferProducer::QueueBufferOutput& output) { + mDequeuedSlots.erase(slot); if (mEnableFrameTimestamps) { mFrameEventHistory->applyDelta(output.frameTimestamps); @@ -935,7 +1135,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { mDirtyRegion = Region::INVALID_REGION; } - if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) { + if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == slot) { mSharedBufferHasBeenQueued = true; } @@ -945,6 +1145,89 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { static FenceMonitor gpuCompletionThread("GPU completion"); gpuCompletionThread.queueFence(fence); } +} + +int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffer"); + Mutex::Autolock lock(mMutex); + + int i = getSlotFromBufferLocked(buffer); + if (i < 0) { + if (fenceFd >= 0) { + close(fenceFd); + } + return i; + } + if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) { + if (fenceFd >= 0) { + close(fenceFd); + } + return OK; + } + + IGraphicBufferProducer::QueueBufferOutput output; + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked(buffer, fenceFd, mTimestamp, &input); + sp<Fence> fence = input.fence; + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("queueBuffer: error queuing buffer, %d", err); + } + + onBufferQueuedLocked(i, fence, output); + return err; +} + +int Surface::queueBuffers(const std::vector<BatchQueuedBuffer>& buffers) { + ATRACE_CALL(); + ALOGV("Surface::queueBuffers"); + Mutex::Autolock lock(mMutex); + + if (mSharedBufferMode) { + ALOGE("%s: batched operation is not supported in shared buffer mode", __FUNCTION__); + return INVALID_OPERATION; + } + + size_t numBuffers = buffers.size(); + std::vector<IGraphicBufferProducer::QueueBufferInput> queueBufferInputs(numBuffers); + std::vector<IGraphicBufferProducer::QueueBufferOutput> queueBufferOutputs; + std::vector<int> bufferSlots(numBuffers, -1); + std::vector<sp<Fence>> bufferFences(numBuffers); + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + int i = getSlotFromBufferLocked(buffers[batchIdx].buffer); + if (i < 0) { + if (buffers[batchIdx].fenceFd >= 0) { + close(buffers[batchIdx].fenceFd); + } + return i; + } + bufferSlots[batchIdx] = i; + + IGraphicBufferProducer::QueueBufferInput input; + getQueueBufferInputLocked( + buffers[batchIdx].buffer, buffers[batchIdx].fenceFd, buffers[batchIdx].timestamp, + &input); + bufferFences[batchIdx] = input.fence; + queueBufferInputs[batchIdx] = input; + } + + nsecs_t now = systemTime(); + status_t err = mGraphicBufferProducer->queueBuffers(queueBufferInputs, &queueBufferOutputs); + mLastQueueDuration = systemTime() - now; + if (err != OK) { + ALOGE("%s: error queuing buffer, %d", __FUNCTION__, err); + } + + + for (size_t batchIdx = 0; batchIdx < numBuffers; batchIdx++) { + onBufferQueuedLocked(bufferSlots[batchIdx], bufferFences[batchIdx], + queueBufferOutputs[batchIdx]); + } return err; } @@ -985,6 +1268,10 @@ int Surface::query(int what, int* value) const { } break; case NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER: { + status_t err = mGraphicBufferProducer->query(what, value); + if (err == NO_ERROR) { + return NO_ERROR; + } if (composerService()->authenticateSurfaceTexture( mGraphicBufferProducer)) { *value = 1; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 5570d99d61..97c2693f29 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -396,7 +396,8 @@ SurfaceComposerClient::Transaction::Transaction(const Transaction& other) mContainsBuffer(other.mContainsBuffer), mDesiredPresentTime(other.mDesiredPresentTime), mIsAutoTimestamp(other.mIsAutoTimestamp), - mFrameTimelineVsyncId(other.mFrameTimelineVsyncId) { + mFrameTimelineVsyncId(other.mFrameTimelineVsyncId), + mApplyToken(other.mApplyToken) { mDisplayStates = other.mDisplayStates; mComposerStates = other.mComposerStates; mInputWindowCommands = other.mInputWindowCommands; @@ -427,7 +428,8 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel const int64_t desiredPresentTime = parcel->readInt64(); const bool isAutoTimestamp = parcel->readBool(); const int64_t frameTimelineVsyncId = parcel->readInt64(); - + sp<IBinder> applyToken; + parcel->readNullableStrongBinder(&applyToken); size_t count = static_cast<size_t>(parcel->readUint32()); if (count > parcel->dataSize()) { return BAD_VALUE; @@ -505,6 +507,7 @@ status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel mListenerCallbacks = listenerCallbacks; mComposerStates = composerStates; mInputWindowCommands = inputWindowCommands; + mApplyToken = applyToken; return NO_ERROR; } @@ -532,6 +535,7 @@ status_t SurfaceComposerClient::Transaction::writeToParcel(Parcel* parcel) const parcel->writeInt64(mDesiredPresentTime); parcel->writeBool(mIsAutoTimestamp); parcel->writeInt64(mFrameTimelineVsyncId); + parcel->writeStrongBinder(mApplyToken); parcel->writeUint32(static_cast<uint32_t>(mDisplayStates.size())); for (auto const& displayState : mDisplayStates) { displayState.write(*parcel); @@ -607,6 +611,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::merge(Tr mEarlyWakeup = mEarlyWakeup || other.mEarlyWakeup; mExplicitEarlyWakeupStart = mExplicitEarlyWakeupStart || other.mExplicitEarlyWakeupStart; mExplicitEarlyWakeupEnd = mExplicitEarlyWakeupEnd || other.mExplicitEarlyWakeupEnd; + mApplyToken = other.mApplyToken; // When merging vsync Ids we take the oldest one if (mFrameTimelineVsyncId != ISurfaceComposer::INVALID_VSYNC_ID && @@ -635,6 +640,7 @@ void SurfaceComposerClient::Transaction::clear() { mDesiredPresentTime = 0; mIsAutoTimestamp = true; mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; + mApplyToken = nullptr; } void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { @@ -763,7 +769,10 @@ status_t SurfaceComposerClient::Transaction::apply(bool synchronous) { flags |= ISurfaceComposer::eExplicitEarlyWakeupEnd; } - sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sp<IBinder> applyToken = mApplyToken + ? mApplyToken + : IInterface::asBinder(TransactionCompletedListener::getIInstance()); + sf->setTransactionState(mFrameTimelineVsyncId, composerStates, displayStates, flags, applyToken, mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp, {} /*uncacheBuffer - only set in doUncacheBufferTransaction*/, @@ -1579,6 +1588,12 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAutoR return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setApplyToken( + const sp<IBinder>& applyToken) { + mApplyToken = applyToken; + return *this; +} + // --------------------------------------------------------------------------- DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) { diff --git a/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl new file mode 100644 index 0000000000..f490118b8f --- /dev/null +++ b/libs/gui/aidl/android/gui/IScreenCaptureListener.aidl @@ -0,0 +1,24 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +import android.gui.ScreenCaptureResults; + +/** @hide */ +oneway interface IScreenCaptureListener { + void onScreenCaptureComplete(in ScreenCaptureResults captureResults); +}
\ No newline at end of file diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl new file mode 100644 index 0000000000..9908edd2ef --- /dev/null +++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.gui; + +parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index c4cdb65537..0fbcbdc50f 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -78,6 +78,8 @@ public: void onBufferFreed(const wp<GraphicBuffer>&/* graphicBuffer*/) override { /* TODO */ } void onFrameReplaced(const BufferItem& item) override; void onFrameAvailable(const BufferItem& item) override; + void onFrameDequeued(const uint64_t) override; + void onFrameCancelled(const uint64_t) override; void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence, const std::vector<SurfaceControlStats>& stats); @@ -165,6 +167,18 @@ private: std::function<void(int64_t)> mTransactionCompleteCallback GUARDED_BY(mMutex) = nullptr; uint64_t mTransactionCompleteFrameNumber GUARDED_BY(mMutex){0}; + + // Queues up transactions using this token in SurfaceFlinger. This prevents queued up + // transactions from other parts of the client from blocking this transaction. + const sp<IBinder> mApplyToken GUARDED_BY(mMutex) = new BBinder(); + + // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or + // we will deadlock. + std::mutex mTimestampMutex; + // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses + // it for debugging purposes. + std::unordered_map<uint64_t /* bufferId */, nsecs_t> mDequeueTimestamps + GUARDED_BY(mTimestampMutex); }; } // namespace android diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 45e0a134ba..c3b92622b6 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -38,6 +38,9 @@ #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h> #include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> +#include <optional> +#include <vector> + namespace android { // ---------------------------------------------------------------------------- @@ -289,8 +292,9 @@ public: const sp<GraphicBuffer>& buffer) = 0; struct QueueBufferInput : public Flattenable<QueueBufferInput> { - friend class Flattenable<QueueBufferInput>; - explicit inline QueueBufferInput(const Parcel& parcel); + explicit inline QueueBufferInput(const Parcel& parcel) { + parcel.read(*this); + } // timestamp - a monotonically increasing value in nanoseconds // isAutoTimestamp - if the timestamp was synthesized at queue time @@ -304,21 +308,29 @@ public: // camera mode). // getFrameTimestamps - whether or not the latest frame timestamps // should be retrieved from the consumer. + // slot - the slot index to queue. This is used only by queueBuffers(). + // queueBuffer() ignores this value and uses the argument `slot` + // instead. inline QueueBufferInput(int64_t _timestamp, bool _isAutoTimestamp, android_dataspace _dataSpace, const Rect& _crop, int _scalingMode, uint32_t _transform, const sp<Fence>& _fence, - uint32_t _sticky = 0, bool _getFrameTimestamps = false) + uint32_t _sticky = 0, bool _getFrameTimestamps = false, + int _slot = -1) : timestamp(_timestamp), isAutoTimestamp(_isAutoTimestamp), dataSpace(_dataSpace), crop(_crop), scalingMode(_scalingMode), - transform(_transform), stickyTransform(_sticky), fence(_fence), - surfaceDamage(), getFrameTimestamps(_getFrameTimestamps) { } + transform(_transform), stickyTransform(_sticky), + fence(_fence), surfaceDamage(), + getFrameTimestamps(_getFrameTimestamps), slot(_slot) { } + + QueueBufferInput() = default; inline void deflate(int64_t* outTimestamp, bool* outIsAutoTimestamp, android_dataspace* outDataSpace, Rect* outCrop, int* outScalingMode, uint32_t* outTransform, sp<Fence>* outFence, uint32_t* outStickyTransform = nullptr, - bool* outGetFrameTimestamps = nullptr) const { + bool* outGetFrameTimestamps = nullptr, + int* outSlot = nullptr) const { *outTimestamp = timestamp; *outIsAutoTimestamp = bool(isAutoTimestamp); *outDataSpace = dataSpace; @@ -332,6 +344,9 @@ public: if (outGetFrameTimestamps) { *outGetFrameTimestamps = getFrameTimestamps; } + if (outSlot) { + *outSlot = slot; + } } // Flattenable protocol @@ -357,6 +372,7 @@ public: sp<Fence> fence; Region surfaceDamage; bool getFrameTimestamps{false}; + int slot{-1}; HdrMetadata hdrMetadata; }; @@ -385,6 +401,7 @@ public: FrameEventHistoryDelta frameTimestamps; bool bufferReplaced{false}; int maxBufferCount{0}; + status_t result{NO_ERROR}; }; // queueBuffer indicates that the client has finished filling in the @@ -404,6 +421,10 @@ public: // Upon success, the output will be filled with meaningful values // (refer to the documentation below). // + // Note: QueueBufferInput::slot was added to QueueBufferInput to be used by + // queueBuffers(), the batched version of queueBuffer(). The non-batched + // method (queueBuffer()) uses `slot` and ignores `input.slot`. + // // Return of a value other than NO_ERROR means an error has occurred: // * NO_INIT - the buffer queue has been abandoned or the producer is not // connected. @@ -639,6 +660,147 @@ public: // the width and height used for dequeueBuffer will be additionally swapped. virtual status_t setAutoPrerotation(bool autoPrerotation); + struct RequestBufferOutput : public Flattenable<RequestBufferOutput> { + RequestBufferOutput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + status_t result; + sp<GraphicBuffer> buffer; + }; + + // Batched version of requestBuffer(). + // This method behaves like a sequence of requestBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t requestBuffers( + const std::vector<int32_t>& slots, + std::vector<RequestBufferOutput>* outputs); + + struct DequeueBufferInput : public LightFlattenable<DequeueBufferInput> { + DequeueBufferInput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + uint32_t width; + uint32_t height; + PixelFormat format; + uint64_t usage; + bool getTimestamps; + }; + + struct DequeueBufferOutput : public Flattenable<DequeueBufferOutput> { + DequeueBufferOutput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + status_t result; + int slot = -1; + sp<Fence> fence = Fence::NO_FENCE; + uint64_t bufferAge; + std::optional<FrameEventHistoryDelta> timestamps; + }; + + // Batched version of dequeueBuffer(). + // This method behaves like a sequence of dequeueBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t dequeueBuffers( + const std::vector<DequeueBufferInput>& inputs, + std::vector<DequeueBufferOutput>* outputs); + + // Batched version of detachBuffer(). + // This method behaves like a sequence of detachBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t detachBuffers(const std::vector<int32_t>& slots, + std::vector<status_t>* results); + + + struct AttachBufferOutput : public LightFlattenable<AttachBufferOutput> { + AttachBufferOutput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + status_t result; + int slot; + }; + // Batched version of attachBuffer(). + // This method behaves like a sequence of attachBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t attachBuffers( + const std::vector<sp<GraphicBuffer>>& buffers, + std::vector<AttachBufferOutput>* outputs); + + // Batched version of queueBuffer(). + // This method behaves like a sequence of queueBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + // + // Note: QueueBufferInput::slot was added to QueueBufferInput to include the + // `slot` input argument of the non-batched method queueBuffer(). + virtual status_t queueBuffers(const std::vector<QueueBufferInput>& inputs, + std::vector<QueueBufferOutput>* outputs); + + struct CancelBufferInput : public Flattenable<CancelBufferInput> { + CancelBufferInput() = default; + + // Flattenable protocol + static constexpr size_t minFlattenedSize(); + size_t getFlattenedSize() const; + size_t getFdCount() const; + status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const; + status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count); + + int slot; + sp<Fence> fence; + }; + // Batched version of cancelBuffer(). + // This method behaves like a sequence of cancelBuffer() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t cancelBuffers( + const std::vector<CancelBufferInput>& inputs, + std::vector<status_t>* results); + + struct QueryOutput : public LightFlattenable<QueryOutput> { + QueryOutput() = default; + + // LightFlattenable protocol + inline bool isFixedSize() const { return true; } + size_t getFlattenedSize() const; + status_t flatten(void* buffer, size_t size) const; + status_t unflatten(void const* buffer, size_t size); + + status_t result; + int64_t value; + }; + // Batched version of query(). + // This method behaves like a sequence of query() calls. + // The return value of the batched method will only be about the + // transaction. For a local call, the return value will always be NO_ERROR. + virtual status_t query(const std::vector<int32_t> inputs, + std::vector<QueryOutput>* outputs); + #ifndef NO_BINDER // Static method exports any IGraphicBufferProducer object to a parcel. It // handles null producer as well. diff --git a/libs/gui/include/gui/IScreenCaptureListener.h b/libs/gui/include/gui/IScreenCaptureListener.h deleted file mode 100644 index a2ddc9ffff..0000000000 --- a/libs/gui/include/gui/IScreenCaptureListener.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <binder/Binder.h> -#include <binder/IInterface.h> -#include <binder/Parcel.h> -#include <binder/SafeInterface.h> - -namespace android { - -struct ScreenCaptureResults; - -// TODO(b/166271443): Convert to AIDL -class IScreenCaptureListener : public IInterface { -public: - DECLARE_META_INTERFACE(ScreenCaptureListener) - - virtual status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) = 0; -}; - -class BnScreenCaptureListener : public SafeBnInterface<IScreenCaptureListener> { -public: - BnScreenCaptureListener() - : SafeBnInterface<IScreenCaptureListener>("BnScreenCaptureListener") {} - - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, - uint32_t flags = 0) override; -}; - -} // namespace android diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 40316dbeea..86f3c605ab 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -22,8 +22,8 @@ #include <binder/IBinder.h> #include <binder/IInterface.h> +#include <android/gui/IScreenCaptureListener.h> #include <android/gui/ITransactionTraceListener.h> -#include <gui/IScreenCaptureListener.h> #include <gui/ITransactionCompletedListener.h> #include <input/Flags.h> @@ -59,7 +59,6 @@ struct DisplayStatInfo; struct DisplayState; struct InputWindowCommands; struct LayerCaptureArgs; -struct ScreenCaptureResults; class LayerDebugInfo; class HdrCapabilities; class IDisplayEventConnection; @@ -69,6 +68,8 @@ class IRegionSamplingListener; class Rect; enum class FrameEvent; +using gui::IScreenCaptureListener; + namespace ui { struct DisplayState; diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index ac48aefae6..41982c28a2 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -29,6 +29,7 @@ enum { METADATA_MOUSE_CURSOR = 4, METADATA_ACCESSIBILITY_ID = 5, METADATA_OWNER_PID = 6, + METADATA_DEQUEUE_TIME = 7 }; struct LayerMetadata : public Parcelable { @@ -51,6 +52,8 @@ struct LayerMetadata : public Parcelable { bool has(uint32_t key) const; int32_t getInt32(uint32_t key, int32_t fallback) const; void setInt32(uint32_t key, int32_t value); + std::optional<int64_t> getInt64(uint32_t key) const; + void setInt64(uint32_t key, int64_t value); std::string itemToString(uint32_t key, const char* separator) const; }; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index f1c5d6712d..4a291aeb02 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -362,16 +362,6 @@ struct LayerCaptureArgs : CaptureArgs { status_t read(const Parcel& input) override; }; -struct ScreenCaptureResults { - sp<GraphicBuffer> buffer; - bool capturedSecureLayers{false}; - ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; - status_t result = OK; - - status_t write(Parcel& output) const; - status_t read(const Parcel& input); -}; - }; // namespace android #endif // ANDROID_SF_LAYER_STATE_H diff --git a/libs/gui/include/gui/ScreenCaptureResults.h b/libs/gui/include/gui/ScreenCaptureResults.h new file mode 100644 index 0000000000..fdb4b6985e --- /dev/null +++ b/libs/gui/include/gui/ScreenCaptureResults.h @@ -0,0 +1,47 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define SAFE_PARCEL(FUNC, ...) \ + { \ + status_t error = FUNC(__VA_ARGS__); \ + if (error) { \ + ALOGE("ERROR(%d). Failed to call parcel %s(%s)", error, #FUNC, #__VA_ARGS__); \ + return error; \ + } \ + } + +#include <binder/Parcel.h> +#include <binder/Parcelable.h> +#include <ui/GraphicBuffer.h> + +namespace android::gui { + +struct ScreenCaptureResults : public Parcelable { +public: + ScreenCaptureResults() = default; + virtual ~ScreenCaptureResults() = default; + status_t writeToParcel(android::Parcel* parcel) const override; + status_t readFromParcel(const android::Parcel* parcel) override; + + sp<GraphicBuffer> buffer; + bool capturedSecureLayers{false}; + ui::Dataspace capturedDataspace{ui::Dataspace::V0_SRGB}; + status_t result = OK; +}; + +} // namespace android::gui diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 82bc5c9efb..43b5dcd60d 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -344,6 +344,23 @@ public: static status_t attachAndQueueBufferWithDataspace(Surface* surface, sp<GraphicBuffer> buffer, ui::Dataspace dataspace); + // Batch version of dequeueBuffer, cancelBuffer and queueBuffer + // Note that these batched operations are not supported when shared buffer mode is being used. + struct BatchBuffer { + ANativeWindowBuffer* buffer = nullptr; + int fenceFd = -1; + }; + virtual int dequeueBuffers(std::vector<BatchBuffer>* buffers); + virtual int cancelBuffers(const std::vector<BatchBuffer>& buffers); + + struct BatchQueuedBuffer { + ANativeWindowBuffer* buffer = nullptr; + int fenceFd = -1; + nsecs_t timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + }; + virtual int queueBuffers( + const std::vector<BatchQueuedBuffer>& buffers); + protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; @@ -373,6 +390,14 @@ protected: void freeAllBuffers(); int getSlotFromBufferLocked(android_native_buffer_t* buffer) const; + void getDequeueBufferInputLocked(IGraphicBufferProducer::DequeueBufferInput* dequeueInput); + + void getQueueBufferInputLocked(android_native_buffer_t* buffer, int fenceFd, nsecs_t timestamp, + IGraphicBufferProducer::QueueBufferInput* out); + + void onBufferQueuedLocked(int slot, sp<Fence> fence, + const IGraphicBufferProducer::QueueBufferOutput& output); + struct BufferSlot { sp<GraphicBuffer> buffer; Region dirtyRegion; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 0abe72c5b7..48bc5d5398 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -388,6 +388,10 @@ public: // The vsync Id provided by Choreographer.getVsyncId int64_t mFrameTimelineVsyncId = ISurfaceComposer::INVALID_VSYNC_ID; + // If not null, transactions will be queued up using this token otherwise a common token + // per process will be used. + sp<IBinder> mApplyToken = nullptr; + InputWindowCommands mInputWindowCommands; int mStatus = NO_ERROR; @@ -555,6 +559,11 @@ public: // in shared buffer mode. Transaction& setAutoRefresh(const sp<SurfaceControl>& sc, bool autoRefresh); + // Queues up transactions using this token in SurfaceFlinger. By default, all transactions + // from a client are placed on the same queue. This can be used to prevent multiple + // transactions from blocking each other. + Transaction& setApplyToken(const sp<IBinder>& token); + status_t setDisplaySurface(const sp<IBinder>& token, const sp<IGraphicBufferProducer>& bufferProducer); diff --git a/libs/gui/include/gui/SyncScreenCaptureListener.h b/libs/gui/include/gui/SyncScreenCaptureListener.h index 2857996c23..8620b77c18 100644 --- a/libs/gui/include/gui/SyncScreenCaptureListener.h +++ b/libs/gui/include/gui/SyncScreenCaptureListener.h @@ -16,16 +16,19 @@ #pragma once +#include <android/gui/BnScreenCaptureListener.h> #include <gui/SurfaceComposerClient.h> #include <future> namespace android { -class SyncScreenCaptureListener : public BnScreenCaptureListener { +using gui::ScreenCaptureResults; + +struct SyncScreenCaptureListener : gui::BnScreenCaptureListener { public: - status_t onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { + binder::Status onScreenCaptureComplete(const ScreenCaptureResults& captureResults) override { resultsPromise.set_value(captureResults); - return NO_ERROR; + return binder::Status::ok(); } ScreenCaptureResults waitForResults() { diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index 17f8b975a8..d69b7c38a3 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -24,6 +24,7 @@ #include <gui/FrameTimestamps.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> +#include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> #include <private/gui/ComposerService.h> @@ -220,6 +221,32 @@ protected: return captureResults.result; } + void queueBuffer(sp<IGraphicBufferProducer> igbp, uint8_t r, uint8_t g, uint8_t b, + nsecs_t presentTimeDelay) { + int slot; + sp<Fence> fence; + sp<GraphicBuffer> buf; + auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, + PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, + nullptr, nullptr); + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); + ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf)); + + uint32_t* bufData; + buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN), + reinterpret_cast<void**>(&bufData)); + fillBuffer(bufData, Rect(buf->getWidth(), buf->getHeight() / 2), buf->getStride(), r, g, b); + buf->unlock(); + + IGraphicBufferProducer::QueueBufferOutput qbOutput; + nsecs_t timestampNanos = systemTime() + presentTimeDelay; + IGraphicBufferProducer::QueueBufferInput input(timestampNanos, false, HAL_DATASPACE_UNKNOWN, + Rect(mDisplayWidth, mDisplayHeight / 2), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, + Fence::NO_FENCE); + igbp->queueBuffer(slot, input, &qbOutput); + } + sp<SurfaceComposerClient> mClient; sp<ISurfaceComposer> mComposer; @@ -515,6 +542,53 @@ TEST_F(BLASTBufferQueueTest, CustomProducerListener) { adapter.waitForCallbacks(); } +TEST_F(BLASTBufferQueueTest, QueryNativeWindowQueuesToWindowComposer) { + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<android::Surface> surface = new Surface(adapter.getIGraphicBufferProducer()); + ANativeWindow* nativeWindow = (ANativeWindow*)(surface.get()); + int queuesToNativeWindow = 0; + int err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + &queuesToNativeWindow); + ASSERT_EQ(NO_ERROR, err); + ASSERT_EQ(queuesToNativeWindow, 1); +} + +TEST_F(BLASTBufferQueueTest, OutOfOrderTransactionTest) { + sp<SurfaceControl> bgSurface = + mClient->createSurface(String8("BGTest"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + ASSERT_NE(nullptr, bgSurface.get()); + Transaction t; + t.setLayerStack(bgSurface, 0) + .show(bgSurface) + .setDataspace(bgSurface, ui::Dataspace::V0_SRGB) + .setFrame(bgSurface, Rect(0, 0, mDisplayWidth, mDisplayHeight)) + .setLayer(bgSurface, std::numeric_limits<int32_t>::max() - 1) + .apply(); + + BLASTBufferQueueHelper slowAdapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> slowIgbProducer; + setUpProducer(slowAdapter, slowIgbProducer); + nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count(); + queueBuffer(slowIgbProducer, 0 /* r */, 0 /* g */, 0 /* b */, presentTimeDelay); + + BLASTBufferQueueHelper fastAdapter(bgSurface, mDisplayWidth, mDisplayHeight); + sp<IGraphicBufferProducer> fastIgbProducer; + setUpProducer(fastAdapter, fastIgbProducer); + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + queueBuffer(fastIgbProducer, r, g, b, 0 /* presentTimeDelay */); + fastAdapter.waitForCallbacks(); + + // capture screen and verify that it is red + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest { public: void test(uint32_t tr) { diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 15bd32d354..2af2fe1f91 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -70,6 +70,9 @@ namespace { const int QUEUE_BUFFER_INPUT_SCALING_MODE = 0; const int QUEUE_BUFFER_INPUT_TRANSFORM = 0; const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE; + const uint32_t QUEUE_BUFFER_INPUT_STICKY_TRANSFORM = 0; + const bool QUEUE_BUFFER_INPUT_GET_TIMESTAMPS = 0; + const int QUEUE_BUFFER_INPUT_SLOT = -1; // Enums to control which IGraphicBufferProducer backend to test. enum IGraphicBufferProducerTestCode { @@ -156,6 +159,9 @@ protected: scalingMode = QUEUE_BUFFER_INPUT_SCALING_MODE; transform = QUEUE_BUFFER_INPUT_TRANSFORM; fence = QUEUE_BUFFER_INPUT_FENCE; + stickyTransform = QUEUE_BUFFER_INPUT_STICKY_TRANSFORM; + getTimestamps = QUEUE_BUFFER_INPUT_GET_TIMESTAMPS; + slot = QUEUE_BUFFER_INPUT_SLOT; } IGraphicBufferProducer::QueueBufferInput build() { @@ -166,7 +172,10 @@ protected: crop, scalingMode, transform, - fence); + fence, + stickyTransform, + getTimestamps, + slot); } QueueBufferInputBuilder& setTimestamp(int64_t timestamp) { @@ -204,6 +213,21 @@ protected: return *this; } + QueueBufferInputBuilder& setStickyTransform(uint32_t stickyTransform) { + this->stickyTransform = stickyTransform; + return *this; + } + + QueueBufferInputBuilder& setGetTimestamps(bool getTimestamps) { + this->getTimestamps = getTimestamps; + return *this; + } + + QueueBufferInputBuilder& setSlot(int slot) { + this->slot = slot; + return *this; + } + private: int64_t timestamp; bool isAutoTimestamp; @@ -212,17 +236,17 @@ protected: int scalingMode; uint32_t transform; sp<Fence> fence; - }; // struct QueueBufferInputBuilder - - // To easily store dequeueBuffer results into containers - struct DequeueBufferResult { + uint32_t stickyTransform; + bool getTimestamps; int slot; - sp<Fence> fence; - }; + }; // struct QueueBufferInputBuilder - status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, DequeueBufferResult* result) { - return mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, - nullptr, nullptr); + status_t dequeueBuffer(uint32_t w, uint32_t h, uint32_t format, uint32_t usage, + IGraphicBufferProducer::DequeueBufferOutput* result) { + result->result = + mProducer->dequeueBuffer(&result->slot, &result->fence, w, h, format, usage, + &result->bufferAge, nullptr); + return result->result; } void setupDequeueRequestBuffer(int *slot, sp<Fence> *fence, @@ -336,6 +360,27 @@ TEST_P(IGraphicBufferProducerTest, Query_Succeeds) { EXPECT_OK(mProducer->query(NATIVE_WINDOW_CONSUMER_USAGE_BITS, &value)); EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, value); + { // Test the batched version + std::vector<int32_t> inputs = { + NATIVE_WINDOW_WIDTH, + NATIVE_WINDOW_HEIGHT, + NATIVE_WINDOW_FORMAT, + NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, + NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND, + NATIVE_WINDOW_CONSUMER_USAGE_BITS }; + using QueryOutput = IGraphicBufferProducer::QueryOutput; + std::vector<QueryOutput> outputs; + EXPECT_OK(mProducer->query(inputs, &outputs)); + EXPECT_EQ(DEFAULT_WIDTH, static_cast<uint32_t>(outputs[0].value)); + EXPECT_EQ(DEFAULT_HEIGHT, static_cast<uint32_t>(outputs[1].value)); + EXPECT_EQ(DEFAULT_FORMAT, outputs[2].value); + EXPECT_LE(0, outputs[3].value); + EXPECT_FALSE(outputs[4].value); + EXPECT_EQ(DEFAULT_CONSUMER_USAGE_BITS, outputs[5].value); + for (const QueryOutput& output : outputs) { + EXPECT_OK(output.result); + } + } } TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { @@ -358,6 +403,24 @@ TEST_P(IGraphicBufferProducerTest, Query_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_TRANSFORM_HINT, &value)); // TODO: Consider documented the above enums as unsupported or make a new enum for IGBP + { // Test the batched version + std::vector<int32_t> inputs = { + -1, + static_cast<int32_t>(0xDEADBEEF), + NATIVE_WINDOW_QUERY_LAST_OFF_BY_ONE, + NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + NATIVE_WINDOW_CONCRETE_TYPE, + NATIVE_WINDOW_DEFAULT_WIDTH, + NATIVE_WINDOW_DEFAULT_HEIGHT, + NATIVE_WINDOW_TRANSFORM_HINT}; + using QueryOutput = IGraphicBufferProducer::QueryOutput; + std::vector<QueryOutput> outputs; + EXPECT_OK(mProducer->query(inputs, &outputs)); + for (const QueryOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } + // Value was NULL EXPECT_EQ(BAD_VALUE, mProducer->query(NATIVE_WINDOW_FORMAT, /*value*/nullptr)); @@ -416,29 +479,113 @@ TEST_P(IGraphicBufferProducerTest, Queue_Succeeds) { // Buffer was not in the dequeued state EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Request + std::vector<int32_t> requestInputs; + requestInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + requestInputs.emplace_back(dequeueOutput.slot); + } + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + std::vector<RequestBufferOutput> requestOutputs; + EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs)); + ASSERT_EQ(requestInputs.size(), requestOutputs.size()); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + EXPECT_OK(requestOutput.result); + } + + // Queue + using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput; + using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput; + std::vector<QueueBufferInput> queueInputs; + queueInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + queueInputs.emplace_back(CreateBufferInput()).slot = + dequeueOutput.slot; + } + std::vector<QueueBufferOutput> queueOutputs; + EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs)); + ASSERT_EQ(queueInputs.size(), queueOutputs.size()); + for (const QueueBufferOutput& queueOutput : queueOutputs) { + EXPECT_OK(queueOutput.result); + } + + // Re-queue + EXPECT_OK(mProducer->queueBuffers(queueInputs, &queueOutputs)); + ASSERT_EQ(queueInputs.size(), queueOutputs.size()); + for (const QueueBufferOutput& queueOutput : queueOutputs) { + EXPECT_EQ(BAD_VALUE, queueOutput.result); + } + } } TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { ASSERT_NO_FATAL_FAILURE(ConnectProducer()); + using QueueBufferInput = IGraphicBufferProducer::QueueBufferInput; + using QueueBufferOutput = IGraphicBufferProducer::QueueBufferOutput; // Invalid slot number { // A generic "valid" input - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/-1, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0xDEADBEEF, input, &output)); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(BufferQueue::NUM_BUFFER_SLOTS, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = -1; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Slot was not in the dequeued state (all slots start out in Free state) { - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(/*slot*/0, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = 0; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Put the slot into the "dequeued" state for the rest of the test @@ -453,10 +600,22 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // Slot was enqueued without requesting a buffer { - IGraphicBufferProducer::QueueBufferInput input = CreateBufferInput(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferInput input = CreateBufferInput(); + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Request the buffer so that the rest of the tests don't fail on earlier checks. @@ -467,11 +626,23 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { { sp<Fence> nullFence = nullptr; - IGraphicBufferProducer::QueueBufferInput input = + QueueBufferInput input = QueueBufferInputBuilder().setFence(nullFence).build(); - IGraphicBufferProducer::QueueBufferOutput output; + QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Scaling mode was unknown @@ -482,9 +653,33 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } + input = QueueBufferInputBuilder().setScalingMode(0xDEADBEEF).build(); EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Crop rect is out of bounds of the buffer dimensions @@ -495,6 +690,18 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { IGraphicBufferProducer::QueueBufferOutput output; EXPECT_EQ(BAD_VALUE, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(BAD_VALUE, output.result); + } + } } // Abandon the buffer queue so that the last test fails @@ -507,6 +714,18 @@ TEST_P(IGraphicBufferProducerTest, Queue_ReturnsError) { // TODO(b/73267953): Make BufferHub honor producer and consumer connection. EXPECT_EQ(NO_INIT, mProducer->queueBuffer(dequeuedSlot, input, &output)); + + { // Test with the batched version + constexpr size_t BATCH_SIZE = 16; + input.slot = dequeuedSlot; + std::vector<QueueBufferInput> inputs(BATCH_SIZE, input); + std::vector<QueueBufferOutput> outputs; + EXPECT_OK(mProducer->queueBuffers(inputs, &outputs)); + ASSERT_EQ(inputs.size(), outputs.size()); + for (const QueueBufferOutput& output : outputs) { + EXPECT_EQ(NO_INIT, output.result); + } + } } } @@ -525,6 +744,44 @@ TEST_P(IGraphicBufferProducerTest, CancelBuffer_DoesntCrash) { // No return code, but at least test that it doesn't blow up... // TODO: add a return code mProducer->cancelBuffer(dequeuedSlot, dequeuedFence); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Cancel + using CancelBufferInput = IGraphicBufferProducer::CancelBufferInput; + std::vector<CancelBufferInput> cancelInputs; + cancelInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, + ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + CancelBufferInput& cancelInput = cancelInputs.emplace_back(); + cancelInput.slot = dequeueOutput.slot; + cancelInput.fence = dequeueOutput.fence; + } + std::vector<status_t> cancelOutputs; + EXPECT_OK(mProducer->cancelBuffers(cancelInputs, &cancelOutputs)); + ASSERT_EQ(cancelInputs.size(), cancelOutputs.size()); + for (status_t result : cancelOutputs) { + EXPECT_OK(result); + } + } } TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { @@ -541,11 +798,11 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { << "bufferCount: " << minBuffers; // Should now be able to dequeue up to minBuffers times - DequeueBufferResult result; + IGraphicBufferProducer::DequeueBufferOutput result; for (int i = 0; i < minBuffers; ++i) { EXPECT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & (dequeueBuffer(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_FORMAT, - TEST_PRODUCER_USAGE_BITS, &result))) + TEST_PRODUCER_USAGE_BITS, &result))) << "iteration: " << i << ", slot: " << result.slot; } @@ -558,7 +815,6 @@ TEST_P(IGraphicBufferProducerTest, SetMaxDequeuedBufferCount_Succeeds) { ASSERT_OK(mProducer->requestBuffer(result.slot, &buffer)); ASSERT_OK(mProducer->queueBuffer(result.slot, input, &output)); - // Should now be able to dequeue up to maxBuffers times int dequeuedSlot = -1; sp<Fence> dequeuedFence; @@ -794,6 +1050,71 @@ TEST_P(IGraphicBufferProducerTest, DetachThenAttach_Succeeds) { EXPECT_OK(mProducer->attachBuffer(&slot, buffer)); EXPECT_OK(buffer->initCheck()); + + ASSERT_OK(mProducer->detachBuffer(slot)); + + { // Test batched methods + constexpr size_t BATCH_SIZE = 4; + ASSERT_OK(mProducer->setMaxDequeuedBufferCount(BATCH_SIZE)); + + // Dequeue + using DequeueBufferInput = IGraphicBufferProducer::DequeueBufferInput; + using DequeueBufferOutput = IGraphicBufferProducer::DequeueBufferOutput; + DequeueBufferInput dequeueInput; + dequeueInput.width = DEFAULT_WIDTH; + dequeueInput.height = DEFAULT_HEIGHT; + dequeueInput.format = DEFAULT_FORMAT; + dequeueInput.usage = TEST_PRODUCER_USAGE_BITS; + dequeueInput.getTimestamps = false; + std::vector<DequeueBufferInput> dequeueInputs(BATCH_SIZE, dequeueInput); + std::vector<DequeueBufferOutput> dequeueOutputs; + EXPECT_OK(mProducer->dequeueBuffers(dequeueInputs, &dequeueOutputs)); + ASSERT_EQ(dequeueInputs.size(), dequeueOutputs.size()); + + // Request + std::vector<int32_t> requestInputs; + requestInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + ASSERT_EQ(OK, ~IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION & + dequeueOutput.result); + requestInputs.emplace_back(dequeueOutput.slot); + } + using RequestBufferOutput = IGraphicBufferProducer::RequestBufferOutput; + std::vector<RequestBufferOutput> requestOutputs; + EXPECT_OK(mProducer->requestBuffers(requestInputs, &requestOutputs)); + ASSERT_EQ(requestInputs.size(), requestOutputs.size()); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + EXPECT_OK(requestOutput.result); + } + + // Detach + std::vector<int32_t> detachInputs; + detachInputs.reserve(BATCH_SIZE); + for (const DequeueBufferOutput& dequeueOutput : dequeueOutputs) { + detachInputs.emplace_back(dequeueOutput.slot); + } + std::vector<status_t> detachOutputs; + EXPECT_OK(mProducer->detachBuffers(detachInputs, &detachOutputs)); + ASSERT_EQ(detachInputs.size(), detachOutputs.size()); + for (status_t result : detachOutputs) { + EXPECT_OK(result); + } + + // Attach + using AttachBufferOutput = IGraphicBufferProducer::AttachBufferOutput; + std::vector<sp<GraphicBuffer>> attachInputs; + attachInputs.reserve(BATCH_SIZE); + for (const RequestBufferOutput& requestOutput : requestOutputs) { + attachInputs.emplace_back(requestOutput.buffer); + } + std::vector<AttachBufferOutput> attachOutputs; + EXPECT_OK(mProducer->attachBuffers(attachInputs, &attachOutputs)); + ASSERT_EQ(attachInputs.size(), attachOutputs.size()); + for (const AttachBufferOutput& attachOutput : attachOutputs) { + EXPECT_OK(attachOutput.result); + EXPECT_NE(-1, attachOutput.slot); + } + } } #if USE_BUFFER_HUB_AS_BUFFER_QUEUE diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index fa98cd4c3c..63db9a7b96 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -2024,4 +2024,86 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { EXPECT_EQ(BufferQueueDefs::NUM_BUFFER_SLOTS, count); } +TEST_F(SurfaceTest, BatchOperations) { + const int BUFFER_COUNT = 16; + const int BATCH_SIZE = 8; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<StubProducerListener> listener = new StubProducerListener(); + + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, + /*reportBufferRemoval*/false)); + + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); + + // Batch dequeued buffers can be queued individually + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + for (size_t i = 0; i < BATCH_SIZE; i++) { + ANativeWindowBuffer* buffer = buffers[i].buffer; + int fence = buffers[i].fenceFd; + ASSERT_EQ(NO_ERROR, window->queueBuffer(window.get(), buffer, fence)); + } + + // Batch dequeued buffers can be canceled individually + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + for (size_t i = 0; i < BATCH_SIZE; i++) { + ANativeWindowBuffer* buffer = buffers[i].buffer; + int fence = buffers[i].fenceFd; + ASSERT_EQ(NO_ERROR, window->cancelBuffer(window.get(), buffer, fence)); + } + + // Batch dequeued buffers can be batch cancelled + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + ASSERT_EQ(NO_ERROR, surface->cancelBuffers(buffers)); + + // Batch dequeued buffers can be batch queued + ASSERT_EQ(NO_ERROR, surface->dequeueBuffers(&buffers)); + std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); + for (size_t i = 0; i < BATCH_SIZE; i++) { + queuedBuffers[i].buffer = buffers[i].buffer; + queuedBuffers[i].fenceFd = buffers[i].fenceFd; + queuedBuffers[i].timestamp = NATIVE_WINDOW_TIMESTAMP_AUTO; + } + ASSERT_EQ(NO_ERROR, surface->queueBuffers(queuedBuffers)); + + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} + +TEST_F(SurfaceTest, BatchIllegalOperations) { + const int BUFFER_COUNT = 16; + const int BATCH_SIZE = 8; + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + + sp<CpuConsumer> cpuConsumer = new CpuConsumer(consumer, 1); + sp<Surface> surface = new Surface(producer); + sp<ANativeWindow> window(surface); + sp<StubProducerListener> listener = new StubProducerListener(); + + ASSERT_EQ(OK, surface->connect(NATIVE_WINDOW_API_CPU, /*listener*/listener, + /*reportBufferRemoval*/false)); + + ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(window.get(), BUFFER_COUNT)); + + std::vector<Surface::BatchBuffer> buffers(BATCH_SIZE); + std::vector<Surface::BatchQueuedBuffer> queuedBuffers(BATCH_SIZE); + + // Batch operations are invalid in shared buffer mode + surface->setSharedBufferMode(true); + ASSERT_EQ(INVALID_OPERATION, surface->dequeueBuffers(&buffers)); + ASSERT_EQ(INVALID_OPERATION, surface->cancelBuffers(buffers)); + ASSERT_EQ(INVALID_OPERATION, surface->queueBuffers(queuedBuffers)); + surface->setSharedBufferMode(false); + + ASSERT_EQ(NO_ERROR, surface->disconnect(NATIVE_WINDOW_API_CPU)); +} + } // namespace android diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 3e65d9afce..45db31c026 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -41,6 +41,9 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg if (strcmp(prop, "skiagl") == 0) { renderEngineType = RenderEngineType::SKIA_GL; } + if (strcmp(prop, "skiaglthreaded") == 0) { + renderEngineType = RenderEngineType::SKIA_GL_THREADED; + } switch (renderEngineType) { case RenderEngineType::THREADED: @@ -49,6 +52,10 @@ std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArg [args]() { return android::renderengine::gl::GLESRenderEngine::create(args); }); case RenderEngineType::SKIA_GL: return renderengine::skia::SkiaGLRenderEngine::create(args); + case RenderEngineType::SKIA_GL_THREADED: + return renderengine::threaded::RenderEngineThreaded::create([args]() { + return android::renderengine::skia::SkiaGLRenderEngine::create(args); + }); case RenderEngineType::GLES: default: ALOGD("RenderEngine with GLES Backend"); diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index 915706675a..506f81ecc2 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -82,6 +82,7 @@ public: GLES = 1, THREADED = 2, SKIA_GL = 3, + SKIA_GL_THREADED = 4, }; static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args); diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp index d126c27728..2ffb547514 100644 --- a/libs/renderengine/skia/AutoBackendTexture.cpp +++ b/libs/renderengine/skia/AutoBackendTexture.cpp @@ -72,6 +72,7 @@ static sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) { AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isRender) { + ATRACE_CALL(); AHardwareBuffer_Desc desc; AHardwareBuffer_describe(buffer, &desc); bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp index 3d6b7afe67..d9495a9089 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp @@ -19,25 +19,10 @@ #define LOG_TAG "RenderEngine" #define ATRACE_TAG ATRACE_TAG_GRAPHICS -#include <cstdint> -#include <memory> - -#include "SkImageInfo.h" -#include "log/log_main.h" -#include "system/graphics-base-v1.0.h" +#include "SkiaGLRenderEngine.h" #include <EGL/egl.h> #include <EGL/eglext.h> -#include <sync/sync.h> -#include <ui/BlurRegion.h> -#include <ui/GraphicBuffer.h> -#include <utils/Trace.h> -#include "../gl/GLExtensions.h" -#include "SkiaGLRenderEngine.h" -#include "filters/BlurFilter.h" -#include "filters/LinearEffect.h" -#include "skia/debug/SkiaCapture.h" - #include <GrContextOptions.h> #include <SkCanvas.h> #include <SkColorFilter.h> @@ -48,8 +33,23 @@ #include <SkShadowUtils.h> #include <SkSurface.h> #include <gl/GrGLInterface.h> +#include <sync/sync.h> +#include <ui/BlurRegion.h> +#include <ui/GraphicBuffer.h> +#include <utils/Trace.h> #include <cmath> +#include <cstdint> +#include <memory> + +#include "../gl/GLExtensions.h" +#include "SkBlendMode.h" +#include "SkImageInfo.h" +#include "filters/BlurFilter.h" +#include "filters/LinearEffect.h" +#include "log/log_main.h" +#include "skia/debug/SkiaCapture.h" +#include "system/graphics-base-v1.0.h" bool checkGlError(const char* op, int lineNumber); @@ -418,6 +418,32 @@ void SkiaGLRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { mProtectedTextureCache.erase(bufferId); } +sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader, + const LayerSettings* layer, + const DisplaySettings& display, + bool undoPremultipliedAlpha) { + if (mUseColorManagement && + needsLinearEffect(layer->colorTransform, layer->sourceDataspace, display.outputDataspace)) { + LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace, + .outputDataspace = display.outputDataspace, + .undoPremultipliedAlpha = undoPremultipliedAlpha}; + + auto effectIter = mRuntimeEffects.find(effect); + sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; + if (effectIter == mRuntimeEffects.end()) { + runtimeEffect = buildRuntimeEffect(effect); + mRuntimeEffects.insert({effect, runtimeEffect}); + } else { + runtimeEffect = effectIter->second; + } + return createLinearEffectShader(shader, effect, runtimeEffect, layer->colorTransform, + display.maxLuminance, + layer->source.buffer.maxMasteringLuminance, + layer->source.buffer.maxContentLuminance); + } + return shader; +} + status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<const LayerSettings*>& layers, const sp<GraphicBuffer>& buffer, @@ -624,44 +650,53 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, shader = image->makeShader(SkSamplingOptions(), matrix); } - if (mUseColorManagement && - needsLinearEffect(layer->colorTransform, layer->sourceDataspace, - display.outputDataspace)) { - LinearEffect effect = LinearEffect{.inputDataspace = layer->sourceDataspace, - .outputDataspace = display.outputDataspace, - .undoPremultipliedAlpha = !item.isOpaque && - item.usePremultipliedAlpha}; - - auto effectIter = mRuntimeEffects.find(effect); - sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; - if (effectIter == mRuntimeEffects.end()) { - runtimeEffect = buildRuntimeEffect(effect); - mRuntimeEffects.insert({effect, runtimeEffect}); - } else { - runtimeEffect = effectIter->second; - } + paint.setShader( + createRuntimeEffectShader(shader, layer, display, + !item.isOpaque && item.usePremultipliedAlpha)); - paint.setShader(createLinearEffectShader(shader, effect, runtimeEffect, - layer->colorTransform, - display.maxLuminance, - layer->source.buffer.maxMasteringLuminance, - layer->source.buffer.maxContentLuminance)); - } else { - paint.setShader(shader); - } // Make sure to take into the account the alpha set on the layer. paint.setAlphaf(layer->alpha); } else { ATRACE_NAME("DrawColor"); const auto color = layer->source.solidColor; - paint.setShader(SkShaders::Color(SkColor4f{.fR = color.r, - .fG = color.g, - .fB = color.b, - layer->alpha}, - nullptr)); + sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, + .fG = color.g, + .fB = color.b, + layer->alpha}, + nullptr); + paint.setShader(createRuntimeEffectShader(shader, layer, display, + /* undoPremultipliedAlpha */ false)); } - paint.setColorFilter(SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform))); + sk_sp<SkColorFilter> filter = + SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); + + // Handle opaque images - it's a little nonstandard how we do this. + // Fundamentally we need to support SurfaceControl.Builder#setOpaque: + // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) + // The important language is that when isOpaque is set, opacity is not sampled from the + // alpha channel, but blending may still be supported on a transaction via setAlpha. So, + // here's the conundrum: + // 1. We can't force the SkImage alpha type to kOpaque_SkAlphaType, because it's treated as + // an internal hint - composition is undefined when there are alpha bits present. + // 2. We can try to lie about the pixel layout, but that only works for RGBA8888 buffers, + // i.e., treating them as RGBx8888 instead. But we can't do the same for RGBA1010102 because + // RGBx1010102 is not supported as a pixel layout for SkImages. It's also not clear what to + // use for F16 either, and lying about the pixel layout is a bit of a hack anyways. + // 3. We can't change the blendmode to src, because while this satisfies the requirement for + // ignoring the alpha channel, it doesn't quite satisfy the blending requirement because + // src always clobbers the destination content. + // + // So, what we do here instead is an additive blend mode where we compose the input image + // with a solid black. This might need to be reassess if this does not support FP16 + // incredibly well, but FP16 end-to-end isn't well supported anyway at the moment. + if (layer->source.buffer.buffer && layer->source.buffer.isOpaque) { + filter = SkColorFilters::Compose(filter, + SkColorFilters::Blend(SK_ColorBLACK, + SkBlendMode::kPlus)); + } + + paint.setColorFilter(filter); for (const auto effectRegion : layer->blurRegions) { drawBlurRegion(canvas, effectRegion, layerRect, cachedBlurs[effectRegion.blurRadius]); @@ -672,13 +707,17 @@ status_t SkiaGLRenderEngine::drawLayers(const DisplaySettings& display, ? getSkRect(layer->geometry.roundedCornersCrop) : dest; drawShadow(canvas, rect, layer->geometry.roundedCornersRadius, layer->shadow); + } else { + // Shadows are assumed to live only on their own layer - it's not valid + // to draw the boundary retangles when there is already a caster shadow + // TODO(b/175915334): consider relaxing this restriction to enable more flexible + // composition - using a well-defined invalid color is long-term less error-prone. + // Push the clipRRect onto the clip stack. Draw the image. Pop the clip. + if (layer->geometry.roundedCornersRadius > 0) { + canvas->clipRRect(getRoundedRect(layer), true); + } + canvas->drawRect(dest, paint); } - - // Push the clipRRect onto the clip stack. Draw the image. Pop the clip. - if (layer->geometry.roundedCornersRadius > 0) { - canvas->clipRRect(getRoundedRect(layer), true); - } - canvas->drawRect(dest, paint); canvas->restore(); } canvas->restore(); diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h index 3d8c693d41..5384ec878d 100644 --- a/libs/renderengine/skia/SkiaGLRenderEngine.h +++ b/libs/renderengine/skia/SkiaGLRenderEngine.h @@ -91,6 +91,11 @@ private: void drawBlurRegion(SkCanvas* canvas, const BlurRegion& blurRegion, const SkRect& layerRect, sk_sp<SkSurface> blurredSurface); SkMatrix getBlurShaderTransform(const SkCanvas* canvas, const SkRect& layerRect); + // If mUseColorManagement is correct and layer needsLinearEffect, it returns a linear runtime + // shader. Otherwise it returns the input shader. + sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings* layer, + const DisplaySettings& display, + bool undoPremultipliedAlpha); EGLDisplay mEGLDisplay; EGLContext mEGLContext; diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp index 8006a111bd..e9cfead8b6 100644 --- a/libs/renderengine/skia/debug/SkiaCapture.cpp +++ b/libs/renderengine/skia/debug/SkiaCapture.cpp @@ -129,8 +129,14 @@ bool SkiaCapture::setupMultiFrameCapture() { // SkDocuments don't take ownership of the streams they write. // we need to keep it until after mMultiPic.close() // procs is passed as a pointer, but just as a method of having an optional default. - // procs doesn't need to outlive this Make call. - mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs); + // procs doesn't need to outlive this Make call + // The last argument is a callback for the endPage behavior. + // See SkSharingProc.h for more explanation of this callback. + mMultiPic = SkMakeMultiPictureDocument( + mOpenMultiPicStream.get(), &procs, + [sharingCtx = mSerialContext.get()](const SkPicture* pic) { + SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx); + }); mCaptureRunning = true; return true; } else { diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index 47b8cad12d..2b8063e357 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -306,6 +306,26 @@ public: backgroundColor.a); } + void expectShadowColorWithoutCaster(const FloatRect& casterBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor) { + const float shadowInset = shadow.length * -1.0f; + const Rect casterRect(casterBounds); + const Rect shadowRect = + Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset); + + const Region backgroundRegion = + Region(fullscreenRect()).subtractSelf(casterRect).subtractSelf(shadowRect); + + expectAlpha(shadowRect, 255); + // (0, 0, 0) fill on the bounds of the layer should be ignored. + expectBufferColor(casterRect, 255, 255, 255, 255, 254); + + // verify background + expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b, + backgroundColor.a); + } + static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength, bool casterIsTranslucent) { renderengine::ShadowSettings shadow; @@ -447,6 +467,10 @@ public: const renderengine::ShadowSettings& shadow, const ubyte4& casterColor, const ubyte4& backgroundColor); + void drawShadowWithoutCaster(const FloatRect& castingBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor); + std::unique_ptr<renderengine::gl::GLESRenderEngine> mRE; // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to @@ -1119,6 +1143,37 @@ void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLaye invokeDraw(settings, layers, mBuffer); } +void RenderEngineTest::drawShadowWithoutCaster(const FloatRect& castingBounds, + const renderengine::ShadowSettings& shadow, + const ubyte4& backgroundColor) { + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<const renderengine::LayerSettings*> layers; + + // add background layer + renderengine::LayerSettings bgLayer; + bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + bgLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f, + backgroundColor.b / 255.0f, this); + bgLayer.alpha = backgroundColor.a / 255.0f; + layers.push_back(&bgLayer); + + // add shadow layer + renderengine::LayerSettings shadowLayer; + shadowLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + shadowLayer.geometry.boundaries = castingBounds; + shadowLayer.alpha = 1.0f; + ColorSourceVariant::fillColor(shadowLayer, 0, 0, 0, this); + shadowLayer.shadow = shadow; + layers.push_back(&shadowLayer); + + invokeDraw(settings, layers, mBuffer); +} + INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest, testing::Values(std::make_shared<GLESRenderEngineFactory>(), std::make_shared<GLESCMRenderEngineFactory>(), @@ -1131,6 +1186,37 @@ TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) { drawEmptyLayers(); } +TEST_P(RenderEngineTest, drawLayers_withoutBuffers_withColorTransform) { + const auto& renderEngineFactory = GetParam(); + mRE = renderEngineFactory->createRenderEngine(); + + renderengine::DisplaySettings settings; + settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + // 255, 255, 255, 255 is full opaque white. + const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f); + // Create layer with given color. + renderengine::LayerSettings bgLayer; + bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR; + bgLayer.geometry.boundaries = fullscreenRect().toFloatRect(); + bgLayer.source.solidColor = half3(backgroundColor.r / 255.0f, backgroundColor.g / 255.0f, + backgroundColor.b / 255.0f); + bgLayer.alpha = backgroundColor.a / 255.0f; + // Transform the red color. + bgLayer.colorTransform = mat4(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + + std::vector<const renderengine::LayerSettings*> layers; + layers.push_back(&bgLayer); + + invokeDraw(settings, layers, mBuffer); + + // Expect to see full opaque pixel (with inverted red from the transform). + expectBufferColor(Rect(0, 0, 1, 1), 0.f, backgroundColor.g, backgroundColor.b, + backgroundColor.a); +} + TEST_P(RenderEngineTest, drawLayers_nullOutputBuffer) { const auto& renderEngineFactory = GetParam(); mRE = renderEngineFactory->createRenderEngine(); @@ -1609,6 +1695,22 @@ TEST_P(RenderEngineTest, cacheExternalBuffer_cachesImages) { EXPECT_FALSE(mRE->isImageCachedForTesting(bufferId)); } +TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) { + const auto& renderEngineFactory = GetParam(); + mRE = renderEngineFactory->createRenderEngine(); + + const ubyte4 backgroundColor(255, 255, 255, 255); + const float shadowLength = 5.0f; + Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f); + casterBounds.offsetBy(shadowLength + 1, shadowLength + 1); + renderengine::ShadowSettings settings = + getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength, + false /* casterIsTranslucent */); + + drawShadowWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor); + expectShadowColorWithoutCaster(casterBounds.toFloatRect(), settings, backgroundColor); +} + TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) { const auto& renderEngineFactory = GetParam(); mRE = renderEngineFactory->createRenderEngine(); diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index f973cbad17..7bddee60a5 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -64,6 +64,15 @@ void SensorPrivacyManager::addSensorPrivacyListener( } } +void SensorPrivacyManager::addIndividualSensorPrivacyListener(int userId, int sensor, + const sp<hardware::ISensorPrivacyListener>& listener) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + service->addIndividualSensorPrivacyListener(userId, sensor, listener); + } +} + void SensorPrivacyManager::removeSensorPrivacyListener( const sp<hardware::ISensorPrivacyListener>& listener) { @@ -85,6 +94,18 @@ bool SensorPrivacyManager::isSensorPrivacyEnabled() return false; } +bool SensorPrivacyManager::isIndividualSensorPrivacyEnabled(int userId, int sensor) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isIndividualSensorPrivacyEnabled(userId, sensor, &result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return false; +} + status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index 4c2d5dbb8f..629b8c2093 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -22,9 +22,17 @@ import android.hardware.ISensorPrivacyListener; interface ISensorPrivacyManager { void addSensorPrivacyListener(in ISensorPrivacyListener listener); + void addIndividualSensorPrivacyListener(int userId, int sensor, in ISensorPrivacyListener listener); + void removeSensorPrivacyListener(in ISensorPrivacyListener listener); boolean isSensorPrivacyEnabled(); + boolean isIndividualSensorPrivacyEnabled(int userId, int sensor); + void setSensorPrivacy(boolean enable); + + void setIndividualSensorPrivacy(int userId, int sensor, boolean enable); + + void setIndividualSensorPrivacyForProfileGroup(int userId, int sensor, boolean enable); } diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index 2546a681b9..bd7c726c69 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -28,11 +28,19 @@ namespace android { class SensorPrivacyManager { public: + enum { + INDIVIDUAL_SENSOR_MICROPHONE = 1, + INDIVIDUAL_SENSOR_CAMERA = 2 + }; + SensorPrivacyManager(); void addSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); + void addIndividualSensorPrivacyListener(int userId, int sensor, + const sp<hardware::ISensorPrivacyListener>& listener); void removeSensorPrivacyListener(const sp<hardware::ISensorPrivacyListener>& listener); bool isSensorPrivacyEnabled(); + bool isIndividualSensorPrivacyEnabled(int userId, int sensor); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/opengl/Android.bp b/opengl/Android.bp index 48abdce712..8b94f616c6 100644 --- a/opengl/Android.bp +++ b/opengl/Android.bp @@ -54,6 +54,7 @@ ndk_headers { cc_library_headers { name: "gl_headers", + host_supported: true, vendor_available: true, export_include_dirs: ["include"], } diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp index 398efc0a76..f57666077e 100644 --- a/opengl/libs/EGL/egl_platform_entries.cpp +++ b/opengl/libs/EGL/egl_platform_entries.cpp @@ -128,6 +128,7 @@ const char* const gExtensionString = "EGL_KHR_stream_producer_eglsurface " "EGL_KHR_surfaceless_context " "EGL_KHR_wait_sync " // strongly recommended + "EGL_NV_context_priority_realtime " "EGL_NV_system_time " ; diff --git a/services/audiomanager/IAudioManager.cpp b/services/audiomanager/IAudioManager.cpp index 6235f06bc1..0d1726508b 100644 --- a/services/audiomanager/IAudioManager.cpp +++ b/services/audiomanager/IAudioManager.cpp @@ -84,11 +84,13 @@ public: return remote()->transact(PLAYER_ATTRIBUTES, data, &reply, IBinder::FLAG_ONEWAY); } - virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event) { + virtual status_t playerEvent(audio_unique_id_t piid, player_state_t event, + audio_port_handle_t deviceId) { Parcel data, reply; data.writeInterfaceToken(IAudioManager::getInterfaceDescriptor()); data.writeInt32((int32_t) piid); data.writeInt32((int32_t) event); + data.writeInt32((int32_t) deviceId); return remote()->transact(PLAYER_EVENT, data, &reply, IBinder::FLAG_ONEWAY); } diff --git a/services/gpuservice/tests/unittests/GpuMemTest.cpp b/services/gpuservice/tests/unittests/GpuMemTest.cpp index c5f8859e45..e916221c2e 100644 --- a/services/gpuservice/tests/unittests/GpuMemTest.cpp +++ b/services/gpuservice/tests/unittests/GpuMemTest.cpp @@ -59,7 +59,6 @@ public: } void SetUp() override { - SKIP_IF_BPF_NOT_SUPPORTED; bpf::setrlimitForTest(); mGpuMem = std::make_unique<GpuMem>(); @@ -87,8 +86,6 @@ public: }; TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { - SKIP_IF_BPF_NOT_SUPPORTED; - EXPECT_EQ(mTestableGpuMem.getGpuMemTraceGroup(), "gpu_mem"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalTracepoint(), "gpu_mem_total"); EXPECT_EQ(mTestableGpuMem.getGpuMemTotalProgPath(), @@ -97,20 +94,16 @@ TEST_F(GpuMemTest, validGpuMemTotalBpfPaths) { } TEST_F(GpuMemTest, bpfInitializationFailed) { - SKIP_IF_BPF_NOT_SUPPORTED; - EXPECT_EQ(dumpsys(), "Failed to initialize GPU memory eBPF\n"); } TEST_F(GpuMemTest, gpuMemTotalMapEmpty) { - SKIP_IF_BPF_NOT_SUPPORTED; mTestableGpuMem.setGpuMemTotalMap(mTestMap); EXPECT_EQ(dumpsys(), "GPU memory total usage map is empty\n"); } TEST_F(GpuMemTest, globalMemTotal) { - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY)); mTestableGpuMem.setGpuMemTotalMap(mTestMap); @@ -118,7 +111,6 @@ TEST_F(GpuMemTest, globalMemTotal) { } TEST_F(GpuMemTest, missingGlobalMemTotal) { - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY)); mTestableGpuMem.setGpuMemTotalMap(mTestMap); @@ -126,7 +118,6 @@ TEST_F(GpuMemTest, missingGlobalMemTotal) { } TEST_F(GpuMemTest, procMemTotal) { - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY)); ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY)); mTestableGpuMem.setGpuMemTotalMap(mTestMap); @@ -146,7 +137,6 @@ TEST_F(GpuMemTest, procMemTotal) { } TEST_F(GpuMemTest, traverseGpuMemTotals) { - SKIP_IF_BPF_NOT_SUPPORTED; ASSERT_RESULT_OK(mTestMap.writeValue(TEST_GLOBAL_KEY, TEST_GLOBAL_VAL, BPF_ANY)); ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_1, TEST_PROC_VAL_1, BPF_ANY)); ASSERT_RESULT_OK(mTestMap.writeValue(TEST_PROC_KEY_2, TEST_PROC_VAL_2, BPF_ANY)); diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 4e55872bc6..887bdd4edc 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -55,12 +55,18 @@ private: ALOGE("There is no focused window for %s", applicationHandle->getName().c_str()); } - void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken, - const std::string& reason) override { - ALOGE("Connection is not responding: %s", reason.c_str()); + void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, + const std::string& reason) override { + ALOGE("Window is not responding: %s", reason.c_str()); } - void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override {} + void notifyWindowResponsive(const sp<IBinder>& connectionToken) override {} + + void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) override { + ALOGE("Monitor is not responding: %s", reason.c_str()); + } + + void notifyMonitorResponsive(int32_t pid) override {} void notifyInputChannelBroken(const sp<IBinder>&) override {} diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 3a860f05e0..26b641d70c 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -271,6 +271,7 @@ struct CommandEntry { sp<IBinder> newToken; std::string obscuringPackage; bool enabled; + int32_t pid; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index f3d969e93f..cdc74f3fa9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -453,6 +453,20 @@ static KeyEvent createKeyEvent(const KeyEntry& entry) { return event; } +static std::optional<int32_t> findMonitorPidByToken( + const std::unordered_map<int32_t, std::vector<Monitor>>& monitorsByDisplay, + const sp<IBinder>& token) { + for (const auto& it : monitorsByDisplay) { + const std::vector<Monitor>& monitors = it.second; + for (const Monitor& monitor : monitors) { + if (monitor.inputChannel->getConnectionToken() == token) { + return monitor.pid; + } + } + } + return std::nullopt; +} + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) @@ -615,7 +629,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { connection->responsive = false; // Stop waking up for this unresponsive connection mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); - onAnrLocked(*connection); + onAnrLocked(connection); return LONG_LONG_MIN; } @@ -1266,14 +1280,20 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( // Disable Pointer Capture token = mWindowTokenWithPointerCapture; mWindowTokenWithPointerCapture = nullptr; - mFocusedWindowRequestedPointerCapture = false; + if (mFocusedWindowRequestedPointerCapture) { + mFocusedWindowRequestedPointerCapture = false; + setPointerCaptureLocked(false); + } } auto channel = getInputChannelLocked(token); if (channel == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; - mFocusedWindowRequestedPointerCapture = false; + if (mFocusedWindowRequestedPointerCapture) { + mFocusedWindowRequestedPointerCapture = false; + setPointerCaptureLocked(false); + } return; } InputTarget target; @@ -5148,6 +5168,14 @@ std::optional<int32_t> InputDispatcher::findGestureMonitorDisplayByTokenLocked( return std::nullopt; } +std::optional<int32_t> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { + std::optional<int32_t> gesturePid = findMonitorPidByToken(mGestureMonitorsByDisplay, token); + if (gesturePid.has_value()) { + return gesturePid; + } + return findMonitorPidByToken(mGlobalMonitorsByDisplay, token); +} + sp<Connection> InputDispatcher::getConnectionLocked(const sp<IBinder>& inputConnectionToken) const { if (inputConnectionToken == nullptr) { return nullptr; @@ -5208,12 +5236,15 @@ void InputDispatcher::notifyFocusChangedLocked(const sp<IBinder>& oldToken, postCommandLocked(std::move(commandEntry)); } -void InputDispatcher::onAnrLocked(const Connection& connection) { +void InputDispatcher::onAnrLocked(const sp<Connection>& connection) { + if (connection == nullptr) { + LOG_ALWAYS_FATAL("Caller must check for nullness"); + } // Since we are allowing the policy to extend the timeout, maybe the waitQueue // is already healthy again. Don't raise ANR in this situation - if (connection.waitQueue.empty()) { + if (connection->waitQueue.empty()) { ALOGI("Not raising ANR because the connection %s has recovered", - connection.inputChannel->getName().c_str()); + connection->inputChannel->getName().c_str()); return; } /** @@ -5224,21 +5255,20 @@ void InputDispatcher::onAnrLocked(const Connection& connection) { * processes the events linearly. So providing information about the oldest entry seems to be * most useful. */ - DispatchEntry* oldestEntry = *connection.waitQueue.begin(); + DispatchEntry* oldestEntry = *connection->waitQueue.begin(); const nsecs_t currentWait = now() - oldestEntry->deliveryTime; std::string reason = android::base::StringPrintf("%s is not responding. Waited %" PRId64 "ms for %s", - connection.inputChannel->getName().c_str(), + connection->inputChannel->getName().c_str(), ns2ms(currentWait), oldestEntry->eventEntry->getDescription().c_str()); - sp<IBinder> connectionToken = connection.inputChannel->getConnectionToken(); + sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken(); updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason); - std::unique_ptr<CommandEntry> commandEntry = std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible); - commandEntry->connectionToken = connectionToken; - commandEntry->reason = std::move(reason); - postCommandLocked(std::move(commandEntry)); + processConnectionUnresponsiveLocked(*connection, std::move(reason)); + + // Stop waking up for events on this connection, it is already unresponsive + cancelEventsForAnrLocked(connection); } void InputDispatcher::onAnrLocked(std::shared_ptr<InputApplicationHandle> application) { @@ -5323,26 +5353,34 @@ void InputDispatcher::doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry mLock.lock(); } -void InputDispatcher::doNotifyConnectionUnresponsiveLockedInterruptible( - CommandEntry* commandEntry) { +void InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) { mLock.unlock(); - mPolicy->notifyConnectionUnresponsive(commandEntry->connectionToken, commandEntry->reason); + mPolicy->notifyWindowUnresponsive(commandEntry->connectionToken, commandEntry->reason); mLock.lock(); +} - // stop waking up for events in this connection, it is already not responding - sp<Connection> connection = getConnectionLocked(commandEntry->connectionToken); - if (connection == nullptr) { - return; - } - cancelEventsForAnrLocked(connection); +void InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyMonitorUnresponsive(commandEntry->pid, commandEntry->reason); + + mLock.lock(); +} + +void InputDispatcher::doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyWindowResponsive(commandEntry->connectionToken); + + mLock.lock(); } -void InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) { +void InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) { mLock.unlock(); - mPolicy->notifyConnectionResponsive(commandEntry->connectionToken); + mPolicy->notifyMonitorResponsive(commandEntry->pid); mLock.lock(); } @@ -5447,13 +5485,8 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c if (!connection->responsive) { connection->responsive = isConnectionResponsive(*connection); if (connection->responsive) { - // The connection was unresponsive, and now it's responsive. Tell the policy - // about it so that it can stop ANR. - std::unique_ptr<CommandEntry> connectionResponsiveCommand = - std::make_unique<CommandEntry>( - &InputDispatcher::doNotifyConnectionResponsiveLockedInterruptible); - connectionResponsiveCommand->connectionToken = connectionToken; - postCommandLocked(std::move(connectionResponsiveCommand)); + // The connection was unresponsive, and now it's responsive. + processConnectionResponsiveLocked(*connection); } } traceWaitQueueLength(connection); @@ -5469,6 +5502,82 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible(CommandEntry* c startDispatchCycleLocked(now(), connection); } +void InputDispatcher::sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) { + std::unique_ptr<CommandEntry> monitorUnresponsiveCommand = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyMonitorUnresponsiveLockedInterruptible); + monitorUnresponsiveCommand->pid = pid; + monitorUnresponsiveCommand->reason = std::move(reason); + postCommandLocked(std::move(monitorUnresponsiveCommand)); +} + +void InputDispatcher::sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, + std::string reason) { + std::unique_ptr<CommandEntry> windowUnresponsiveCommand = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyWindowUnresponsiveLockedInterruptible); + windowUnresponsiveCommand->connectionToken = std::move(connectionToken); + windowUnresponsiveCommand->reason = std::move(reason); + postCommandLocked(std::move(windowUnresponsiveCommand)); +} + +void InputDispatcher::sendMonitorResponsiveCommandLocked(int32_t pid) { + std::unique_ptr<CommandEntry> monitorResponsiveCommand = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyMonitorResponsiveLockedInterruptible); + monitorResponsiveCommand->pid = pid; + postCommandLocked(std::move(monitorResponsiveCommand)); +} + +void InputDispatcher::sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) { + std::unique_ptr<CommandEntry> windowResponsiveCommand = std::make_unique<CommandEntry>( + &InputDispatcher::doNotifyWindowResponsiveLockedInterruptible); + windowResponsiveCommand->connectionToken = std::move(connectionToken); + postCommandLocked(std::move(windowResponsiveCommand)); +} + +/** + * Tell the policy that a connection has become unresponsive so that it can start ANR. + * Check whether the connection of interest is a monitor or a window, and add the corresponding + * command entry to the command queue. + */ +void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection, + std::string reason) { + const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + if (connection.monitor) { + ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + reason.c_str()); + std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken); + if (!pid.has_value()) { + ALOGE("Could not find unresponsive monitor for connection %s", + connection.inputChannel->getName().c_str()); + return; + } + sendMonitorUnresponsiveCommandLocked(pid.value(), std::move(reason)); + return; + } + // If not a monitor, must be a window + ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + reason.c_str()); + sendWindowUnresponsiveCommandLocked(connectionToken, std::move(reason)); +} + +/** + * Tell the policy that a connection has become responsive so that it can stop ANR. + */ +void InputDispatcher::processConnectionResponsiveLocked(const Connection& connection) { + const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + if (connection.monitor) { + std::optional<int32_t> pid = findMonitorPidByTokenLocked(connectionToken); + if (!pid.has_value()) { + ALOGE("Could not find responsive monitor for connection %s", + connection.inputChannel->getName().c_str()); + return; + } + sendMonitorResponsiveCommandLocked(pid.value()); + return; + } + // If not a monitor, must be a window + sendWindowResponsiveCommandLocked(connectionToken); +} + bool InputDispatcher::afterKeyEventLockedInterruptible(const sp<Connection>& connection, DispatchEntry* dispatchEntry, KeyEntry& keyEntry, bool handled) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index df0be99854..c7299e9471 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -233,6 +233,8 @@ private: // Finds the display ID of the gesture monitor identified by the provided token. std::optional<int32_t> findGestureMonitorDisplayByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); + // Find a monitor pid by the provided token. + std::optional<int32_t> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock); @@ -433,6 +435,34 @@ private: void processNoFocusedWindowAnrLocked() REQUIRES(mLock); /** + * Tell policy about a window or a monitor that just became unresponsive. Starts ANR. + */ + void processConnectionUnresponsiveLocked(const Connection& connection, std::string reason) + REQUIRES(mLock); + /** + * Tell policy about a window or a monitor that just became responsive. + */ + void processConnectionResponsiveLocked(const Connection& connection) REQUIRES(mLock); + + /** + * Post `doNotifyMonitorUnresponsiveLockedInterruptible` command. + */ + void sendMonitorUnresponsiveCommandLocked(int32_t pid, std::string reason) REQUIRES(mLock); + /** + * Post `doNotifyWindowUnresponsiveLockedInterruptible` command. + */ + void sendWindowUnresponsiveCommandLocked(sp<IBinder> connectionToken, std::string reason) + REQUIRES(mLock); + /** + * Post `doNotifyMonitorResponsiveLockedInterruptible` command. + */ + void sendMonitorResponsiveCommandLocked(int32_t pid) REQUIRES(mLock); + /** + * Post `doNotifyWindowResponsiveLockedInterruptible` command. + */ + void sendWindowResponsiveCommandLocked(sp<IBinder> connectionToken) REQUIRES(mLock); + + /** * This map will store the pending focus requests that cannot be currently processed. This can * happen if the window requested to be focused is not currently visible. Such a window might * become visible later, and these requests would be processed at that time. @@ -577,7 +607,7 @@ private: int32_t displayId, std::string_view reason) REQUIRES(mLock); void notifyFocusChangedLocked(const sp<IBinder>& oldFocus, const sp<IBinder>& newFocus) REQUIRES(mLock); - void onAnrLocked(const Connection& connection) REQUIRES(mLock); + void onAnrLocked(const sp<Connection>& connection) REQUIRES(mLock); void onAnrLocked(std::shared_ptr<InputApplicationHandle> application) REQUIRES(mLock); void onUntrustedTouchLocked(const std::string& obscuringPackage) REQUIRES(mLock); void updateLastAnrStateLocked(const sp<InputWindowHandle>& window, const std::string& reason) @@ -592,11 +622,13 @@ private: REQUIRES(mLock); void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + // ANR-related callbacks - start void doNotifyNoFocusedWindowAnrLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); - void doNotifyConnectionUnresponsiveLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); - void doNotifyConnectionResponsiveLockedInterruptible(CommandEntry* commandEntry) - REQUIRES(mLock); + void doNotifyWindowUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyMonitorUnresponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyWindowResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyMonitorResponsiveLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + // ANR-related callbacks - end void doNotifySensorLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doNotifyUntrustedTouchLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index d9ec020a2b..b976129773 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -50,20 +50,31 @@ public: virtual void notifyNoFocusedWindowAnr( const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0; - /* Notifies the system that a connection just became unresponsive. This indicates that ANR - * should be raised for this connection. The connection is identified via token. + /* Notifies the system that a window just became unresponsive. This indicates that ANR + * should be raised for this window. The window is identified via token. * The string reason contains information about the input event that we haven't received * a response for. */ - virtual void notifyConnectionUnresponsive(const sp<IBinder>& token, - const std::string& reason) = 0; + virtual void notifyWindowUnresponsive(const sp<IBinder>& token, const std::string& reason) = 0; + /* Notifies the system that a monitor just became unresponsive. This indicates that ANR + * should be raised for this monitor. The monitor is identified via its pid. + * The string reason contains information about the input event that we haven't received + * a response for. + */ + virtual void notifyMonitorUnresponsive(int32_t pid, const std::string& reason) = 0; - /* Notifies the system that a connection just became responsive. This is only called after the - * connection was first marked "unresponsive". This indicates that ANR dialog (if any) should - * no longer should be shown to the user. The connection is eligible to cause a new ANR in the + /* Notifies the system that a window just became responsive. This is only called after the + * window was first marked "unresponsive". This indicates that ANR dialog (if any) should + * no longer should be shown to the user. The window is eligible to cause a new ANR in the + * future. + */ + virtual void notifyWindowResponsive(const sp<IBinder>& token) = 0; + /* Notifies the system that a monitor just became responsive. This is only called after the + * monitor was first marked "unresponsive". This indicates that ANR dialog (if any) should + * no longer should be shown to the user. The monitor is eligible to cause a new ANR in the * future. */ - virtual void notifyConnectionResponsive(const sp<IBinder>& token) = 0; + virtual void notifyMonitorResponsive(int32_t pid) = 0; /* Notifies the system that an input channel is unrecoverably broken. */ virtual void notifyInputChannelBroken(const sp<IBinder>& token) = 0; diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index ac72ac4189..3e6910d420 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -54,10 +54,11 @@ bool InputDevice::isEnabled() { if (!hasEventHubDevices()) { return false; } - // devices are either all enabled or all disabled, so we only need to check the first - auto& devicePair = mDevices.begin()->second; - auto& contextPtr = devicePair.first; - return contextPtr->isDeviceEnabled(); + // An input device composed of sub devices can be individually enabled or disabled. + // If any of the sub device is enabled then the input device is considered as enabled. + bool enabled = false; + for_each_subdevice([&enabled](auto& context) { enabled |= context.isDeviceEnabled(); }); + return enabled; } void InputDevice::setEnabled(bool enabled, nsecs_t when) { @@ -504,8 +505,7 @@ void InputDevice::bumpGeneration() { } void InputDevice::notifyReset(nsecs_t when) { - NotifyDeviceResetArgs args(mContext->getNextId(), when, mId); - mContext->getListener()->notifyDeviceReset(&args); + mContext->notifyDeviceReset(when, mId); } std::optional<int32_t> InputDevice::getAssociatedDisplayId() { diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index be21ace221..7c448e4efa 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -222,7 +222,9 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { } // Sensor input device is noisy, to save power disable it by default. - if (device->getClasses().test(InputDeviceClass::SENSOR)) { + // Input device is classified as SENSOR when any sub device is a SENSOR device, check Eventhub + // device class to disable SENSOR sub device only. + if (mEventHub->getDeviceClasses(eventHubId).test(InputDeviceClass::SENSOR)) { mEventHub->disableDevice(eventHubId); } } @@ -337,8 +339,7 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { updateGlobalMetaStateLocked(); // Enqueue configuration changed. - NotifyConfigurationChangedArgs args(mContext.getNextId(), when); - mQueuedListener->notifyConfigurationChanged(&args); + mContext.notifyConfigurationChanged(when); } void InputReader::refreshConfigurationLocked(uint32_t changes) { @@ -365,9 +366,7 @@ void InputReader::refreshConfigurationLocked(uint32_t changes) { } if (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE) { - const NotifyPointerCaptureChangedArgs args(mContext.getNextId(), now, - mConfig.pointerCapture); - mQueuedListener->notifyPointerCaptureChanged(&args); + mContext.notifyPointerCaptureChanged(now, mConfig.pointerCapture); } } @@ -866,16 +865,64 @@ InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } -InputListenerInterface* InputReader::ContextImpl::getListener() { - return mReader->mQueuedListener.get(); -} - EventHubInterface* InputReader::ContextImpl::getEventHub() { return mReader->mEventHub.get(); } -int32_t InputReader::ContextImpl::getNextId() { - return mIdGenerator.nextId(); +void InputReader::ContextImpl::notifyConfigurationChanged(nsecs_t when) { + NotifyConfigurationChangedArgs args(mIdGenerator.nextId(), when); + mReader->mQueuedListener->notifyConfigurationChanged(&args); +} + +void InputReader::ContextImpl::notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, nsecs_t downTime) { + NotifyKeyArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, policyFlags, + action, flags, keyCode, scanCode, metaState, downTime); + mReader->mQueuedListener->notifyKey(&args); +} +void InputReader::ContextImpl::notifyMotion( + nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames) { + NotifyMotionArgs args(mIdGenerator.nextId(), eventTime, deviceId, source, displayId, + policyFlags, action, actionButton, flags, metaState, buttonState, + classification, edgeFlags, pointerCount, pointerProperties, pointerCoords, + xPrecision, yPrecision, xCursorPosition, yCursorPosition, downTime, + videoFrames); + mReader->mQueuedListener->notifyMotion(&args); +} + +void InputReader::ContextImpl::notifySensor(nsecs_t when, int32_t deviceId, + InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, + bool accuracyChanged, nsecs_t timestamp, + std::vector<float> values) { + NotifySensorArgs args(mIdGenerator.nextId(), when, deviceId, AINPUT_SOURCE_SENSOR, sensorType, + accuracy, accuracyChanged, timestamp, std::move(values)); + mReader->mQueuedListener->notifySensor(&args); +} + +void InputReader::ContextImpl::notifySwitch(nsecs_t eventTime, uint32_t switchValues, + uint32_t switchMask) { + NotifySwitchArgs args(mIdGenerator.nextId(), eventTime, 0 /*policyFlags*/, switchValues, + switchMask); + mReader->mQueuedListener->notifySwitch(&args); +} + +void InputReader::ContextImpl::notifyDeviceReset(nsecs_t when, int32_t deviceId) { + NotifyDeviceResetArgs args(mIdGenerator.nextId(), when, deviceId); + mReader->mQueuedListener->notifyDeviceReset(&args); +} + +void InputReader::ContextImpl::notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) { + const NotifyPointerCaptureChangedArgs args(mIdGenerator.nextId(), when, hasCapture); + mReader->mQueuedListener->notifyPointerCaptureChanged(&args); } } // namespace android diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 48d4596383..7be932a458 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -127,11 +127,30 @@ protected: void dispatchExternalStylusState(const StylusState& outState) NO_THREAD_SAFETY_ANALYSIS override; InputReaderPolicyInterface* getPolicy() NO_THREAD_SAFETY_ANALYSIS override; - InputListenerInterface* getListener() NO_THREAD_SAFETY_ANALYSIS override; EventHubInterface* getEventHub() NO_THREAD_SAFETY_ANALYSIS override; - int32_t getNextId() NO_THREAD_SAFETY_ANALYSIS override; void updateLedMetaState(int32_t metaState) NO_THREAD_SAFETY_ANALYSIS override; int32_t getLedMetaState() NO_THREAD_SAFETY_ANALYSIS override; + + // Send events to InputListener interface + void notifyConfigurationChanged(nsecs_t when) override; + void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, nsecs_t downTime) override; + void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, + uint32_t pointerCount, const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, float yPrecision, + float xCursorPosition, float yCursorPosition, nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames) override; + void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) override; + void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, bool accuracyChanged, + nsecs_t timestamp, std::vector<float> values) override; + void notifyDeviceReset(nsecs_t when, int32_t deviceId) override; + void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) override; + } mContext; friend class ContextImpl; diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index dc807f7886..edab3128aa 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -55,13 +55,33 @@ public: virtual void dispatchExternalStylusState(const StylusState& outState) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual InputListenerInterface* getListener() = 0; virtual EventHubInterface* getEventHub() = 0; - virtual int32_t getNextId() = 0; - virtual void updateLedMetaState(int32_t metaState) = 0; virtual int32_t getLedMetaState() = 0; + + // Send events to InputListener interface + + virtual void notifyConfigurationChanged(nsecs_t when) = 0; + virtual void notifyKey(nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, + int32_t scanCode, int32_t metaState, nsecs_t downTime) = 0; + virtual void notifyMotion(nsecs_t eventTime, int32_t deviceId, uint32_t source, + int32_t displayId, uint32_t policyFlags, int32_t action, + int32_t actionButton, int32_t flags, int32_t metaState, + int32_t buttonState, MotionClassification classification, + int32_t edgeFlags, uint32_t pointerCount, + const PointerProperties* pointerProperties, + const PointerCoords* pointerCoords, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, + const std::vector<TouchVideoFrame>& videoFrames) = 0; + virtual void notifySwitch(nsecs_t eventTime, uint32_t switchValues, uint32_t switchMask) = 0; + virtual void notifySensor(nsecs_t when, int32_t deviceId, InputDeviceSensorType sensorType, + InputDeviceSensorAccuracy accuracy, bool accuracyChanged, + nsecs_t timestamp, std::vector<float> values) = 0; + virtual void notifyDeviceReset(nsecs_t when, int32_t deviceId) = 0; + virtual void notifyPointerCaptureChanged(nsecs_t when, bool hasCapture) = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 254b64b4d9..7f7b33cc3e 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -175,8 +175,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } bumpGeneration(); if (changes) { - NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getContext()->notifyDeviceReset(when, getDeviceId()); } } @@ -383,40 +382,35 @@ void CursorInputMapper::sync(nsecs_t when) { while (!released.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; - NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, getDeviceId(), - mSource, displayId, policyFlags, - AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, - metaState, buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&releaseArgs); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, + metaState, buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); } } - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, - xCursorPosition, yCursorPosition, downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + motionEventAction, 0, 0, metaState, currentButtonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, + xCursorPosition, yCursorPosition, downTime, + /* videoFrames */ {}); if (buttonsPressed) { BitSet32 pressed(buttonsPressed); while (!pressed.isEmpty()) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; - NotifyMotionArgs pressArgs(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&pressArgs); } } @@ -424,13 +418,12 @@ void CursorInputMapper::sync(nsecs_t when) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { - NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, - 0, metaState, currentButtonState, MotionClassification::NONE, + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&hoverArgs); } // Send scroll events. @@ -438,13 +431,12 @@ void CursorInputMapper::sync(nsecs_t when) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, currentButtonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, - yCursorPosition, downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + currentButtonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, + yCursorPosition, downTime, /* videoFrames */ {}); } } diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h index bd64d8d0a8..6ca6ec9a73 100644 --- a/services/inputflinger/reader/mapper/InputMapper.h +++ b/services/inputflinger/reader/mapper/InputMapper.h @@ -19,7 +19,6 @@ #include "EventHub.h" #include "InputDevice.h" -#include "InputListener.h" #include "InputReaderContext.h" #include "StylusState.h" #include "VibrationElement.h" @@ -48,7 +47,6 @@ public: inline const std::string getDeviceName() { return mDeviceContext.getName(); } inline InputReaderContext* getContext() { return mDeviceContext.getContext(); } inline InputReaderPolicyInterface* getPolicy() { return getContext()->getPolicy(); } - inline InputListenerInterface* getListener() { return getContext()->getListener(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp index abd8aa9119..ac4669c71e 100644 --- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp +++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp @@ -337,13 +337,12 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, - ADISPLAY_ID_NONE, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &pointerProperties, &pointerCoords, 0, 0, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, ADISPLAY_ID_NONE, + policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); } void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 8b9f235438..03d740529f 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -350,10 +350,9 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t scanCode, policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT; } - NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, getDisplayId(), - policyFlags, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, - AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); - getListener()->notifyKey(&args); + getContext()->notifyKey(when, getDeviceId(), mSource, getDisplayId(), policyFlags, + down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, + AKEY_EVENT_FLAG_FROM_SYSTEM, keyCode, scanCode, keyMetaState, downTime); } ssize_t KeyboardInputMapper::findKeyDown(int32_t scanCode) { diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp index 594ff42f87..3f8a36433f 100644 --- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp +++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp @@ -121,13 +121,12 @@ void RotaryEncoderInputMapper::sync(nsecs_t when) { int32_t metaState = getContext()->getGlobalMetaState(); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_SCROLL, scroll * mScalingFactor); - NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, - metaState, /* buttonState */ 0, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, - &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); - getListener()->notifyMotion(&scrollArgs); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + /* buttonState */ 0, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, + &pointerCoords, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, 0, /* videoFrames */ {}); } mRotaryEncoderScrollAccumulator.finishSync(); diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index 7ac2dec895..68c1e40357 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -405,13 +405,10 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { // Convert to Android unit convertFromLinuxToAndroid(values, sensorType); // Notify dispatcher for sensor event - NotifySensorArgs args(getContext()->getNextId(), when, getDeviceId(), - AINPUT_SOURCE_SENSOR, sensorType, sensor.sensorInfo.accuracy, - sensor.accuracy != - sensor.sensorInfo.accuracy /* accuracyChanged */, - timestamp /* hwTimestamp */, values); - - getListener()->notifySensor(&args); + getContext()->notifySensor(when, getDeviceId(), sensorType, sensor.sensorInfo.accuracy, + sensor.accuracy != + sensor.sensorInfo.accuracy /* accuracyChanged */, + timestamp /* hwTimestamp */, std::move(values)); sensor.lastSampleTimeNs = timestamp; sensor.accuracy = sensor.sensorInfo.accuracy; } diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp index 4f736810bc..07de244c31 100644 --- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp @@ -56,10 +56,7 @@ void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { void SwitchInputMapper::sync(nsecs_t when) { if (mUpdatedSwitchMask) { uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask; - NotifySwitchArgs args(getContext()->getNextId(), when, 0 /*policyFlags*/, - updatedSwitchValues, mUpdatedSwitchMask); - getListener()->notifySwitch(&args); - + getContext()->notifySwitch(when, updatedSwitchValues, mUpdatedSwitchMask); mUpdatedSwitchMask = 0; } } diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index a86443dee1..ff6341fbdb 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -66,9 +66,8 @@ static void synthesizeButtonKey(InputReaderContext* context, int32_t action, nse (currentButtonState & buttonState)) || (action == AKEY_EVENT_ACTION_UP && (lastButtonState & buttonState) && !(currentButtonState & buttonState))) { - NotifyKeyArgs args(context->getNextId(), when, deviceId, source, displayId, policyFlags, - action, 0, keyCode, 0, context->getGlobalMetaState(), when); - context->getListener()->notifyKey(&args); + context->notifyKey(when, deviceId, source, displayId, policyFlags, action, 0 /*flags*/, + keyCode, 0 /*scanCode*/, context->getGlobalMetaState(), when); } } diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ce12c27470..fb30b88b8d 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -397,8 +397,7 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c if (changes && resetNeeded) { // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. - NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); - getListener()->notifyDeviceReset(&args); + getContext()->notifyDeviceReset(when, getDeviceId()); } } @@ -1829,10 +1828,9 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, uint32_t policyFlags, int32_t metaState = getContext()->getGlobalMetaState(); policyFlags |= POLICY_FLAG_VIRTUAL; - NotifyKeyArgs args(getContext()->getNextId(), when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, - mViewport.displayId, policyFlags, keyEventAction, keyEventFlags, keyCode, - scanCode, metaState, downTime); - getListener()->notifyKey(&args); + getContext()->notifyKey(when, getDeviceId(), AINPUT_SOURCE_KEYBOARD, mViewport.displayId, + policyFlags, keyEventAction, keyEventFlags, keyCode, scanCode, + metaState, downTime); } void TouchInputMapper::abortTouches(nsecs_t when, uint32_t policyFlags) { @@ -2508,12 +2506,11 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); const int32_t displayId = mPointerController->getDisplayId(); - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerProperties, &pointerCoords, 0, 0, x, y, - mPointerGesture.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, buttonState, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &pointerProperties, &pointerCoords, 0, 0, x, y, + mPointerGesture.downTime, /* videoFrames */ {}); } // Update state. @@ -3428,28 +3425,28 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.down = false; // Send up. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_UP, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_UP, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, + /* videoFrames */ {}); } if (mPointerSimple.hovering && !hovering) { mPointerSimple.hovering = false; // Send hover exit. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, - mLastRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, - &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_EXIT, 0, 0, metaState, + mLastRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.lastProperties, + &mPointerSimple.lastCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, + /* videoFrames */ {}); } if (down) { @@ -3458,25 +3455,24 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.downTime = when; // Send down. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_DOWN, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); } // Send move. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); } if (hovering) { @@ -3484,25 +3480,24 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, mPointerSimple.hovering = true; // Send hover enter. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, - displayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, - metaState, mCurrentRawState.buttonState, - MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, - &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, - mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, - yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_ENTER, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, + &mPointerSimple.currentCoords, mOrientedXPrecision, + mOrientedYPrecision, xCursorPosition, yCursorPosition, + mPointerSimple.downTime, /* videoFrames */ {}); } // Send hover move. - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &mPointerSimple.currentCoords, mOrientedXPrecision, - mOrientedYPrecision, xCursorPosition, yCursorPosition, - mPointerSimple.downTime, /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, /* videoFrames */ {}); } if (mCurrentRawState.rawVScroll || mCurrentRawState.rawHScroll) { @@ -3517,14 +3512,14 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); - NotifyMotionArgs args(getContext()->getNextId(), when, getDeviceId(), mSource, displayId, - policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, - mCurrentRawState.buttonState, MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, 1, &mPointerSimple.currentProperties, - &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, - xCursorPosition, yCursorPosition, mPointerSimple.downTime, - /* videoFrames */ {}); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, getDeviceId(), mSource, displayId, policyFlags, + AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, + mCurrentRawState.buttonState, MotionClassification::NONE, + AMOTION_EVENT_EDGE_FLAG_NONE, 1, + &mPointerSimple.currentProperties, &pointerCoords, + mOrientedXPrecision, mOrientedYPrecision, xCursorPosition, + yCursorPosition, mPointerSimple.downTime, + /* videoFrames */ {}); } // Save state. @@ -3595,12 +3590,11 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames(); std::for_each(frames.begin(), frames.end(), [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); - NotifyMotionArgs args(getContext()->getNextId(), when, deviceId, source, displayId, policyFlags, - action, actionButton, flags, metaState, buttonState, - MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties, - pointerCoords, xPrecision, yPrecision, xCursorPosition, yCursorPosition, - downTime, std::move(frames)); - getListener()->notifyMotion(&args); + getContext()->notifyMotion(when, deviceId, source, displayId, policyFlags, action, actionButton, + flags, metaState, buttonState, MotionClassification::NONE, edgeFlags, + pointerCount, pointerProperties, pointerCoords, xPrecision, + yPrecision, xCursorPosition, yCursorPosition, downTime, + std::move(frames)); } bool TouchInputMapper::updateMovedPointers(const PointerProperties* inProperties, @@ -3644,6 +3638,9 @@ void TouchInputMapper::rotateAndScale(float& x, float& y) { const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale; const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale; + const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale; + const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale; + // Rotate to surface coordinate. // 0 - no swap and reverse. // 90 - swap x/y and reverse y. @@ -3655,16 +3652,16 @@ void TouchInputMapper::rotateAndScale(float& x, float& y) { y = yScaled + mYTranslate; break; case DISPLAY_ORIENTATION_90: - y = mSurfaceRight - xScaled; + y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight); x = yScaled + mYTranslate; break; case DISPLAY_ORIENTATION_180: - x = mSurfaceRight - xScaled; - y = mSurfaceBottom - yScaled; + x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight); + y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom); break; case DISPLAY_ORIENTATION_270: y = xScaled + mXTranslate; - x = mSurfaceBottom - yScaled; + x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom); break; default: assert(false); diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 6dbc88f4d6..c7e05eb86a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -140,27 +140,49 @@ public: ASSERT_EQ(expectedApplication, application); } - void assertNotifyConnectionUnresponsiveWasCalled(std::chrono::nanoseconds timeout, - const sp<IBinder>& expectedConnectionToken) { - sp<IBinder> connectionToken = getUnresponsiveConnectionToken(timeout); + void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, + const sp<IBinder>& expectedConnectionToken) { + sp<IBinder> connectionToken = getUnresponsiveWindowToken(timeout); ASSERT_EQ(expectedConnectionToken, connectionToken); } - void assertNotifyConnectionResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) { - sp<IBinder> connectionToken = getResponsiveConnectionToken(); + void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedConnectionToken) { + sp<IBinder> connectionToken = getResponsiveWindowToken(); ASSERT_EQ(expectedConnectionToken, connectionToken); } - sp<IBinder> getUnresponsiveConnectionToken(std::chrono::nanoseconds timeout) { + void assertNotifyMonitorUnresponsiveWasCalled(std::chrono::nanoseconds timeout) { + int32_t pid = getUnresponsiveMonitorPid(timeout); + ASSERT_EQ(MONITOR_PID, pid); + } + + void assertNotifyMonitorResponsiveWasCalled() { + int32_t pid = getResponsiveMonitorPid(); + ASSERT_EQ(MONITOR_PID, pid); + } + + sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + return getAnrTokenLockedInterruptible(timeout, mAnrWindowTokens, lock); + } + + sp<IBinder> getResponsiveWindowToken() { + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLocked(mLock); + return getAnrTokenLockedInterruptible(0s, mResponsiveWindowTokens, lock); + } + + int32_t getUnresponsiveMonitorPid(std::chrono::nanoseconds timeout) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); - return getAnrTokenLockedInterruptible(timeout, mAnrConnectionTokens, lock); + return getAnrTokenLockedInterruptible(timeout, mAnrMonitorPids, lock); } - sp<IBinder> getResponsiveConnectionToken() { + int32_t getResponsiveMonitorPid() { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); - return getAnrTokenLockedInterruptible(0s, mResponsiveConnectionTokens, lock); + return getAnrTokenLockedInterruptible(0s, mResponsiveMonitorPids, lock); } // All three ANR-related callbacks behave the same way, so we use this generic function to wait @@ -182,7 +204,7 @@ public: const std::chrono::duration waited = std::chrono::steady_clock::now() - start; if (storage.empty()) { ADD_FAILURE() << "Did not receive the ANR callback"; - return nullptr; + return {}; } // Ensure that the ANR didn't get raised too early. We can't be too strict here because // the dispatcher started counting before this function was called @@ -201,10 +223,14 @@ public: void assertNotifyAnrWasNotCalled() { std::scoped_lock lock(mLock); ASSERT_TRUE(mAnrApplications.empty()); - ASSERT_TRUE(mAnrConnectionTokens.empty()); - ASSERT_TRUE(mResponsiveConnectionTokens.empty()) + ASSERT_TRUE(mAnrWindowTokens.empty()); + ASSERT_TRUE(mAnrMonitorPids.empty()); + ASSERT_TRUE(mResponsiveWindowTokens.empty()) << "ANR was not called, but please also consume the 'connection is responsive' " "signal"; + ASSERT_TRUE(mResponsiveMonitorPids.empty()) + << "Monitor ANR was not called, but please also consume the 'monitor is responsive'" + " signal"; } void setKeyRepeatConfiguration(nsecs_t timeout, nsecs_t delay) { @@ -251,8 +277,10 @@ private: // ANR handling std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock); - std::queue<sp<IBinder>> mAnrConnectionTokens GUARDED_BY(mLock); - std::queue<sp<IBinder>> mResponsiveConnectionTokens GUARDED_BY(mLock); + std::queue<sp<IBinder>> mAnrWindowTokens GUARDED_BY(mLock); + std::queue<sp<IBinder>> mResponsiveWindowTokens GUARDED_BY(mLock); + std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock); + std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock); std::condition_variable mNotifyAnr; void notifyConfigurationChanged(nsecs_t when) override { @@ -260,16 +288,27 @@ private: mConfigurationChangedTime = when; } - void notifyConnectionUnresponsive(const sp<IBinder>& connectionToken, - const std::string&) override { + void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, const std::string&) override { + std::scoped_lock lock(mLock); + mAnrWindowTokens.push(connectionToken); + mNotifyAnr.notify_all(); + } + + void notifyMonitorUnresponsive(int32_t pid, const std::string&) override { std::scoped_lock lock(mLock); - mAnrConnectionTokens.push(connectionToken); + mAnrMonitorPids.push(pid); mNotifyAnr.notify_all(); } - void notifyConnectionResponsive(const sp<IBinder>& connectionToken) override { + void notifyWindowResponsive(const sp<IBinder>& connectionToken) override { std::scoped_lock lock(mLock); - mResponsiveConnectionTokens.push(connectionToken); + mResponsiveWindowTokens.push(connectionToken); + mNotifyAnr.notify_all(); + } + + void notifyMonitorResponsive(int32_t pid) override { + std::scoped_lock lock(mLock); + mResponsiveMonitorPids.push(pid); mNotifyAnr.notify_all(); } @@ -1962,11 +2001,10 @@ TEST_F(InputDispatcherTest, UnresponsiveGestureMonitor_GetsAnr) { std::optional<uint32_t> consumeSeq = monitor.receiveEvent(); ASSERT_TRUE(consumeSeq); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(DISPATCHING_TIMEOUT, - monitor.getToken()); + mFakePolicy->assertNotifyMonitorUnresponsiveWasCalled(DISPATCHING_TIMEOUT); monitor.finishEvent(*consumeSeq); ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(monitor.getToken()); + mFakePolicy->assertNotifyMonitorResponsiveWasCalled(); } TEST_F(InputDispatcherTest, TestMoveEvent) { @@ -3194,13 +3232,13 @@ TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) { std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); // ACTION_DOWN ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); mWindow->finishEvent(*sequenceNum); mWindow->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, ADISPLAY_ID_DEFAULT, 0 /*flags*/); ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); } // Send a key to the app and have the app not respond right away. @@ -3210,7 +3248,7 @@ TEST_F(InputDispatcherSingleWindowAnr, OnKeyDown_BasicAnr) { std::optional<uint32_t> sequenceNum = mWindow->receiveEvent(); ASSERT_TRUE(sequenceNum); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); ASSERT_TRUE(mDispatcher->waitForIdle()); } @@ -3315,7 +3353,7 @@ TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) // We have now sent down and up. Let's consume first event and then ANR on the second. mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); } // If an app is not responding to a key event, gesture monitors should continue to receive @@ -3332,7 +3370,7 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); // New tap will go to the gesture monitor, but not to the window tapOnWindow(); @@ -3341,7 +3379,7 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); monitor.assertNoEvents(); } @@ -3360,7 +3398,7 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr mWindow->consumeMotionDown(); // Stuck on the ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); // New tap will go to the gesture monitor, but not to the window tapOnWindow(); @@ -3369,7 +3407,7 @@ TEST_F(InputDispatcherSingleWindowAnr, GestureMonitors_ReceiveEventsDuringAppAnr mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion mDispatcher->waitForIdle(); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); monitor.assertNoEvents(); } @@ -3386,19 +3424,19 @@ TEST_F(InputDispatcherSingleWindowAnr, SameWindow_CanReceiveAnrTwice) { mWindow->consumeMotionDown(); // Block on ACTION_UP const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); mWindow->consumeMotionUp(); // Now the connection should be healthy again mDispatcher->waitForIdle(); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mWindow->assertNoEvents(); tapOnWindow(); mWindow->consumeMotionDown(); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken()); mWindow->consumeMotionUp(); mDispatcher->waitForIdle(); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mFakePolicy->assertNotifyAnrWasNotCalled(); mWindow->assertNoEvents(); } @@ -3411,7 +3449,7 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { WINDOW_LOCATION)); const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(windowTimeout, mWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow->getToken()); std::this_thread::sleep_for(windowTimeout); // 'notifyConnectionUnresponsive' should only be called once per connection mFakePolicy->assertNotifyAnrWasNotCalled(); @@ -3421,7 +3459,7 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { ADISPLAY_ID_DEFAULT, 0 /*flags*/); mWindow->assertNoEvents(); mDispatcher->waitForIdle(); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken()); mFakePolicy->assertNotifyAnrWasNotCalled(); } @@ -3589,7 +3627,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { const std::chrono::duration timeout = mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); // Because we injected two DOWN events in a row, CANCEL is enqueued for the first event // sequence to make it consistent mFocusedWindow->consumeMotionCancel(); @@ -3600,7 +3638,7 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsive) { mFocusedWindow->assertNoEvents(); mUnfocusedWindow->assertNoEvents(); ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken()); mFakePolicy->assertNotifyAnrWasNotCalled(); } @@ -3614,8 +3652,8 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout tapOnFocusedWindow(); // we should have ACTION_DOWN/ACTION_UP on focused window and ACTION_OUTSIDE on unfocused window - sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveConnectionToken(10ms); - sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveConnectionToken(0ms); + sp<IBinder> anrConnectionToken1 = mFakePolicy->getUnresponsiveWindowToken(10ms); + sp<IBinder> anrConnectionToken2 = mFakePolicy->getUnresponsiveWindowToken(0ms); // We don't know which window will ANR first. But both of them should happen eventually. ASSERT_TRUE(mFocusedWindow->getToken() == anrConnectionToken1 || @@ -3630,8 +3668,8 @@ TEST_F(InputDispatcherMultiWindowAnr, TwoWindows_BothUnresponsiveWithSameTimeout mFocusedWindow->consumeMotionUp(); mUnfocusedWindow->consumeMotionOutside(); - sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveConnectionToken(); - sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveConnectionToken(); + sp<IBinder> responsiveToken1 = mFakePolicy->getResponsiveWindowToken(); + sp<IBinder> responsiveToken2 = mFakePolicy->getResponsiveWindowToken(); // Both applications should be marked as responsive, in any order ASSERT_TRUE(mFocusedWindow->getToken() == responsiveToken1 || @@ -3655,7 +3693,7 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { ASSERT_TRUE(upEventSequenceNum); const std::chrono::duration timeout = mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); // Tap once again // We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success @@ -3676,7 +3714,7 @@ TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) { // The second tap did not go to the focused window mFocusedWindow->assertNoEvents(); // Since all events are finished, connection should be deemed healthy again - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken()); mFakePolicy->assertNotifyAnrWasNotCalled(); } @@ -3788,7 +3826,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { const std::chrono::duration timeout = mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); - mFakePolicy->assertNotifyConnectionUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mFocusedWindow->getToken()); mUnfocusedWindow->consumeMotionDown(); mFocusedWindow->consumeMotionDown(); @@ -3807,7 +3845,7 @@ TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) { ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionEvent.getAction()); } ASSERT_TRUE(mDispatcher->waitForIdle()); - mFakePolicy->assertNotifyConnectionResponsiveWasCalled(mFocusedWindow->getToken()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mFocusedWindow->getToken()); mUnfocusedWindow->assertNoEvents(); mFocusedWindow->assertNoEvents(); @@ -4172,6 +4210,7 @@ TEST_F(InputDispatcherPointerCaptureTests, UnexpectedStateChangeDisablesPointerC notifyPointerCaptureChanged(true); // Ensure that Pointer Capture is disabled. + mFakePolicy->waitForSetPointerCapture(false); mWindow->consumeCaptureEvent(false); mWindow->assertNoEvents(); } diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 23f30260bc..91d5864670 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -1481,6 +1481,32 @@ TEST_F(InputReaderTest, GetMergedInputDevices) { ASSERT_EQ(1U, mReader->getInputDevices().size()); } +TEST_F(InputReaderTest, GetMergedInputDevicesEnabled) { + constexpr int32_t deviceId = END_RESERVED_ID + 1000; + constexpr int32_t eventHubIds[2] = {END_RESERVED_ID, END_RESERVED_ID + 1}; + // Add two subdevices to device + std::shared_ptr<InputDevice> device = mReader->newDevice(deviceId, "fake"); + // Must add at least one mapper or the device will be ignored! + device->addMapper<FakeInputMapper>(eventHubIds[0], AINPUT_SOURCE_KEYBOARD); + device->addMapper<FakeInputMapper>(eventHubIds[1], AINPUT_SOURCE_KEYBOARD); + + // Push same device instance for next device to be added, so they'll have same identifier. + mReader->pushNextDevice(device); + mReader->pushNextDevice(device); + // Sensor device is initially disabled + ASSERT_NO_FATAL_FAILURE(addDevice(eventHubIds[0], "fake1", + InputDeviceClass::KEYBOARD | InputDeviceClass::SENSOR, + nullptr)); + // Device is disabled because the only sub device is a sensor device and disabled initially. + ASSERT_FALSE(mFakeEventHub->isDeviceEnabled(eventHubIds[0])); + ASSERT_FALSE(device->isEnabled()); + ASSERT_NO_FATAL_FAILURE( + addDevice(eventHubIds[1], "fake2", InputDeviceClass::KEYBOARD, nullptr)); + // The merged device is enabled if any sub device is enabled + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(eventHubIds[1])); + ASSERT_TRUE(device->isEnabled()); +} + TEST_F(InputReaderTest, WhenEnabledChanges_SendsDeviceResetNotification) { constexpr int32_t deviceId = END_RESERVED_ID + 1000; constexpr Flags<InputDeviceClass> deviceClass(InputDeviceClass::KEYBOARD); @@ -2721,6 +2747,7 @@ TEST_F(SensorInputMapperTest, ProcessAccelerometerSensor) { ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::ACCELEROMETER, std::chrono::microseconds(10000), std::chrono::microseconds(0))); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_X, 20000); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Y, -20000); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_Z, 40000); @@ -2750,6 +2777,7 @@ TEST_F(SensorInputMapperTest, ProcessGyroscopeSensor) { ASSERT_TRUE(mapper.enableSensor(InputDeviceSensorType::GYROSCOPE, std::chrono::microseconds(10000), std::chrono::microseconds(0))); + ASSERT_TRUE(mFakeEventHub->isDeviceEnabled(EVENTHUB_ID)); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RX, 20000); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RY, -20000); process(mapper, ARBITRARY_TIME, EV_ABS, ABS_RZ, 40000); @@ -8017,6 +8045,34 @@ TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_270) { processPositionAndVerify(mapper, x - 1, y, x + 1, y, xExpected, yExpected); } +TEST_F(MultiTouchInputMapperTest_SurfaceRange, Viewports_SurfaceRange_Corner) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareAxes(POSITION); + MultiTouchInputMapper& mapper = addMapperAndConfigure<MultiTouchInputMapper>(); + + const int32_t x = 0; + const int32_t y = 0; + + const int32_t xExpected = x; + const int32_t yExpected = y; + processPositionAndVerify(mapper, x - 1, y, x, y, xExpected, yExpected); + + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_90); + // expect x/y = swap x/y then reverse y. + const int32_t xExpected90 = y; + const int32_t yExpected90 = DISPLAY_WIDTH - 1; + processPositionAndVerify(mapper, x - 1, y, x, y, xExpected90, yExpected90); + + clearViewports(); + prepareDisplay(DISPLAY_ORIENTATION_270); + // expect x/y = swap x/y then reverse x. + const int32_t xExpected270 = DISPLAY_HEIGHT - 1; + const int32_t yExpected270 = x; + processPositionAndVerify(mapper, x - 1, y, x, y, xExpected270, yExpected270); +} + TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) { // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0) std::shared_ptr<FakePointerController> fakePointerController = diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp index 4a711ca80b..2f32827367 100644 --- a/services/powermanager/PowerHalWrapper.cpp +++ b/services/powermanager/PowerHalWrapper.cpp @@ -114,14 +114,15 @@ HalResult HidlHalWrapperV1_1::sendPowerHint(V1_0::PowerHint hintId, uint32_t dat HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { std::unique_lock<std::mutex> lock(mBoostMutex); + size_t idx = static_cast<size_t>(boost); + // Quick return if boost is not supported by HAL - if (boost > Boost::DISPLAY_UPDATE_IMMINENT || - mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::OFF) { + if (idx >= mBoostSupportedArray.size() || mBoostSupportedArray[idx] == HalSupport::OFF) { ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); return HalResult::UNSUPPORTED; } - if (mBoostSupportedArray[static_cast<int32_t>(boost)] == HalSupport::UNKNOWN) { + if (mBoostSupportedArray[idx] == HalSupport::UNKNOWN) { bool isSupported = false; auto isSupportedRet = mHandle->isBoostSupported(boost, &isSupported); if (!isSupportedRet.isOk()) { @@ -130,8 +131,7 @@ HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { return HalResult::FAILED; } - mBoostSupportedArray[static_cast<int32_t>(boost)] = - isSupported ? HalSupport::ON : HalSupport::OFF; + mBoostSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { ALOGV("Skipped setBoost %s because Power HAL doesn't support it", toString(boost).c_str()); @@ -145,14 +145,15 @@ HalResult AidlHalWrapper::setBoost(Boost boost, int32_t durationMs) { HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) { std::unique_lock<std::mutex> lock(mModeMutex); + size_t idx = static_cast<size_t>(mode); + // Quick return if mode is not supported by HAL - if (mode > Mode::DISPLAY_INACTIVE || - mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::OFF) { + if (idx >= mModeSupportedArray.size() || mModeSupportedArray[idx] == HalSupport::OFF) { ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); return HalResult::UNSUPPORTED; } - if (mModeSupportedArray[static_cast<int32_t>(mode)] == HalSupport::UNKNOWN) { + if (mModeSupportedArray[idx] == HalSupport::UNKNOWN) { bool isSupported = false; auto isSupportedRet = mHandle->isModeSupported(mode, &isSupported); if (!isSupportedRet.isOk()) { @@ -161,8 +162,7 @@ HalResult AidlHalWrapper::setMode(Mode mode, bool enabled) { return HalResult::FAILED; } - mModeSupportedArray[static_cast<int32_t>(mode)] = - isSupported ? HalSupport::ON : HalSupport::OFF; + mModeSupportedArray[idx] = isSupported ? HalSupport::ON : HalSupport::OFF; if (!isSupported) { ALOGV("Skipped setMode %s because Power HAL doesn't support it", toString(mode).c_str()); diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 6561707bcc..424a8b350a 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -389,33 +389,58 @@ void BufferLayer::gatherBufferInfo() { mBufferInfo.mFrameLatencyNeeded = true; } +bool BufferLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { + // If this is not a valid vsync for the layer's uid, return and try again later + const bool isVsyncValidForUid = + mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid); + if (!isVsyncValidForUid) { + ATRACE_NAME("!isVsyncValidForUid"); + return false; + } + + // AutoRefresh layers and sideband streams should always be presented + if (getSidebandStreamChanged() || getAutoRefresh()) { + return true; + } + + // If the next planned present time is not current, return and try again later + if (frameIsEarly(expectedPresentTime)) { + ATRACE_NAME("frameIsEarly()"); + return false; + } + + // If this layer doesn't have a frame is shouldn't be presented + if (!hasFrameUpdate()) { + return false; + } + + // Defer to the derived class to decide whether the next buffer is due for + // presentation. + return isBufferDue(expectedPresentTime); +} + bool BufferLayer::frameIsEarly(nsecs_t expectedPresentTime) const { // TODO(b/169901895): kEarlyLatchVsyncThreshold should be based on the // vsync period. We can do this change as soon as ag/13100772 is merged. constexpr static std::chrono::nanoseconds kEarlyLatchVsyncThreshold = 5ms; const auto presentTime = nextPredictedPresentTime(); - if (std::abs(presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) { + if (!presentTime.has_value()) { return false; } - return presentTime >= expectedPresentTime && - presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count(); + if (std::abs(*presentTime - expectedPresentTime) >= kEarlyLatchMaxThreshold.count()) { + return false; + } + + return *presentTime >= expectedPresentTime && + *presentTime - expectedPresentTime >= kEarlyLatchVsyncThreshold.count(); } bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, nsecs_t expectedPresentTime) { ATRACE_CALL(); - // If this is not a valid vsync for the layer's uid, return and try again later - const bool isVsyncValidForUid = - mFlinger->mScheduler->isVsyncValid(expectedPresentTime, mOwnerUid); - if (!isVsyncValidForUid) { - ATRACE_NAME("!isVsyncValidForUid"); - mFlinger->setTransactionFlags(eTraversalNeeded); - return false; - } - bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); if (refreshRequired) { @@ -435,12 +460,6 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, return false; } - if (frameIsEarly(expectedPresentTime)) { - ATRACE_NAME("frameIsEarly()"); - mFlinger->signalLayerUpdate(); - return false; - } - // If the head buffer's acquire fence hasn't signaled yet, return and // try again later if (!fenceHasSignaled()) { diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 5cd9a7cde1..2118f4ad18 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -175,12 +175,24 @@ protected: void setTransformHint(ui::Transform::RotationFlags displayTransformHint) override; // Transform hint provided to the producer. This must be accessed holding - /// the mStateLock. + // the mStateLock. ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0; bool getAutoRefresh() const { return mAutoRefresh; } bool getSidebandStreamChanged() const { return mSidebandStreamChanged; } + // Returns true if the next buffer should be presented at the expected present time + bool shouldPresentNow(nsecs_t expectedPresentTime) const final; + + // Returns true if the next buffer should be presented at the expected present time, + // overridden by BufferStateLayer and BufferQueueLayer for implementation + // specific logic + virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0; + + // Returns true if the next frame is considered too early to present + // at the given expectedPresentTime + bool frameIsEarly(nsecs_t expectedPresentTime) const; + std::atomic<bool> mAutoRefresh{false}; std::atomic<bool> mSidebandStreamChanged{false}; @@ -225,13 +237,8 @@ private: FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; - // Returns true if the next frame is considered too early to present - // at the given expectedPresentTime - bool frameIsEarly(nsecs_t expectedPresentTime) const; - - // Returns the predicted present time of the next frame if available or - // 0 otherwise. - virtual nsecs_t nextPredictedPresentTime() const = 0; + // Returns the predicted present time of the next frame if available + virtual std::optional<nsecs_t> nextPredictedPresentTime() const = 0; // The amount of time SF can delay a frame if it is considered early based // on the VsyncModulator::VsyncConfig::appWorkDuration diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 04cec4fafe..32e6b1098f 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -100,15 +100,7 @@ int32_t BufferQueueLayer::getQueuedFrameCount() const { return mQueuedFrames; } -bool BufferQueueLayer::shouldPresentNow(nsecs_t expectedPresentTime) const { - if (getSidebandStreamChanged() || getAutoRefresh()) { - return true; - } - - if (!hasFrameUpdate()) { - return false; - } - +bool BufferQueueLayer::isBufferDue(nsecs_t expectedPresentTime) const { Mutex::Autolock lock(mQueueItemLock); const int64_t addedTime = mQueueItems[0].item.mTimestamp; @@ -223,16 +215,16 @@ bool BufferQueueLayer::hasFrameUpdate() const { return mQueuedFrames > 0; } -nsecs_t BufferQueueLayer::nextPredictedPresentTime() const { +std::optional<nsecs_t> BufferQueueLayer::nextPredictedPresentTime() const { Mutex::Autolock lock(mQueueItemLock); if (mQueueItems.empty()) { - return 0; + return std::nullopt; } const auto& bufferData = mQueueItems[0]; if (!bufferData.item.mIsAutoTimestamp || !bufferData.surfaceFrame) { - return 0; + return std::nullopt; } return bufferData.surfaceFrame->getPredictions().presentTime; diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index 2347d8cec7..0e8fdbe092 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -53,7 +53,8 @@ public: int32_t getQueuedFrameCount() const override; - bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + // Returns true if the next buffer should be presented at the expected present time + bool isBufferDue(nsecs_t expectedPresentTime) const override; // Implements BufferLayer. bool fenceHasSignaled() const override; @@ -116,7 +117,7 @@ private: // Temporary - Used only for LEGACY camera mode. uint32_t getProducerStickyTransform() const; - nsecs_t nextPredictedPresentTime() const override; + std::optional<nsecs_t> nextPredictedPresentTime() const override; sp<BufferLayerConsumer> mConsumer; sp<IGraphicBufferProducer> mProducer; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index bca1c69c0f..0cc15c2acd 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -212,14 +212,6 @@ void BufferStateLayer::finalizeFrameEventHistory(const std::shared_ptr<FenceTime } } -bool BufferStateLayer::shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { - if (getSidebandStreamChanged() || getAutoRefresh()) { - return true; - } - - return hasFrameUpdate(); -} - bool BufferStateLayer::willPresentCurrentTransaction() const { // Returns true if the most recent Transaction applied to CurrentState will be presented. return (getSidebandStreamChanged() || getAutoRefresh() || @@ -343,7 +335,8 @@ bool BufferStateLayer::addFrameEvent(const sp<Fence>& acquireFence, nsecs_t post bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& clientCacheId, uint64_t frameNumber) { + const client_cache_t& clientCacheId, uint64_t frameNumber, + std::optional<nsecs_t> /* dequeueTime */) { ATRACE_CALL(); if (mCurrentState.buffer) { @@ -602,9 +595,9 @@ bool BufferStateLayer::hasFrameUpdate() const { return mCurrentStateModified && (c.buffer != nullptr || c.bgColorLayer != nullptr); } -nsecs_t BufferStateLayer::nextPredictedPresentTime() const { +std::optional<nsecs_t> BufferStateLayer::nextPredictedPresentTime() const { if (!getDrawingState().isAutoTimestamp || !mSurfaceFrame) { - return 0; + return std::nullopt; } return mSurfaceFrame->getPredictions().presentTime; @@ -644,7 +637,10 @@ status_t BufferStateLayer::updateActiveBuffer() { if (s.buffer == nullptr) { return BAD_VALUE; } - decrementPendingBufferCount(); + + if (s.buffer != mBufferInfo.mBuffer) { + decrementPendingBufferCount(); + } mPreviousBufferId = getCurrentBufferId(); mBufferInfo.mBuffer = s.buffer; diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 6414e6be60..f638caa4f3 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -45,7 +45,7 @@ public: void finalizeFrameEventHistory(const std::shared_ptr<FenceTime>& glDoneFence, const CompositorTiming& compositorTiming) override; - bool shouldPresentNow(nsecs_t expectedPresentTime) const override; + bool isBufferDue(nsecs_t /*expectedPresentTime*/) const override { return true; } uint32_t doTransactionResize(uint32_t flags, Layer::State* /*stateToCommit*/) override { return flags; @@ -71,7 +71,8 @@ public: bool setFrame(const Rect& frame) override; bool setBuffer(const sp<GraphicBuffer>& buffer, const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& clientCacheId, uint64_t frameNumber) override; + const client_cache_t& clientCacheId, uint64_t frameNumber, + std::optional<nsecs_t> dequeueTime) override; bool setAcquireFence(const sp<Fence>& fence) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; @@ -152,7 +153,7 @@ private: bool bufferNeedsFiltering() const override; - nsecs_t nextPredictedPresentTime() const override; + std::optional<nsecs_t> nextPredictedPresentTime() const override; static const std::array<float, 16> IDENTITY_MATRIX; diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index 43792dccfd..4f99495ff2 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -167,15 +167,10 @@ void Output::setDisplaySize(const ui::Size& size) { // Update framebuffer space const Rect newBounds(size); - ScaleVector scale; - scale = getScale(state.framebufferSpace.bounds, newBounds); state.framebufferSpace.bounds = newBounds; - state.framebufferSpace.content.scaleSelf(scale.x, scale.y); // Update display space - scale = getScale(state.displaySpace.bounds, newBounds); state.displaySpace.bounds = newBounds; - state.displaySpace.content.scaleSelf(scale.x, scale.y); state.transform = state.layerStackSpace.getTransform(state.displaySpace); // Update oriented display space @@ -185,9 +180,7 @@ void Output::setDisplaySize(const ui::Size& size) { std::swap(orientedSize.width, orientedSize.height); } const Rect newOrientedBounds(orientedSize); - scale = getScale(state.orientedDisplaySpace.bounds, newOrientedBounds); state.orientedDisplaySpace.bounds = newOrientedBounds; - state.orientedDisplaySpace.content.scaleSelf(scale.x, scale.y); dirtyEntireOutput(); } diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 1452192785..376bac8c69 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -338,15 +338,12 @@ TEST_F(OutputTest, setDisplaySpaceSizeUpdatesOutputStateAndDirtiesEntireOutput) EXPECT_EQ(Rect(0, 0, 2000, 1000), state.layerStackSpace.bounds); EXPECT_EQ(ui::ROTATION_0, state.orientedDisplaySpace.orientation); - EXPECT_EQ(Rect(0, 0, 900, 450), state.orientedDisplaySpace.content); EXPECT_EQ(Rect(0, 0, 1000, 500), state.orientedDisplaySpace.bounds); EXPECT_EQ(displayRect, state.displaySpace.bounds); - EXPECT_EQ(Rect(0, 0, 450, 900), state.displaySpace.content); EXPECT_EQ(ui::ROTATION_90, state.displaySpace.orientation); EXPECT_EQ(displayRect, state.framebufferSpace.bounds); - EXPECT_EQ(Rect(0, 0, 450, 900), state.framebufferSpace.content); EXPECT_EQ(ui::ROTATION_90, state.framebufferSpace.orientation); EXPECT_EQ(state.displaySpace.content, state.transform.transform(state.layerStackSpace.content)); diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp index a8fef04863..c994434a15 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp @@ -239,11 +239,16 @@ nsecs_t getMinTime(PredictionState predictionState, TimelineItem predictions, return minTime; } +int64_t TraceCookieCounter::getCookieForTracing() { + return ++mTraceCookie; +} + SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, PredictionState predictionState, frametimeline::TimelineItem&& predictions, std::shared_ptr<TimeStats> timeStats, - JankClassificationThresholds thresholds) + JankClassificationThresholds thresholds, + TraceCookieCounter* traceCookieCounter) : mToken(token), mOwnerPid(ownerPid), mOwnerUid(ownerUid), @@ -254,7 +259,8 @@ SurfaceFrame::SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::s mPredictions(predictions), mActuals({0, 0, 0}), mTimeStats(timeStats), - mJankClassificationThresholds(thresholds) {} + mJankClassificationThresholds(thresholds), + mTraceCookieCounter(*traceCookieCounter) {} void SurfaceFrame::setActualStartTime(nsecs_t actualStartTime) { std::lock_guard<std::mutex> lock(mMutex); @@ -443,7 +449,7 @@ void SurfaceFrame::onPresent(nsecs_t presentTime, int32_t displayFrameJankType, void SurfaceFrame::trace(int64_t displayFrameToken) { using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; - FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + { std::lock_guard<std::mutex> lock(mMutex); if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) { ALOGD("Cannot trace SurfaceFrame - %s with invalid token", mLayerName.c_str()); @@ -453,36 +459,83 @@ void SurfaceFrame::trace(int64_t displayFrameToken) { mLayerName.c_str()); return; } + } + + int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + // Expected timeline start + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::lock_guard<std::mutex> lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); + + auto* event = packet->set_frame_timeline_event(); + auto* expectedSurfaceFrameStartEvent = event->set_expected_surface_frame_start(); + + expectedSurfaceFrameStartEvent->set_cookie(expectedTimelineCookie); + + expectedSurfaceFrameStartEvent->set_token(mToken); + expectedSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken); + + expectedSurfaceFrameStartEvent->set_pid(mOwnerPid); + expectedSurfaceFrameStartEvent->set_layer_name(mDebugName); + }); + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::lock_guard<std::mutex> lock(mMutex); auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(systemTime())); + packet->set_timestamp(static_cast<uint64_t>(mPredictions.endTime)); auto* event = packet->set_frame_timeline_event(); - auto* surfaceFrameEvent = event->set_surface_frame(); + auto* expectedSurfaceFrameEndEvent = event->set_frame_end(); - surfaceFrameEvent->set_token(mToken); - surfaceFrameEvent->set_display_frame_token(displayFrameToken); + expectedSurfaceFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + + int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + // Actual timeline start + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::lock_guard<std::mutex> lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + // Actual start time is not yet available, so use expected start instead + packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime)); + + auto* event = packet->set_frame_timeline_event(); + auto* actualSurfaceFrameStartEvent = event->set_actual_surface_frame_start(); + + actualSurfaceFrameStartEvent->set_cookie(actualTimelineCookie); + + actualSurfaceFrameStartEvent->set_token(mToken); + actualSurfaceFrameStartEvent->set_display_frame_token(displayFrameToken); + + actualSurfaceFrameStartEvent->set_pid(mOwnerPid); + actualSurfaceFrameStartEvent->set_layer_name(mDebugName); if (mPresentState == PresentState::Dropped) { - surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED); + actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_DROPPED); } else if (mPresentState == PresentState::Unknown) { - surfaceFrameEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED); + actualSurfaceFrameStartEvent->set_present_type(FrameTimelineEvent::PRESENT_UNSPECIFIED); } else { - surfaceFrameEvent->set_present_type(toProto(mFramePresentMetadata)); + actualSurfaceFrameStartEvent->set_present_type(toProto(mFramePresentMetadata)); } - surfaceFrameEvent->set_on_time_finish(mFrameReadyMetadata == - FrameReadyMetadata::OnTimeFinish); - surfaceFrameEvent->set_gpu_composition(mGpuComposition); - surfaceFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); - - surfaceFrameEvent->set_expected_start_ns(mPredictions.startTime); - surfaceFrameEvent->set_expected_end_ns(mPredictions.endTime); + actualSurfaceFrameStartEvent->set_on_time_finish(mFrameReadyMetadata == + FrameReadyMetadata::OnTimeFinish); + actualSurfaceFrameStartEvent->set_gpu_composition(mGpuComposition); + actualSurfaceFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); + }); + // Actual timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + std::lock_guard<std::mutex> lock(mMutex); + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp(static_cast<uint64_t>(mActuals.endTime)); - surfaceFrameEvent->set_actual_start_ns(mActuals.startTime); - surfaceFrameEvent->set_actual_end_ns(mActuals.endTime); + auto* event = packet->set_frame_timeline_event(); + auto* actualSurfaceFrameEndEvent = event->set_frame_end(); - surfaceFrameEvent->set_layer_name(mDebugName); - surfaceFrameEvent->set_pid(mOwnerPid); + actualSurfaceFrameEndEvent->set_cookie(actualTimelineCookie); }); } @@ -520,11 +573,13 @@ void TokenManager::flushTokens(nsecs_t flushTime) { FrameTimeline::FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, JankClassificationThresholds thresholds) - : mCurrentDisplayFrame(std::make_shared<DisplayFrame>(timeStats, thresholds)), - mMaxDisplayFrames(kDefaultMaxDisplayFrames), + : mMaxDisplayFrames(kDefaultMaxDisplayFrames), mTimeStats(std::move(timeStats)), mSurfaceFlingerPid(surfaceFlingerPid), - mJankClassificationThresholds(thresholds) {} + mJankClassificationThresholds(thresholds) { + mCurrentDisplayFrame = + std::make_shared<DisplayFrame>(mTimeStats, thresholds, &mTraceCookieCounter); +} void FrameTimeline::onBootFinished() { perfetto::TracingInitArgs args; @@ -547,27 +602,29 @@ std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken( return std::make_shared<SurfaceFrame>(ISurfaceComposer::INVALID_VSYNC_ID, ownerPid, ownerUid, std::move(layerName), std::move(debugName), PredictionState::None, TimelineItem(), mTimeStats, - mJankClassificationThresholds); + mJankClassificationThresholds, &mTraceCookieCounter); } std::optional<TimelineItem> predictions = mTokenManager.getPredictionsForToken(*token); if (predictions) { return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName), std::move(debugName), PredictionState::Valid, std::move(*predictions), mTimeStats, - mJankClassificationThresholds); + mJankClassificationThresholds, &mTraceCookieCounter); } return std::make_shared<SurfaceFrame>(*token, ownerPid, ownerUid, std::move(layerName), std::move(debugName), PredictionState::Expired, - TimelineItem(), mTimeStats, - mJankClassificationThresholds); + TimelineItem(), mTimeStats, mJankClassificationThresholds, + &mTraceCookieCounter); } FrameTimeline::DisplayFrame::DisplayFrame(std::shared_ptr<TimeStats> timeStats, - JankClassificationThresholds thresholds) + JankClassificationThresholds thresholds, + TraceCookieCounter* traceCookieCounter) : mSurfaceFlingerPredictions(TimelineItem()), mSurfaceFlingerActuals(TimelineItem()), mTimeStats(timeStats), - mJankClassificationThresholds(thresholds) { + mJankClassificationThresholds(thresholds), + mTraceCookieCounter(*traceCookieCounter) { mSurfaceFrames.reserve(kNumSurfaceFramesInitial); } @@ -725,32 +782,69 @@ void FrameTimeline::DisplayFrame::onPresent(nsecs_t signalTime) { } void FrameTimeline::DisplayFrame::trace(pid_t surfaceFlingerPid) const { + if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) { + ALOGD("Cannot trace DisplayFrame with invalid token"); + return; + } + + int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + // Expected timeline start + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.startTime)); + + auto* event = packet->set_frame_timeline_event(); + auto* expectedDisplayFrameStartEvent = event->set_expected_display_frame_start(); + + expectedDisplayFrameStartEvent->set_cookie(expectedTimelineCookie); + + expectedDisplayFrameStartEvent->set_token(mToken); + expectedDisplayFrameStartEvent->set_pid(surfaceFlingerPid); + }); + // Expected timeline end FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { - if (mToken == ISurfaceComposer::INVALID_VSYNC_ID) { - ALOGD("Cannot trace DisplayFrame with invalid token"); - return; - } auto packet = ctx.NewTracePacket(); packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); - packet->set_timestamp(static_cast<uint64_t>(systemTime())); + packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerPredictions.endTime)); auto* event = packet->set_frame_timeline_event(); - auto* displayFrameEvent = event->set_display_frame(); + auto* expectedDisplayFrameEndEvent = event->set_frame_end(); - displayFrameEvent->set_token(mToken); - displayFrameEvent->set_present_type(toProto(mFramePresentMetadata)); - displayFrameEvent->set_on_time_finish(mFrameReadyMetadata == - FrameReadyMetadata::OnTimeFinish); - displayFrameEvent->set_gpu_composition(mGpuComposition); - displayFrameEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); + expectedDisplayFrameEndEvent->set_cookie(expectedTimelineCookie); + }); + + int64_t actualTimelineCookie = mTraceCookieCounter.getCookieForTracing(); + // Expected timeline start + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.startTime)); + + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameStartEvent = event->set_actual_display_frame_start(); - displayFrameEvent->set_expected_start_ns(mSurfaceFlingerPredictions.startTime); - displayFrameEvent->set_expected_end_ns(mSurfaceFlingerPredictions.endTime); + actualDisplayFrameStartEvent->set_cookie(actualTimelineCookie); - displayFrameEvent->set_actual_start_ns(mSurfaceFlingerActuals.startTime); - displayFrameEvent->set_actual_end_ns(mSurfaceFlingerActuals.endTime); + actualDisplayFrameStartEvent->set_token(mToken); + actualDisplayFrameStartEvent->set_pid(surfaceFlingerPid); - displayFrameEvent->set_pid(surfaceFlingerPid); + actualDisplayFrameStartEvent->set_present_type(toProto(mFramePresentMetadata)); + actualDisplayFrameStartEvent->set_on_time_finish(mFrameReadyMetadata == + FrameReadyMetadata::OnTimeFinish); + actualDisplayFrameStartEvent->set_gpu_composition(mGpuComposition); + actualDisplayFrameStartEvent->set_jank_type(jankTypeBitmaskToProto(mJankType)); + }); + // Expected timeline end + FrameTimelineDataSource::Trace([&](FrameTimelineDataSource::TraceContext ctx) { + auto packet = ctx.NewTracePacket(); + packet->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + packet->set_timestamp(static_cast<uint64_t>(mSurfaceFlingerActuals.endTime)); + + auto* event = packet->set_frame_timeline_event(); + auto* actualDisplayFrameEndEvent = event->set_frame_end(); + + actualDisplayFrameEndEvent->set_cookie(actualTimelineCookie); }); for (auto& surfaceFrame : mSurfaceFrames) { @@ -786,8 +880,8 @@ void FrameTimeline::finalizeCurrentDisplayFrame() { } mDisplayFrames.push_back(mCurrentDisplayFrame); mCurrentDisplayFrame.reset(); - mCurrentDisplayFrame = - std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds); + mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds, + &mTraceCookieCounter); } nsecs_t FrameTimeline::DisplayFrame::getBaseTime() const { diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h index f5239aa572..ed38cc6375 100644 --- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h +++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h @@ -127,6 +127,23 @@ enum class PredictionState { None, // Predictions are either not present or didn't come from TokenManager }; +/* + * Trace cookie is used to send start and end timestamps of <Surface/Display>Frames separately + * without needing to resend all the other information. We send all info to perfetto, along with a + * new cookie, in the start of a frame. For the corresponding end, we just send the same cookie. + * This helps in reducing the amount of data emitted by the producer. + */ +class TraceCookieCounter { +public: + int64_t getCookieForTracing(); + +private: + // Friend class for testing + friend class android::frametimeline::FrameTimelineTest; + + std::atomic<int64_t> mTraceCookie = 0; +}; + class SurfaceFrame { public: enum class PresentState { @@ -139,7 +156,8 @@ public: // TokenManager), Thresholds and TimeStats pointer. SurfaceFrame(int64_t token, pid_t ownerPid, uid_t ownerUid, std::string layerName, std::string debugName, PredictionState predictionState, TimelineItem&& predictions, - std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds); + std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds, + TraceCookieCounter* traceCookieCounter); ~SurfaceFrame() = default; // Returns std::nullopt if the frame hasn't been classified yet. @@ -205,6 +223,9 @@ private: // for BufferStuffing where the current buffer is expected to be ready but the previous buffer // was latched instead. nsecs_t mLastLatchTime GUARDED_BY(mMutex) = 0; + // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. Using a + // reference here because the counter is owned by FrameTimeline, which outlives SurfaceFrame. + TraceCookieCounter& mTraceCookieCounter; }; /* @@ -291,7 +312,8 @@ public: */ class DisplayFrame { public: - DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds); + DisplayFrame(std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds, + TraceCookieCounter* traceCookieCounter); virtual ~DisplayFrame() = default; // Dumpsys interface - dumps only if the DisplayFrame itself is janky or is at least one // SurfaceFrame is janky. @@ -360,6 +382,10 @@ public: // The refresh rate (vsync period) in nanoseconds as seen by SF during this DisplayFrame's // timeline nsecs_t mVsyncPeriod = 0; + // TraceCookieCounter is used to obtain the cookie for sendig trace packets to perfetto. + // Using a reference here because the counter is owned by FrameTimeline, which outlives + // DisplayFrame. + TraceCookieCounter& mTraceCookieCounter; }; FrameTimeline(std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid, @@ -402,6 +428,7 @@ private: mPendingPresentFences GUARDED_BY(mMutex); std::shared_ptr<DisplayFrame> mCurrentDisplayFrame GUARDED_BY(mMutex); TokenManager mTokenManager; + TraceCookieCounter mTraceCookieCounter; mutable std::mutex mMutex; uint32_t mMaxDisplayFrames; std::shared_ptr<TimeStats> mTimeStats; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 4731f50373..a545d186dd 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -688,6 +688,7 @@ std::optional<compositionengine::LayerFE::LayerSettings> Layer::prepareShadowCli shadowLayer.source.buffer.fence = nullptr; shadowLayer.frameNumber = 0; shadowLayer.bufferId = 0; + shadowLayer.name = getName(); if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) { return {}; @@ -723,6 +724,7 @@ void Layer::prepareClearClientComposition(LayerFE::LayerSettings& layerSettings, // If layer is blacked out, force alpha to 1 so that we draw a black color layer. layerSettings.alpha = blackout ? 1.0f : 0.0f; + layerSettings.name = getName(); } std::vector<compositionengine::LayerFE::LayerSettings> Layer::prepareClientCompositionList( diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index d6023b66ac..f78b5f31e9 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -446,7 +446,7 @@ public: virtual bool setBuffer(const sp<GraphicBuffer>& /*buffer*/, const sp<Fence>& /*acquireFence*/, nsecs_t /*postTime*/, nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/, const client_cache_t& /*clientCacheId*/, - uint64_t /* frameNumber */) { + uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */) { return false; }; virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 6a511a85a0..9230e72f45 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -233,11 +233,12 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } -void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) { - mCurrentFps = refreshRate.getFps().getIntValue(); +void RefreshRateOverlay::changeRefreshRate(const Fps& fps) { + mCurrentFps = fps.getIntValue(); auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame]; mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, - mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); + mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), + std::nullopt /* dequeueTime */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } @@ -249,15 +250,17 @@ void RefreshRateOverlay::onInvalidate() { mFrame = (mFrame + 1) % buffers.size(); auto buffer = buffers[mFrame]; mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, - mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */)); + mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), + std::nullopt /* dequeueTime */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } void RefreshRateOverlay::reset() { mBufferCache.clear(); - mLowFps = mFlinger.mRefreshRateConfigs->getMinRefreshRate().getFps().getIntValue(); - mHighFps = mFlinger.mRefreshRateConfigs->getMaxRefreshRate().getFps().getIntValue(); + const auto range = mFlinger.mRefreshRateConfigs->getSupportedRefreshRateRange(); + mLowFps = range.min.getIntValue(); + mHighFps = range.max.getIntValue(); } } // namespace android diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index 4ca1337c67..c16cfa07a4 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -23,7 +23,7 @@ #include <ui/Size.h> #include <utils/StrongPointer.h> -#include "Scheduler/RefreshRateConfigs.h" +#include "Fps.h" namespace android { @@ -34,14 +34,12 @@ class IGraphicBufferProducer; class Layer; class SurfaceFlinger; -using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate; - class RefreshRateOverlay { public: RefreshRateOverlay(SurfaceFlinger&, bool showSpinner); void setViewport(ui::Size); - void changeRefreshRate(const RefreshRate&); + void changeRefreshRate(const Fps&); void onInvalidate(); void reset(); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index 35b382ed05..975754b064 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -26,6 +26,7 @@ #include <utils/Trace.h> #include <chrono> #include <cmath> +#include "../SurfaceFlingerProperties.h" #undef LOG_TAG #define LOG_TAG "RefreshRateConfigs" @@ -38,6 +39,26 @@ std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, f toString(layer.seamlessness).c_str(), to_string(layer.desiredRefreshRate).c_str()); } + +std::vector<Fps> constructKnownFrameRates( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { + std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)}; + knownFrameRates.reserve(knownFrameRates.size() + configs.size()); + + // Add all supported refresh rates to the set + for (const auto& config : configs) { + const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod()); + knownFrameRates.emplace_back(refreshRate); + } + + // Sort and remove duplicates + std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess); + knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), + Fps::EqualsWithMargin()), + knownFrameRates.end()); + return knownFrameRates; +} + } // namespace using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType; @@ -153,9 +174,9 @@ struct RefreshRateScore { float score; }; -const RefreshRate& RefreshRateConfigs::getBestRefreshRate( - const std::vector<LayerRequirement>& layers, const GlobalSignals& globalSignals, - GlobalSignals* outSignalsConsidered) const { +RefreshRate RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& layers, + const GlobalSignals& globalSignals, + GlobalSignals* outSignalsConsidered) const { ATRACE_CALL(); ALOGV("getBestRefreshRate %zu layers", layers.size()); @@ -468,9 +489,20 @@ const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) return bestRefreshRate; } -const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const { +std::optional<Fps> RefreshRateConfigs::onKernelTimerChanged( + std::optional<HwcConfigIndexType> desiredActiveConfigId, bool timerExpired) const { std::lock_guard lock(mLock); - return getMinRefreshRateByPolicyLocked(); + + const auto& current = desiredActiveConfigId ? *mRefreshRates.at(*desiredActiveConfigId) + : *mCurrentRefreshRate; + const auto& min = *mMinSupportedRefreshRate; + + if (current != min) { + const auto& refreshRate = timerExpired ? min : current; + return refreshRate.getFps(); + } + + return {}; } const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { @@ -486,7 +518,7 @@ const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicyLocked() const { return *mPrimaryRefreshRates.front(); } -const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const { +RefreshRate RefreshRateConfigs::getMaxRefreshRateByPolicy() const { std::lock_guard lock(mLock); return getMaxRefreshRateByPolicyLocked(); } @@ -505,12 +537,12 @@ const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicyLocked() const { return *mPrimaryRefreshRates.back(); } -const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const { +RefreshRate RefreshRateConfigs::getCurrentRefreshRate() const { std::lock_guard lock(mLock); return *mCurrentRefreshRate; } -const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const { +RefreshRate RefreshRateConfigs::getCurrentRefreshRateByPolicy() const { std::lock_guard lock(mLock); return getCurrentRefreshRateByPolicyLocked(); } @@ -532,16 +564,23 @@ RefreshRateConfigs::RefreshRateConfigs( const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, HwcConfigIndexType currentConfigId) : mKnownFrameRates(constructKnownFrameRates(configs)) { + updateDisplayConfigs(configs, currentConfigId); +} + +void RefreshRateConfigs::updateDisplayConfigs( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + HwcConfigIndexType currentConfigId) { + std::lock_guard lock(mLock); LOG_ALWAYS_FATAL_IF(configs.empty()); LOG_ALWAYS_FATAL_IF(currentConfigId.value() < 0); LOG_ALWAYS_FATAL_IF(currentConfigId.value() >= configs.size()); + mRefreshRates.clear(); for (auto configId = HwcConfigIndexType(0); configId.value() < configs.size(); configId++) { const auto& config = configs.at(static_cast<size_t>(configId.value())); + const auto fps = Fps::fromPeriodNsecs(config->getVsyncPeriod()); mRefreshRates.emplace(configId, - std::make_unique<RefreshRate>(configId, config, - Fps::fromPeriodNsecs( - config->getVsyncPeriod()), + std::make_unique<RefreshRate>(configId, config, fps, RefreshRate::ConstructorTag(0))); if (configId == currentConfigId) { mCurrentRefreshRate = mRefreshRates.at(configId).get(); @@ -549,24 +588,27 @@ RefreshRateConfigs::RefreshRateConfigs( } std::vector<const RefreshRate*> sortedConfigs; - getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs); + getSortedRefreshRateListLocked([](const RefreshRate&) { return true; }, &sortedConfigs); mDisplayManagerPolicy.defaultConfig = currentConfigId; mMinSupportedRefreshRate = sortedConfigs.front(); mMaxSupportedRefreshRate = sortedConfigs.back(); mSupportsFrameRateOverride = false; - for (const auto& config1 : sortedConfigs) { - for (const auto& config2 : sortedConfigs) { - if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) { - mSupportsFrameRateOverride = true; - break; + if (android::sysprop::enable_frame_rate_override(true)) { + for (const auto& config1 : sortedConfigs) { + for (const auto& config2 : sortedConfigs) { + if (getFrameRateDivider(config1->getFps(), config2->getFps()) >= 2) { + mSupportsFrameRateOverride = true; + break; + } } } } + constructAvailableRefreshRates(); } -bool RefreshRateConfigs::isPolicyValid(const Policy& policy) { +bool RefreshRateConfigs::isPolicyValidLocked(const Policy& policy) const { // defaultConfig must be a valid config, and within the given refresh rate range. auto iter = mRefreshRates.find(policy.defaultConfig); if (iter == mRefreshRates.end()) { @@ -584,7 +626,7 @@ bool RefreshRateConfigs::isPolicyValid(const Policy& policy) { status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { std::lock_guard lock(mLock); - if (!isPolicyValid(policy)) { + if (!isPolicyValidLocked(policy)) { ALOGE("Invalid refresh rate policy: %s", policy.toString().c_str()); return BAD_VALUE; } @@ -599,7 +641,7 @@ status_t RefreshRateConfigs::setDisplayManagerPolicy(const Policy& policy) { status_t RefreshRateConfigs::setOverridePolicy(const std::optional<Policy>& policy) { std::lock_guard lock(mLock); - if (policy && !isPolicyValid(*policy)) { + if (policy && !isPolicyValidLocked(*policy)) { return BAD_VALUE; } Policy previousPolicy = *getCurrentPolicyLocked(); @@ -635,14 +677,14 @@ bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const { return false; } -void RefreshRateConfigs::getSortedRefreshRateList( +void RefreshRateConfigs::getSortedRefreshRateListLocked( const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate, std::vector<const RefreshRate*>* outRefreshRates) { outRefreshRates->clear(); outRefreshRates->reserve(mRefreshRates.size()); for (const auto& [type, refreshRate] : mRefreshRates) { if (shouldAddRefreshRate(*refreshRate)) { - ALOGV("getSortedRefreshRateList: config %d added to list policy", + ALOGV("getSortedRefreshRateListLocked: config %d added to list policy", refreshRate->configId.value()); outRefreshRates->push_back(refreshRate.get()); } @@ -668,8 +710,9 @@ void RefreshRateConfigs::constructAvailableRefreshRates() { ALOGV("constructAvailableRefreshRates: %s ", policy->toString().c_str()); auto filterRefreshRates = [&](Fps min, Fps max, const char* listName, - std::vector<const RefreshRate*>* outRefreshRates) { - getSortedRefreshRateList( + std::vector<const RefreshRate*>* + outRefreshRates) REQUIRES(mLock) { + getSortedRefreshRateListLocked( [&](const RefreshRate& refreshRate) REQUIRES(mLock) { const auto& hwcConfig = refreshRate.hwcConfig; @@ -702,25 +745,6 @@ void RefreshRateConfigs::constructAvailableRefreshRates() { &mAppRequestRefreshRates); } -std::vector<Fps> RefreshRateConfigs::constructKnownFrameRates( - const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { - std::vector<Fps> knownFrameRates = {Fps(24.0f), Fps(30.0f), Fps(45.0f), Fps(60.0f), Fps(72.0f)}; - knownFrameRates.reserve(knownFrameRates.size() + configs.size()); - - // Add all supported refresh rates to the set - for (const auto& config : configs) { - const auto refreshRate = Fps::fromPeriodNsecs(config->getVsyncPeriod()); - knownFrameRates.emplace_back(refreshRate); - } - - // Sort and remove duplicates - std::sort(knownFrameRates.begin(), knownFrameRates.end(), Fps::comparesLess); - knownFrameRates.erase(std::unique(knownFrameRates.begin(), knownFrameRates.end(), - Fps::EqualsWithMargin()), - knownFrameRates.end()); - return knownFrameRates; -} - Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { if (frameRate.lessThanOrEqualWithMargin(*mKnownFrameRates.begin())) { return *mKnownFrameRates.begin(); @@ -740,7 +764,7 @@ Fps RefreshRateConfigs::findClosestKnownFrameRate(Fps frameRate) const { RefreshRateConfigs::KernelIdleTimerAction RefreshRateConfigs::getIdleTimerAction() const { std::lock_guard lock(mLock); - const auto& deviceMin = getMinRefreshRate(); + const auto& deviceMin = *mMinSupportedRefreshRate; const auto& minByPolicy = getMinRefreshRateByPolicyLocked(); const auto& maxByPolicy = getMaxRefreshRateByPolicyLocked(); diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index e4bbf7f516..4b99145184 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -68,8 +68,6 @@ public: std::shared_ptr<const HWC2::Display::Config> config, Fps fps, ConstructorTag) : configId(configId), hwcConfig(config), fps(std::move(fps)) {} - RefreshRate(const RefreshRate&) = delete; - HwcConfigIndexType getConfigId() const { return configId; } nsecs_t getVsyncPeriod() const { return hwcConfig->getVsyncPeriod(); } int32_t getConfigGroup() const { return hwcConfig->getConfigGroup(); } @@ -111,27 +109,26 @@ public: using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>; - struct Policy { - private: - static constexpr int kAllowGroupSwitchingDefault = false; + struct FpsRange { + Fps min{0.0f}; + Fps max{std::numeric_limits<float>::max()}; - public: - struct Range { - Fps min{0.0f}; - Fps max{std::numeric_limits<float>::max()}; + bool operator==(const FpsRange& other) const { + return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max); + } - bool operator==(const Range& other) const { - return min.equalsWithMargin(other.min) && max.equalsWithMargin(other.max); - } + bool operator!=(const FpsRange& other) const { return !(*this == other); } - bool operator!=(const Range& other) const { return !(*this == other); } + std::string toString() const { + return base::StringPrintf("[%s %s]", to_string(min).c_str(), to_string(max).c_str()); + } + }; - std::string toString() const { - return base::StringPrintf("[%s %s]", to_string(min).c_str(), - to_string(max).c_str()); - } - }; + struct Policy { + private: + static constexpr int kAllowGroupSwitchingDefault = false; + public: // The default config, used to ensure we only initiate display config switches within the // same config group as defaultConfigId's group. HwcConfigIndexType defaultConfig; @@ -140,29 +137,29 @@ public: // The primary refresh rate range represents display manager's general guidance on the // display configs we'll consider when switching refresh rates. Unless we get an explicit // signal from an app, we should stay within this range. - Range primaryRange; + FpsRange primaryRange; // The app request refresh rate range allows us to consider more display configs when // switching refresh rates. Although we should generally stay within the primary range, // specific considerations, such as layer frame rate settings specified via the // setFrameRate() api, may cause us to go outside the primary range. We never go outside the // app request range. The app request range will be greater than or equal to the primary // refresh rate range, never smaller. - Range appRequestRange; + FpsRange appRequestRange; Policy() = default; - Policy(HwcConfigIndexType defaultConfig, const Range& range) + Policy(HwcConfigIndexType defaultConfig, const FpsRange& range) : Policy(defaultConfig, kAllowGroupSwitchingDefault, range, range) {} - Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const Range& range) + Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, const FpsRange& range) : Policy(defaultConfig, allowGroupSwitching, range, range) {} - Policy(HwcConfigIndexType defaultConfig, const Range& primaryRange, - const Range& appRequestRange) + Policy(HwcConfigIndexType defaultConfig, const FpsRange& primaryRange, + const FpsRange& appRequestRange) : Policy(defaultConfig, kAllowGroupSwitchingDefault, primaryRange, appRequestRange) {} Policy(HwcConfigIndexType defaultConfig, bool allowGroupSwitching, - const Range& primaryRange, const Range& appRequestRange) + const FpsRange& primaryRange, const FpsRange& appRequestRange) : defaultConfig(defaultConfig), allowGroupSwitching(allowGroupSwitching), primaryRange(primaryRange), @@ -258,35 +255,35 @@ public: // globalSignals - global state of touch and idle // outSignalsConsidered - An output param that tells the caller whether the refresh rate was // chosen based on touch boost and/or idle timer. - const RefreshRate& getBestRefreshRate(const std::vector<LayerRequirement>& layers, - const GlobalSignals& globalSignals, - GlobalSignals* outSignalsConsidered = nullptr) const + RefreshRate getBestRefreshRate(const std::vector<LayerRequirement>& layers, + const GlobalSignals& globalSignals, + GlobalSignals* outSignalsConsidered = nullptr) const EXCLUDES(mLock); - // Returns the lowest refresh rate supported by the device. This won't change at runtime. - const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; } + FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) { + std::lock_guard lock(mLock); + return {mMinSupportedRefreshRate->getFps(), mMaxSupportedRefreshRate->getFps()}; + } - // Returns the lowest refresh rate according to the current policy. May change at runtime. Only - // uses the primary range, not the app request range. - const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock); - - // Returns the highest refresh rate supported by the device. This won't change at runtime. - const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; } + std::optional<Fps> onKernelTimerChanged(std::optional<HwcConfigIndexType> desiredActiveConfigId, + bool timerExpired) const EXCLUDES(mLock); // Returns the highest refresh rate according to the current policy. May change at runtime. Only // uses the primary range, not the app request range. - const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock); + RefreshRate getMaxRefreshRateByPolicy() const EXCLUDES(mLock); // Returns the current refresh rate - const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock); + RefreshRate getCurrentRefreshRate() const EXCLUDES(mLock); // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by // the policy. - const RefreshRate& getCurrentRefreshRateByPolicy() const; + RefreshRate getCurrentRefreshRateByPolicy() const; - // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at + // Returns the refresh rate that corresponds to a HwcConfigIndexType. This may change at // runtime. - const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const { + // TODO(b/159590486) An invalid config id may be given here if the dipslay configs have changed. + RefreshRate getRefreshRateFromConfigId(HwcConfigIndexType configId) const EXCLUDES(mLock) { + std::lock_guard lock(mLock); return *mRefreshRates.at(configId); }; @@ -302,10 +299,17 @@ public: RefreshRateConfigs(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, HwcConfigIndexType currentConfigId); + void updateDisplayConfigs( + const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, + HwcConfigIndexType currentConfig) EXCLUDES(mLock); + // Returns whether switching configs (refresh rate or resolution) is possible. // TODO(b/158780872): Consider HAL support, and skip frame rate detection if the configs only // differ in resolution. - bool canSwitch() const { return mRefreshRates.size() > 1; } + bool canSwitch() const EXCLUDES(mLock) { + std::lock_guard lock(mLock); + return mRefreshRates.size() > 1; + } // Class to enumerate options around toggling the kernel timer on and off. We have an option // for no change to avoid extra calls to kernel. @@ -334,12 +338,10 @@ private: friend class RefreshRateConfigsTest; void constructAvailableRefreshRates() REQUIRES(mLock); - static std::vector<Fps> constructKnownFrameRates( - const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs); - void getSortedRefreshRateList( + void getSortedRefreshRateListLocked( const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate, - std::vector<const RefreshRate*>* outRefreshRates); + std::vector<const RefreshRate*>* outRefreshRates) REQUIRES(mLock); // Returns the refresh rate with the highest score in the collection specified from begin // to end. If there are more than one with the same highest refresh rate, the first one is @@ -364,7 +366,7 @@ private: const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock); const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); - bool isPolicyValid(const Policy& policy); + bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); // Return the display refresh rate divider to match the layer // frame rate, or 0 if the display refresh rate is not a multiple of the @@ -376,9 +378,9 @@ private: float calculateLayerScoreLocked(const LayerRequirement&, const RefreshRate&, bool isSeamlessSwitch) const REQUIRES(mLock); - // The list of refresh rates, indexed by display config ID. This must not change after this + // The list of refresh rates, indexed by display config ID. This may change after this // object is initialized. - AllRefreshRatesMapType mRefreshRates; + AllRefreshRatesMapType mRefreshRates GUARDED_BY(mLock); // The list of refresh rates in the primary range of the current policy, ordered by vsyncPeriod // (the first element is the lowest refresh rate). @@ -398,9 +400,9 @@ private: std::optional<Policy> mOverridePolicy GUARDED_BY(mLock); // The min and max refresh rates supported by the device. - // This will not change at runtime. - const RefreshRate* mMinSupportedRefreshRate; - const RefreshRate* mMaxSupportedRefreshRate; + // This may change at runtime. + const RefreshRate* mMinSupportedRefreshRate GUARDED_BY(mLock); + const RefreshRate* mMaxSupportedRefreshRate GUARDED_BY(mLock); mutable std::mutex mLock; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 18c899bc0f..49e3903eb2 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -612,7 +612,7 @@ void Scheduler::chooseRefreshRateForContent() { mFeatures.contentRequirements = summary; newConfigId = calculateRefreshRateConfigIndexType(&consideredSignals); - auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); + auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newRefreshRate.getFps()); @@ -629,7 +629,7 @@ void Scheduler::chooseRefreshRateForContent() { } } if (frameRateChanged) { - auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); + auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId); mSchedulerCallback.changeRefreshRate(newRefreshRate, consideredSignals.idle ? ConfigEvent::None : ConfigEvent::Changed); diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp index db82772f42..f42cd53e0a 100644 --- a/services/surfaceflinger/StartPropertySetThread.cpp +++ b/services/surfaceflinger/StartPropertySetThread.cpp @@ -31,6 +31,7 @@ bool StartPropertySetThread::threadLoop() { property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0"); // Clear BootAnimation exit flag property_set("service.bootanim.exit", "0"); + property_set("service.bootanim.progress", "0"); // Start BootAnimation if not started property_set("ctl.start", "bootanim"); // Exit immediately diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6967f69876..2e00ca8939 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -995,7 +995,7 @@ int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) { void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { ATRACE_CALL(); - auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId); + auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId); ALOGV("setDesiredActiveConfig(%s)", refreshRate.getName().c_str()); std::lock_guard<std::mutex> lock(mActiveConfigLock); @@ -1030,7 +1030,7 @@ void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) { } if (mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(refreshRate); + mRefreshRateOverlay->changeRefreshRate(refreshRate.getFps()); } } @@ -1076,14 +1076,14 @@ void SurfaceFlinger::setActiveConfigInternal() { return; } - auto& oldRefreshRate = + auto oldRefreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(display->getActiveConfig()); std::lock_guard<std::mutex> lock(mActiveConfigLock); mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId); display->setActiveConfig(mUpcomingActiveConfig.configId); - auto& refreshRate = + auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId); mRefreshRateStats->setRefreshRate(refreshRate.getFps()); @@ -1126,7 +1126,7 @@ void SurfaceFlinger::performSetActiveConfig() { return; } - auto& refreshRate = + auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId); ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.getConfigId().value(), refreshRate.getName().c_str()); @@ -1914,19 +1914,17 @@ void SurfaceFlinger::onMessageInvalidate(int64_t vsyncId, nsecs_t expectedVSyncT bool SurfaceFlinger::handleMessageTransaction() { ATRACE_CALL(); - uint32_t transactionFlags = peekTransactionFlags(); - - bool flushedATransaction = flushTransactionQueues(); + if (getTransactionFlags(eTransactionFlushNeeded)) { + flushPendingTransactionQueues(); + flushTransactionQueue(); + } + uint32_t transactionFlags = peekTransactionFlags(); bool runHandleTransaction = - (transactionFlags && (transactionFlags != eTransactionFlushNeeded)) || - flushedATransaction || - mForceTraversal; + ((transactionFlags & (~eTransactionFlushNeeded)) != 0) || mForceTraversal; if (runHandleTransaction) { handleTransaction(eTransactionMask); - } else { - getTransactionFlags(eTransactionFlushNeeded); } if (transactionFlushNeeded()) { @@ -1994,11 +1992,6 @@ void SurfaceFlinger::onMessageRefresh() { mScheduler->onDisplayRefreshed(presentTime); - // Set presentation information before calling postComposition, such that jank information from - // this' frame classification is already available when sending jank info to clients. - mFrameTimeline->setSfPresent(systemTime(), - std::make_shared<FenceTime>(mPreviousPresentFences[0])); - postFrame(); postComposition(); @@ -2120,11 +2113,6 @@ void SurfaceFlinger::postComposition() { ATRACE_CALL(); ALOGV("postComposition"); - nsecs_t dequeueReadyTime = systemTime(); - for (const auto& layer : mLayersWithQueuedFrames) { - layer->releasePendingBuffer(dequeueReadyTime); - } - const auto* display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked()).get(); getBE().mGlCompositionDoneTimeline.updateSignalTimes(); @@ -2146,6 +2134,17 @@ void SurfaceFlinger::postComposition() { auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFences[0]); getBE().mDisplayTimeline.push(presentFenceTime); + // Set presentation information before calling Layer::releasePendingBuffer, such that jank + // information from previous' frame classification is already available when sending jank info + // to clients, so they get jank classification as early as possible. + mFrameTimeline->setSfPresent(systemTime(), + std::make_shared<FenceTime>(mPreviousPresentFences[0])); + + nsecs_t dequeueReadyTime = systemTime(); + for (const auto& layer : mLayersWithQueuedFrames) { + layer->releasePendingBuffer(dequeueReadyTime); + } + const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(systemTime()); // We use the CompositionEngine::getLastFrameRefreshTimestamp() which might @@ -2621,6 +2620,9 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken, if (currentState.physical->hwcDisplayId == getHwComposer().getInternalHwcDisplayId()) { const auto displayId = currentState.physical->id; const auto configs = getHwComposer().getConfigs(displayId); + const auto currentConfig = + HwcConfigIndexType(getHwComposer().getActiveConfigIndex(displayId)); + mRefreshRateConfigs->updateDisplayConfigs(configs, currentConfig); mVsyncConfiguration->reset(); updatePhaseConfiguration(mRefreshRateConfigs->getCurrentRefreshRate()); if (mRefreshRateOverlay) { @@ -2827,7 +2829,6 @@ void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags) { }); } - commitInputWindowCommands(); commitTransaction(); } @@ -2868,11 +2869,6 @@ void SurfaceFlinger::updateInputWindowInfo() { : nullptr); } -void SurfaceFlinger::commitInputWindowCommands() { - mInputWindowCommands.merge(mPendingInputWindowCommands); - mPendingInputWindowCommands.clear(); -} - void SurfaceFlinger::updateCursorAsync() { compositionengine::CompositionRefreshArgs refreshArgs; for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) { @@ -3236,55 +3232,55 @@ void SurfaceFlinger::setTraversalNeeded() { mForceTraversal = true; } -bool SurfaceFlinger::flushTransactionQueues() { +void SurfaceFlinger::flushPendingTransactionQueues() { // to prevent onHandleDestroyed from being called while the lock is held, // we must keep a copy of the transactions (specifically the composer // states) around outside the scope of the lock std::vector<const TransactionState> transactions; - bool flushedATransaction = false; { - Mutex::Autolock _l(mStateLock); + Mutex::Autolock _l(mQueueLock); - auto it = mTransactionQueues.begin(); - while (it != mTransactionQueues.end()) { + auto it = mPendingTransactionQueues.begin(); + while (it != mPendingTransactionQueues.end()) { auto& [applyToken, transactionQueue] = *it; while (!transactionQueue.empty()) { const auto& transaction = transactionQueue.front(); if (!transactionIsReadyToBeApplied(transaction.desiredPresentTime, transaction.states)) { - setTransactionFlags(eTransactionFlushNeeded); break; } transactions.push_back(transaction); - applyTransactionState(transaction.frameTimelineVsyncId, transaction.states, - transaction.displays, transaction.flags, - mPendingInputWindowCommands, transaction.desiredPresentTime, - transaction.isAutoTimestamp, transaction.buffer, - transaction.postTime, transaction.privileged, - transaction.hasListenerCallbacks, - transaction.listenerCallbacks, transaction.originPid, - transaction.originUid, transaction.id, /*isMainThread*/ true); transactionQueue.pop(); - flushedATransaction = true; } if (transactionQueue.empty()) { - it = mTransactionQueues.erase(it); - mTransactionCV.broadcast(); + it = mPendingTransactionQueues.erase(it); + mTransactionQueueCV.broadcast(); } else { it = std::next(it, 1); } } } - return flushedATransaction; + + { + Mutex::Autolock _l(mStateLock); + for (const auto& transaction : transactions) { + applyTransactionState(transaction.frameTimelineVsyncId, transaction.states, + transaction.displays, transaction.flags, mInputWindowCommands, + transaction.desiredPresentTime, transaction.isAutoTimestamp, + transaction.buffer, transaction.postTime, transaction.privileged, + transaction.hasListenerCallbacks, transaction.listenerCallbacks, + transaction.originPid, transaction.originUid, transaction.id); + } + } } bool SurfaceFlinger::transactionFlushNeeded() { - return !mTransactionQueues.empty(); + Mutex::Autolock _l(mQueueLock); + return !mPendingTransactionQueues.empty(); } - bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, const Vector<ComposerState>& states, bool updateTransactionCounters) { @@ -3306,6 +3302,8 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, if (s.acquireFence && s.acquireFence->getStatus() == Fence::Status::Unsignaled) { ready = false; } + + Mutex::Autolock _l(mStateLock); sp<Layer> layer = nullptr; if (s.surface) { layer = fromHandleLocked(s.surface).promote(); @@ -3318,9 +3316,8 @@ bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, ready = false; } if (updateTransactionCounters) { - // See BufferStateLayer::mPendingBufferTransactions - if (layer) layer->incrementPendingBufferCount(); - + // See BufferStateLayer::mPendingBufferTransactions + if (layer) layer->incrementPendingBufferCount(); } } return ready; @@ -3337,88 +3334,153 @@ status_t SurfaceFlinger::setTransactionState( const int64_t postTime = systemTime(); bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess(); + { + Mutex::Autolock _l(mQueueLock); - Mutex::Autolock _l(mStateLock); + // If its TransactionQueue already has a pending TransactionState or if it is pending + auto itr = mPendingTransactionQueues.find(applyToken); + // if this is an animation frame, wait until prior animation frame has + // been applied by SF + if (flags & eAnimation) { + while (itr != mPendingTransactionQueues.end() || + (!mTransactionQueue.empty() && mAnimTransactionPending)) { + status_t err = mTransactionQueueCV.waitRelative(mQueueLock, s2ns(5)); + if (CC_UNLIKELY(err != NO_ERROR)) { + ALOGW_IF(err == TIMED_OUT, + "setTransactionState timed out " + "waiting for animation frame to apply"); + break; + } + itr = mPendingTransactionQueues.find(applyToken); + } + } - // If its TransactionQueue already has a pending TransactionState or if it is pending - auto itr = mTransactionQueues.find(applyToken); - // if this is an animation frame, wait until prior animation frame has - // been applied by SF - if (flags & eAnimation) { - while (itr != mTransactionQueues.end()) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - ALOGW_IF(err == TIMED_OUT, - "setTransactionState timed out " - "waiting for animation frame to apply"); - break; + const bool pendingTransactions = itr != mPendingTransactionQueues.end(); + // Expected present time is computed and cached on invalidate, so it may be stale. + if (!pendingTransactions) { + const auto now = systemTime(); + const bool nextVsyncPending = now < mExpectedPresentTime.load(); + const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now); + mExpectedPresentTime = calculateExpectedPresentTime(stats); + // The transaction might arrive just before the next vsync but after + // invalidate was called. In that case we need to get the next vsync + // afterwards. + if (nextVsyncPending) { + mExpectedPresentTime += stats.vsyncPeriod; } - itr = mTransactionQueues.find(applyToken); } - } - const bool pendingTransactions = itr != mTransactionQueues.end(); - // Expected present time is computed and cached on invalidate, so it may be stale. - if (!pendingTransactions) { - const auto now = systemTime(); - const bool nextVsyncPending = now < mExpectedPresentTime.load(); - const DisplayStatInfo stats = mScheduler->getDisplayStatInfo(now); - mExpectedPresentTime = calculateExpectedPresentTime(stats); - // The transaction might arrive just before the next vsync but after - // invalidate was called. In that case we need to get the next vsync - // afterwards. - if (nextVsyncPending) { - mExpectedPresentTime += stats.vsyncPeriod; + // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag + if (flags & eEarlyWakeup) { + ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]"); + } + + if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) { + ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags"); + flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd); + } + + IPCThreadState* ipc = IPCThreadState::self(); + const int originPid = ipc->getCallingPid(); + const int originUid = ipc->getCallingUid(); + + // Call transactionIsReadyToBeApplied first in case we need to incrementPendingBufferCount + // if the transaction contains a buffer. + if (!transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, + true) || + pendingTransactions) { + mPendingTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, + flags, inputWindowCommands, + desiredPresentTime, isAutoTimestamp, + uncacheBuffer, postTime, privileged, + hasListenerCallbacks, listenerCallbacks, + originPid, originUid, transactionId); + + setTransactionFlags(eTransactionFlushNeeded); + return NO_ERROR; } + + mTransactionQueue.emplace_back(frameTimelineVsyncId, states, displays, flags, + inputWindowCommands, desiredPresentTime, isAutoTimestamp, + uncacheBuffer, postTime, privileged, hasListenerCallbacks, + listenerCallbacks, originPid, originUid, transactionId); } - IPCThreadState* ipc = IPCThreadState::self(); - const int originPid = ipc->getCallingPid(); - const int originUid = ipc->getCallingUid(); - - if (pendingTransactions || - !transactionIsReadyToBeApplied(isAutoTimestamp ? 0 : desiredPresentTime, states, true)) { - mTransactionQueues[applyToken].emplace(frameTimelineVsyncId, states, displays, flags, - desiredPresentTime, isAutoTimestamp, uncacheBuffer, - postTime, privileged, hasListenerCallbacks, - listenerCallbacks, originPid, originUid, - transactionId); - setTransactionFlags(eTransactionFlushNeeded); + const auto schedule = [](uint32_t flags) { + if (flags & eEarlyWakeup) return TransactionSchedule::Early; + if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd; + if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart; + return TransactionSchedule::Late; + }(flags); + setTransactionFlags(eTransactionFlushNeeded, schedule); + + // if this is a synchronous transaction, wait for it to take effect + // before returning. + const bool synchronous = flags & eSynchronous; + const bool syncInput = inputWindowCommands.syncInputWindows; + if (!synchronous && !syncInput) { return NO_ERROR; } - applyTransactionState(frameTimelineVsyncId, states, displays, flags, inputWindowCommands, - desiredPresentTime, isAutoTimestamp, uncacheBuffer, postTime, privileged, - hasListenerCallbacks, listenerCallbacks, originPid, originUid, - transactionId, /*isMainThread*/ false); - return NO_ERROR; -} - -void SurfaceFlinger::applyTransactionState( - int64_t frameTimelineVsyncId, const Vector<ComposerState>& states, - const Vector<DisplayState>& displays, uint32_t flags, - const InputWindowCommands& inputWindowCommands, const int64_t desiredPresentTime, - bool isAutoTimestamp, const client_cache_t& uncacheBuffer, const int64_t postTime, - bool privileged, bool hasListenerCallbacks, - const std::vector<ListenerCallbacks>& listenerCallbacks, int originPid, int originUid, - uint64_t transactionId, bool isMainThread) { - uint32_t transactionFlags = 0; + // Handle synchronous cases. + { + Mutex::Autolock _l(mStateLock); + if (synchronous) { + mTransactionPending = true; + } + if (syncInput) { + mPendingSyncInputWindows = true; + } - if (flags & eAnimation) { - // For window updates that are part of an animation we must wait for - // previous animation "frames" to be handled. - while (!isMainThread && mAnimTransactionPending) { + // applyTransactionState can be called by either the main SF thread or by + // another process through setTransactionState. While a given process may wish + // to wait on synchronous transactions, the main SF thread should never + // be blocked. Therefore, we only wait if isMainThread is false. + while (mTransactionPending || mPendingSyncInputWindows) { status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); if (CC_UNLIKELY(err != NO_ERROR)) { // just in case something goes wrong in SF, return to the - // caller after a few seconds. - ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out " - "waiting for previous animation frame"); - mAnimTransactionPending = false; + // called after a few seconds. + ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!"); + mTransactionPending = false; + mPendingSyncInputWindows = false; break; } } } + return NO_ERROR; +} + +void SurfaceFlinger::flushTransactionQueue() { + std::vector<TransactionState> transactionQueue; + { + Mutex::Autolock _l(mQueueLock); + if (!mTransactionQueue.empty()) { + transactionQueue.swap(mTransactionQueue); + } + mTransactionQueueCV.broadcast(); + } + + Mutex::Autolock _l(mStateLock); + for (const auto& t : transactionQueue) { + applyTransactionState(t.frameTimelineVsyncId, t.states, t.displays, t.flags, + t.inputWindowCommands, t.desiredPresentTime, t.isAutoTimestamp, + t.buffer, t.postTime, t.privileged, t.hasListenerCallbacks, + t.listenerCallbacks, t.originPid, t.originUid, t.id); + } +} + +void SurfaceFlinger::applyTransactionState(int64_t frameTimelineVsyncId, + const Vector<ComposerState>& states, + const Vector<DisplayState>& displays, uint32_t flags, + const InputWindowCommands& inputWindowCommands, + const int64_t desiredPresentTime, bool isAutoTimestamp, + const client_cache_t& uncacheBuffer, + const int64_t postTime, bool privileged, + bool hasListenerCallbacks, + const std::vector<ListenerCallbacks>& listenerCallbacks, + int originPid, int originUid, uint64_t transactionId) { + uint32_t transactionFlags = 0; for (const DisplayState& display : displays) { transactionFlags |= setDisplayStateLocked(display); @@ -3477,80 +3539,25 @@ void SurfaceFlinger::applyTransactionState( transactionFlags = eTransactionNeeded; } - // If we are on the main thread, we are about to preform a traversal. Clear the traversal bit - // so we don't have to wake up again next frame to preform an uneeded traversal. - if (isMainThread && (transactionFlags & eTraversalNeeded)) { - transactionFlags = transactionFlags & (~eTraversalNeeded); - mForceTraversal = true; - } - - const auto schedule = [](uint32_t flags) { - if (flags & eEarlyWakeup) return TransactionSchedule::Early; - if (flags & eExplicitEarlyWakeupEnd) return TransactionSchedule::EarlyEnd; - if (flags & eExplicitEarlyWakeupStart) return TransactionSchedule::EarlyStart; - return TransactionSchedule::Late; - }(flags); - if (transactionFlags) { if (mInterceptor->isEnabled()) { mInterceptor->saveTransaction(states, mCurrentState.displays, displays, flags, originPid, originUid, transactionId); } - // TODO(b/159125966): Remove eEarlyWakeup completly as no client should use this flag - if (flags & eEarlyWakeup) { - ALOGW("eEarlyWakeup is deprecated. Use eExplicitEarlyWakeup[Start|End]"); + // We are on the main thread, we are about to preform a traversal. Clear the traversal bit + // so we don't have to wake up again next frame to preform an unnecessary traversal. + if (transactionFlags & eTraversalNeeded) { + transactionFlags = transactionFlags & (~eTraversalNeeded); + mForceTraversal = true; } - - if (!privileged && (flags & (eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd))) { - ALOGE("Only WindowManager is allowed to use eExplicitEarlyWakeup[Start|End] flags"); - flags &= ~(eExplicitEarlyWakeupStart | eExplicitEarlyWakeupEnd); + if (transactionFlags) { + setTransactionFlags(transactionFlags); } - // this triggers the transaction - setTransactionFlags(transactionFlags, schedule); - if (flags & eAnimation) { mAnimTransactionPending = true; } - - // if this is a synchronous transaction, wait for it to take effect - // before returning. - const bool synchronous = flags & eSynchronous; - const bool syncInput = inputWindowCommands.syncInputWindows; - if (!synchronous && !syncInput) { - return; - } - - if (synchronous) { - mTransactionPending = true; - } - if (syncInput) { - mPendingSyncInputWindows = true; - } - - - // applyTransactionState can be called by either the main SF thread or by - // another process through setTransactionState. While a given process may wish - // to wait on synchronous transactions, the main SF thread should never - // be blocked. Therefore, we only wait if isMainThread is false. - while (!isMainThread && (mTransactionPending || mPendingSyncInputWindows)) { - status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); - if (CC_UNLIKELY(err != NO_ERROR)) { - // just in case something goes wrong in SF, return to the - // called after a few seconds. - ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!"); - mTransactionPending = false; - mPendingSyncInputWindows = false; - break; - } - } - } else { - // Update VsyncModulator state machine even if transaction is not needed. - if (schedule == TransactionSchedule::EarlyStart || - schedule == TransactionSchedule::EarlyEnd) { - modulateVsync(&VsyncModulator::setTransactionSchedule, schedule); - } } } @@ -3840,7 +3847,9 @@ uint32_t SurfaceFlinger::setClientStateLocked( ALOGE("Attempt to update InputWindowInfo without permission ACCESS_SURFACE_FLINGER"); } } + std::optional<nsecs_t> dequeueBufferTimestamp; if (what & layer_state_t::eMetadataChanged) { + dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME); if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded; } if (what & layer_state_t::eColorSpaceAgnosticChanged) { @@ -3928,7 +3937,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( : layer->getHeadFrameNumber(-1 /* expectedPresentTime */) + 1; if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp, - s.cachedBuffer, frameNumber)) { + s.cachedBuffer, frameNumber, dequeueBufferTimestamp)) { flags |= eTraversalNeeded; } } @@ -3940,7 +3949,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( } uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) { - bool hasChanges = mPendingInputWindowCommands.merge(inputWindowCommands); + bool hasChanges = mInputWindowCommands.merge(inputWindowCommands); return hasChanges ? eTraversalNeeded : 0; } @@ -4205,9 +4214,11 @@ void SurfaceFlinger::onInitializeDisplays() { d.width = 0; d.height = 0; displays.add(d); - setTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, nullptr, - mPendingInputWindowCommands, systemTime(), true, {}, false, {}, - 0 /* Undefined transactionId */); + + // This called on the main thread, apply it directly. + applyTransactionState(ISurfaceComposer::INVALID_VSYNC_ID, state, displays, 0, + mInputWindowCommands, systemTime(), true, {}, systemTime(), true, false, + {}, getpid(), getuid(), 0 /* Undefined transactionId */); setPowerModeInternal(display, hal::PowerMode::ON); const nsecs_t vsyncPeriod = mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod(); @@ -4507,12 +4518,9 @@ void SurfaceFlinger::recordBufferingStats(const std::string& layerName, void SurfaceFlinger::dumpFrameEventsLocked(std::string& result) { result.append("Layer frame timestamps:\n"); - - const LayerVector& currentLayers = mCurrentState.layersSortedByZ; - const size_t count = currentLayers.size(); - for (size_t i=0 ; i<count ; i++) { - currentLayers[i]->dumpFrameEvents(result); - } + // Traverse all layers to dump frame-events for each layer + mCurrentState.traverseInZOrder( + [&] (Layer* layer) { layer->dumpFrameEvents(result); }); } void SurfaceFlinger::dumpBufferingStats(std::string& result) const { @@ -5387,7 +5395,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r void SurfaceFlinger::repaintEverything() { mRepaintEverything = true; - signalTransaction(); + setTransactionFlags(eTransactionNeeded); } void SurfaceFlinger::repaintEverythingForHWC() { @@ -5406,16 +5414,16 @@ void SurfaceFlinger::kernelTimerChanged(bool expired) { // mRefreshRateConfigs->getCurrentRefreshRate() static_cast<void>(schedule([=] { const auto desiredActiveConfig = getDesiredActiveConfig(); - const auto& current = desiredActiveConfig - ? mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig->configId) - : mRefreshRateConfigs->getCurrentRefreshRate(); - const auto& min = mRefreshRateConfigs->getMinRefreshRate(); - - if (current != min) { - const bool timerExpired = mKernelIdleTimerEnabled && expired; - + const std::optional<HwcConfigIndexType> desiredConfigId = desiredActiveConfig + ? std::make_optional(desiredActiveConfig->configId) + : std::nullopt; + + const bool timerExpired = mKernelIdleTimerEnabled && expired; + const auto newRefreshRate = + mRefreshRateConfigs->onKernelTimerChanged(desiredConfigId, timerExpired); + if (newRefreshRate) { if (Mutex::Autolock lock(mStateLock); mRefreshRateOverlay) { - mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current); + mRefreshRateOverlay->changeRefreshRate(*newRefreshRate); } mEventQueue->invalidate(); } @@ -6065,7 +6073,7 @@ status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal( toggleKernelIdleTimer(); auto configId = mScheduler->getPreferredConfigId(); - auto& preferredRefreshRate = configId + auto preferredRefreshRate = configId ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId) // NOTE: Choose the default config ID, if Scheduler doesn't have one in mind. : mRefreshRateConfigs->getRefreshRateFromConfigId(currentPolicy.defaultConfig); @@ -6371,7 +6379,8 @@ void SurfaceFlinger::enableRefreshRateOverlay(bool enable) { mRefreshRateOverlay->setViewport(display->getSize()); } - mRefreshRateOverlay->changeRefreshRate(mRefreshRateConfigs->getCurrentRefreshRate()); + mRefreshRateOverlay->changeRefreshRate( + mRefreshRateConfigs->getCurrentRefreshRate().getFps()); } })); } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 9f0f2eaf5d..bae15dca51 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -99,6 +99,8 @@ class RenderArea; class TimeStats; class FrameTracer; +using gui::ScreenCaptureResults; + namespace frametimeline { class FrameTimeline; } @@ -434,15 +436,16 @@ private: struct TransactionState { TransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, - int64_t desiredPresentTime, bool isAutoTimestamp, - const client_cache_t& uncacheBuffer, int64_t postTime, bool privileged, - bool hasListenerCallbacks, + const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime, + bool isAutoTimestamp, const client_cache_t& uncacheBuffer, + int64_t postTime, bool privileged, bool hasListenerCallbacks, std::vector<ListenerCallbacks> listenerCallbacks, int originPid, int originUid, uint64_t transactionId) : frameTimelineVsyncId(frameTimelineVsyncId), states(composerStates), displays(displayStates), flags(transactionFlags), + inputWindowCommands(inputWindowCommands), desiredPresentTime(desiredPresentTime), isAutoTimestamp(isAutoTimestamp), buffer(uncacheBuffer), @@ -458,6 +461,7 @@ private: Vector<ComposerState> states; Vector<DisplayState> displays; uint32_t flags; + InputWindowCommands inputWindowCommands; const int64_t desiredPresentTime; const bool isAutoTimestamp; client_cache_t buffer; @@ -723,6 +727,7 @@ private: /* * Transactions */ + void flushTransactionQueue(); void applyTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, @@ -730,10 +735,12 @@ private: const client_cache_t& uncacheBuffer, const int64_t postTime, bool privileged, bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks, - int originPid, int originUid, uint64_t transactionId, - bool isMainThread = false) REQUIRES(mStateLock); + int originPid, int originUid, uint64_t transactionId) + REQUIRES(mStateLock); // Returns true if at least one transaction was flushed bool flushTransactionQueues(); + // flush pending transaction that was presented after desiredPresentTime. + void flushPendingTransactionQueues(); // Returns true if there is at least one transaction that needs to be flushed bool transactionFlushNeeded(); uint32_t getTransactionFlags(uint32_t flags); @@ -751,7 +758,7 @@ private: void commitOffscreenLayers(); bool transactionIsReadyToBeApplied(int64_t desiredPresentTime, const Vector<ComposerState>& states, - bool updateTransactionCounters = false) REQUIRES(mStateLock); + bool updateTransactionCounters = false); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); @@ -1168,8 +1175,11 @@ private: uint32_t mTexturePoolSize = 0; std::vector<uint32_t> mTexturePool; - std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> mTransactionQueues; - + mutable Mutex mQueueLock; + Condition mTransactionQueueCV; + std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash> + mPendingTransactionQueues GUARDED_BY(mQueueLock); + std::vector<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock); /* * Feature prototyping */ @@ -1246,7 +1256,6 @@ private: const float mEmulatedDisplayDensity; sp<os::IInputFlinger> mInputFlinger; - InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock); // Should only be accessed by the main thread. InputWindowCommands mInputWindowCommands; diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index 97725ec309..c043866354 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -376,5 +376,9 @@ bool update_device_product_info_on_hotplug_reconnect(bool defaultValue) { defaultValue); } +bool enable_frame_rate_override(bool defaultValue) { + return SurfaceFlingerProperties::enable_frame_rate_override().value_or(defaultValue); +} + } // namespace sysprop } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 37a6b4011c..816cb092d0 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -98,6 +98,8 @@ android::ui::DisplayPrimaries getDisplayNativePrimaries(); bool update_device_product_info_on_hotplug_reconnect(bool defaultValue); +bool enable_frame_rate_override(bool defaultValue); + } // namespace sysprop } // namespace android #endif // SURFACEFLINGERPROPERTIES_H_ diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index 421484ff73..4d25a7a2c2 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -445,3 +445,12 @@ prop { access: Readonly prop_name: "ro.surface_flinger.update_device_product_info_on_hotplug_reconnect" } + +# Enables the frame rate override feature +prop { + api_name: "enable_frame_rate_override" + type: Boolean + scope: Public + access: Readonly + prop_name: "ro.surface_flinger.enable_frame_rate_override" +} diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt index da66ecef81..0e0be09d41 100644 --- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt +++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt @@ -41,6 +41,10 @@ props { prop_name: "ro.surface_flinger.display_update_imminent_timeout_ms" } prop { + api_name: "enable_frame_rate_override" + prop_name: "ro.surface_flinger.enable_frame_rate_override" + } + prop { api_name: "enable_protected_contents" prop_name: "ro.surface_flinger.protected_contents" } diff --git a/services/surfaceflinger/tests/LayerState_test.cpp b/services/surfaceflinger/tests/LayerState_test.cpp index e66df4a2b9..ecfed13adb 100644 --- a/services/surfaceflinger/tests/LayerState_test.cpp +++ b/services/surfaceflinger/tests/LayerState_test.cpp @@ -22,6 +22,8 @@ #include <gui/LayerState.h> namespace android { +using gui::ScreenCaptureResults; + namespace test { TEST(LayerStateTest, ParcellingDisplayCaptureArgs) { @@ -86,11 +88,11 @@ TEST(LayerStateTest, ParcellingScreenCaptureResults) { results.result = BAD_VALUE; Parcel p; - results.write(p); + results.writeToParcel(&p); p.setDataPosition(0); ScreenCaptureResults results2; - results2.read(p); + results2.readFromParcel(&p); // GraphicBuffer object is reallocated so compare the data in the graphic buffer // rather than the object itself diff --git a/services/surfaceflinger/tests/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h index a361b1e956..3ef63d5626 100644 --- a/services/surfaceflinger/tests/TransactionTestHarnesses.h +++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h @@ -68,6 +68,9 @@ public: Rect(displayState.layerStackSpaceRect), Rect(resolution)); t.apply(); SurfaceComposerClient::Transaction().apply(true); + // wait for 3 vsyncs to ensure the buffer is latched. + usleep(static_cast<int32_t>(1e6 / displayConfig.refreshRate) * 3); + BufferItem item; itemConsumer->acquireBuffer(&item, 0, true); auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer); diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp index 169698ba70..e2584e266d 100644 --- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp @@ -33,8 +33,13 @@ using namespace std::chrono_literals; using testing::AtLeast; using testing::Contains; using FrameTimelineEvent = perfetto::protos::FrameTimelineEvent; -using ProtoDisplayFrame = perfetto::protos::FrameTimelineEvent_DisplayFrame; -using ProtoSurfaceFrame = perfetto::protos::FrameTimelineEvent_SurfaceFrame; +using ProtoExpectedDisplayFrameStart = + perfetto::protos::FrameTimelineEvent_ExpectedDisplayFrameStart; +using ProtoExpectedSurfaceFrameStart = + perfetto::protos::FrameTimelineEvent_ExpectedSurfaceFrameStart; +using ProtoActualDisplayFrameStart = perfetto::protos::FrameTimelineEvent_ActualDisplayFrameStart; +using ProtoActualSurfaceFrameStart = perfetto::protos::FrameTimelineEvent_ActualSurfaceFrameStart; +using ProtoFrameEnd = perfetto::protos::FrameTimelineEvent_FrameEnd; using ProtoPresentType = perfetto::protos::FrameTimelineEvent_PresentType; using ProtoJankType = perfetto::protos::FrameTimelineEvent_JankType; @@ -67,10 +72,11 @@ public: void SetUp() override { mTimeStats = std::make_shared<mock::TimeStats>(); - mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, mSurfaceFlingerPid, + mFrameTimeline = std::make_unique<impl::FrameTimeline>(mTimeStats, kSurfaceFlingerPid, kTestThresholds); mFrameTimeline->registerDataSource(); mTokenManager = &mFrameTimeline->mTokenManager; + mTraceCookieCounter = &mFrameTimeline->mTraceCookieCounter; maxDisplayFrames = &mFrameTimeline->mMaxDisplayFrames; maxTokenRetentionTime = mTokenManager->kMaxRetentionTime; } @@ -130,22 +136,31 @@ public: a.presentTime == b.presentTime; } - const std::map<int64_t, TokenManagerPrediction>& getPredictions() { + const std::map<int64_t, TokenManagerPrediction>& getPredictions() const { return mTokenManager->mPredictions; } - uint32_t getNumberOfDisplayFrames() { + uint32_t getNumberOfDisplayFrames() const { std::lock_guard<std::mutex> lock(mFrameTimeline->mMutex); return static_cast<uint32_t>(mFrameTimeline->mDisplayFrames.size()); } + int64_t snoopCurrentTraceCookie() const { return mTraceCookieCounter->mTraceCookie; } + + void flushTrace() { + using FrameTimelineDataSource = impl::FrameTimeline::FrameTimelineDataSource; + FrameTimelineDataSource::Trace( + [&](FrameTimelineDataSource::TraceContext ctx) { ctx.Flush(); }); + } + std::shared_ptr<mock::TimeStats> mTimeStats; std::unique_ptr<impl::FrameTimeline> mFrameTimeline; impl::TokenManager* mTokenManager; + TraceCookieCounter* mTraceCookieCounter; FenceToFenceTimeMap fenceFactory; uint32_t* maxDisplayFrames; nsecs_t maxTokenRetentionTime; - pid_t mSurfaceFlingerPid = 666; + static constexpr pid_t kSurfaceFlingerPid = 666; static constexpr nsecs_t kPresentThreshold = std::chrono::duration_cast<std::chrono::nanoseconds>(2ns).count(); static constexpr nsecs_t kDeadlineThreshold = @@ -549,7 +564,7 @@ TEST_F(FrameTimelineTest, tracing_noPacketsSentWithoutTraceStart) { TEST_F(FrameTimelineTest, tracing_sanityTest) { auto tracingSession = getTracingSessionForTest(); // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -574,23 +589,18 @@ TEST_F(FrameTimelineTest, tracing_sanityTest) { mFrameTimeline->setSfPresent(55, presentFence2); presentFence2->signalForTest(55); - // The SurfaceFrame packet from the first frame is emitted, but not flushed yet. Emitting a new - // packet will flush it. To emit a new packet, we'll need to call flushPendingPresentFences() - // again, which is done by setSfPresent(). - addEmptyDisplayFrame(); + flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - // Display Frame 1 has two packets - DisplayFrame and a SurfaceFrame. - // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not - // flushed through traced, so this is not counted. - EXPECT_EQ(packets.size(), 2); + // Display Frame 1 has 8 packets - 4 from DisplayFrame and 4 from SurfaceFrame. + EXPECT_EQ(packets.size(), 8); } TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) { auto tracingSession = getTracingSessionForTest(); // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -608,20 +618,17 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_invalidTokenDoesNotEmitTracePacket) mFrameTimeline->setSfPresent(55, presentFence2); presentFence2->signalForTest(60); - addEmptyDisplayFrame(); + flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - // Display Frame 1 has no packets. - // Display Frame 2 has one packet - DisplayFrame. However, this packet has - // been emitted but not flushed through traced, so this is not counted. EXPECT_EQ(packets.size(), 0); } TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) { auto tracingSession = getTracingSessionForTest(); // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -644,21 +651,38 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_invalidTokenDoesNotEmitTracePacket) mFrameTimeline->setSfPresent(55, presentFence2); presentFence2->signalForTest(60); - addEmptyDisplayFrame(); + flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - // Display Frame 1 has one packet - DisplayFrame (SurfaceFrame shouldn't be traced since it has - // an invalid token). - // Display Frame 2 has one packet - DisplayFrame. However, this packet has - // been emitted but not flushed through traced, so this is not counted. - EXPECT_EQ(packets.size(), 1); + // Display Frame 1 has 4 packets (SurfaceFrame shouldn't be traced since it has an invalid + // token). + EXPECT_EQ(packets.size(), 4); +} + +void validateTraceEvent(const ProtoExpectedDisplayFrameStart& received, + const ProtoExpectedDisplayFrameStart& source) { + ASSERT_TRUE(received.has_cookie()); + EXPECT_EQ(received.cookie(), source.cookie()); + + ASSERT_TRUE(received.has_token()); + EXPECT_EQ(received.token(), source.token()); + + ASSERT_TRUE(received.has_pid()); + EXPECT_EQ(received.pid(), source.pid()); } -void validateDisplayFrameEvent(const ProtoDisplayFrame& received, const ProtoDisplayFrame& source) { +void validateTraceEvent(const ProtoActualDisplayFrameStart& received, + const ProtoActualDisplayFrameStart& source) { + ASSERT_TRUE(received.has_cookie()); + EXPECT_EQ(received.cookie(), source.cookie()); + ASSERT_TRUE(received.has_token()); EXPECT_EQ(received.token(), source.token()); + ASSERT_TRUE(received.has_pid()); + EXPECT_EQ(received.pid(), source.pid()); + ASSERT_TRUE(received.has_present_type()); EXPECT_EQ(received.present_type(), source.present_type()); ASSERT_TRUE(received.has_on_time_finish()); @@ -667,25 +691,43 @@ void validateDisplayFrameEvent(const ProtoDisplayFrame& received, const ProtoDis EXPECT_EQ(received.gpu_composition(), source.gpu_composition()); ASSERT_TRUE(received.has_jank_type()); EXPECT_EQ(received.jank_type(), source.jank_type()); +} - ASSERT_TRUE(received.has_expected_start_ns()); - EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns()); - ASSERT_TRUE(received.has_expected_end_ns()); - EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns()); +void validateTraceEvent(const ProtoExpectedSurfaceFrameStart& received, + const ProtoExpectedSurfaceFrameStart& source) { + ASSERT_TRUE(received.has_cookie()); + EXPECT_EQ(received.cookie(), source.cookie()); - ASSERT_TRUE(received.has_actual_start_ns()); - EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns()); - ASSERT_TRUE(received.has_actual_end_ns()); - EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns()); + ASSERT_TRUE(received.has_token()); + EXPECT_EQ(received.token(), source.token()); + + ASSERT_TRUE(received.has_display_frame_token()); + EXPECT_EQ(received.display_frame_token(), source.display_frame_token()); + + ASSERT_TRUE(received.has_pid()); + EXPECT_EQ(received.pid(), source.pid()); + + ASSERT_TRUE(received.has_layer_name()); + EXPECT_EQ(received.layer_name(), source.layer_name()); } -void validateSurfaceFrameEvent(const ProtoSurfaceFrame& received, const ProtoSurfaceFrame& source) { +void validateTraceEvent(const ProtoActualSurfaceFrameStart& received, + const ProtoActualSurfaceFrameStart& source) { + ASSERT_TRUE(received.has_cookie()); + EXPECT_EQ(received.cookie(), source.cookie()); + ASSERT_TRUE(received.has_token()); EXPECT_EQ(received.token(), source.token()); ASSERT_TRUE(received.has_display_frame_token()); EXPECT_EQ(received.display_frame_token(), source.display_frame_token()); + ASSERT_TRUE(received.has_pid()); + EXPECT_EQ(received.pid(), source.pid()); + + ASSERT_TRUE(received.has_layer_name()); + EXPECT_EQ(received.layer_name(), source.layer_name()); + ASSERT_TRUE(received.has_present_type()); EXPECT_EQ(received.present_type(), source.present_type()); ASSERT_TRUE(received.has_on_time_finish()); @@ -694,27 +736,17 @@ void validateSurfaceFrameEvent(const ProtoSurfaceFrame& received, const ProtoSur EXPECT_EQ(received.gpu_composition(), source.gpu_composition()); ASSERT_TRUE(received.has_jank_type()); EXPECT_EQ(received.jank_type(), source.jank_type()); +} - ASSERT_TRUE(received.has_expected_start_ns()); - EXPECT_EQ(received.expected_start_ns(), source.expected_start_ns()); - ASSERT_TRUE(received.has_expected_end_ns()); - EXPECT_EQ(received.expected_end_ns(), source.expected_end_ns()); - - ASSERT_TRUE(received.has_actual_start_ns()); - EXPECT_EQ(received.actual_start_ns(), source.actual_start_ns()); - ASSERT_TRUE(received.has_actual_end_ns()); - EXPECT_EQ(received.actual_end_ns(), source.actual_end_ns()); - - ASSERT_TRUE(received.has_layer_name()); - EXPECT_EQ(received.layer_name(), source.layer_name()); - ASSERT_TRUE(received.has_pid()); - EXPECT_EQ(received.pid(), source.pid()); +void validateTraceEvent(const ProtoFrameEnd& received, const ProtoFrameEnd& source) { + ASSERT_TRUE(received.has_cookie()); + EXPECT_EQ(received.cookie(), source.cookie()); } TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { auto tracingSession = getTracingSessionForTest(); // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -727,16 +759,27 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { mFrameTimeline->setSfPresent(26, presentFence1); presentFence1->signalForTest(31); - ProtoDisplayFrame protoDisplayFrame; - protoDisplayFrame.set_token(displayFrameToken1); - protoDisplayFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); - protoDisplayFrame.set_on_time_finish(true); - protoDisplayFrame.set_gpu_composition(false); - protoDisplayFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); - protoDisplayFrame.set_expected_start_ns(10); - protoDisplayFrame.set_expected_end_ns(25); - protoDisplayFrame.set_actual_start_ns(20); - protoDisplayFrame.set_actual_end_ns(26); + int64_t traceCookie = snoopCurrentTraceCookie(); + ProtoExpectedDisplayFrameStart protoExpectedDisplayFrameStart; + protoExpectedDisplayFrameStart.set_cookie(traceCookie + 1); + protoExpectedDisplayFrameStart.set_token(displayFrameToken1); + protoExpectedDisplayFrameStart.set_pid(kSurfaceFlingerPid); + + ProtoFrameEnd protoExpectedDisplayFrameEnd; + protoExpectedDisplayFrameEnd.set_cookie(traceCookie + 1); + + ProtoActualDisplayFrameStart protoActualDisplayFrameStart; + protoActualDisplayFrameStart.set_cookie(traceCookie + 2); + protoActualDisplayFrameStart.set_token(displayFrameToken1); + protoActualDisplayFrameStart.set_pid(kSurfaceFlingerPid); + protoActualDisplayFrameStart.set_present_type( + ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); + protoActualDisplayFrameStart.set_on_time_finish(true); + protoActualDisplayFrameStart.set_gpu_composition(false); + protoActualDisplayFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); + + ProtoFrameEnd protoActualDisplayFrameEnd; + protoActualDisplayFrameEnd.set_cookie(traceCookie + 2); // Trigger a flushPresentFence (which will call trace function) by calling setSfPresent for the // next frame @@ -744,30 +787,61 @@ TEST_F(FrameTimelineTest, traceDisplayFrame_emitsValidTracePacket) { mFrameTimeline->setSfPresent(55, presentFence2); presentFence2->signalForTest(55); - addEmptyDisplayFrame(); + flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - // Display Frame 1 has one packet - DisplayFrame. - // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not - // flushed through traced, so this is not counted. - EXPECT_EQ(packets.size(), 1); - - const auto& packet = packets[0]; - ASSERT_TRUE(packet.has_timestamp()); - ASSERT_TRUE(packet.has_frame_timeline_event()); - - const auto& event = packet.frame_timeline_event(); - ASSERT_TRUE(event.has_display_frame()); - ASSERT_FALSE(event.has_surface_frame()); - const auto& displayFrameEvent = event.display_frame(); - validateDisplayFrameEvent(displayFrameEvent, protoDisplayFrame); + EXPECT_EQ(packets.size(), 4); + + // Packet - 0 : ExpectedDisplayFrameStart + const auto& packet0 = packets[0]; + ASSERT_TRUE(packet0.has_timestamp()); + EXPECT_EQ(packet0.timestamp(), 10); + ASSERT_TRUE(packet0.has_frame_timeline_event()); + + const auto& event0 = packet0.frame_timeline_event(); + ASSERT_TRUE(event0.has_expected_display_frame_start()); + const auto& expectedDisplayFrameStart = event0.expected_display_frame_start(); + validateTraceEvent(expectedDisplayFrameStart, protoExpectedDisplayFrameStart); + + // Packet - 1 : FrameEnd (ExpectedDisplayFrame) + const auto& packet1 = packets[1]; + ASSERT_TRUE(packet1.has_timestamp()); + EXPECT_EQ(packet1.timestamp(), 25); + ASSERT_TRUE(packet1.has_frame_timeline_event()); + + const auto& event1 = packet1.frame_timeline_event(); + ASSERT_TRUE(event1.has_frame_end()); + const auto& expectedDisplayFrameEnd = event1.frame_end(); + validateTraceEvent(expectedDisplayFrameEnd, protoExpectedDisplayFrameEnd); + + // Packet - 2 : ActualDisplayFrameStart + const auto& packet2 = packets[2]; + ASSERT_TRUE(packet2.has_timestamp()); + EXPECT_EQ(packet2.timestamp(), 20); + ASSERT_TRUE(packet2.has_frame_timeline_event()); + + const auto& event2 = packet2.frame_timeline_event(); + ASSERT_TRUE(event2.has_actual_display_frame_start()); + const auto& actualDisplayFrameStart = event2.actual_display_frame_start(); + validateTraceEvent(actualDisplayFrameStart, protoActualDisplayFrameStart); + + // Packet - 3 : FrameEnd (ActualDisplayFrame) + const auto& packet3 = packets[3]; + ASSERT_TRUE(packet3.has_timestamp()); + EXPECT_EQ(packet3.timestamp(), 26); + ASSERT_TRUE(packet3.has_frame_timeline_event()); + + const auto& event3 = packet3.frame_timeline_event(); + ASSERT_TRUE(event3.has_frame_end()); + const auto& actualDisplayFrameEnd = event3.frame_end(); + validateTraceEvent(actualDisplayFrameEnd, protoActualDisplayFrameEnd); } TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { auto tracingSession = getTracingSessionForTest(); // Global increment - EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)).Times(2); + EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_)); // Layer specific increment EXPECT_CALL(*mTimeStats, incrementJankyFrames(testing::_, testing::_, testing::_)); auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); @@ -785,19 +859,33 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { surfaceFrame1->setActualQueueTime(15); surfaceFrame1->setAcquireFenceTime(20); - ProtoSurfaceFrame protoSurfaceFrame; - protoSurfaceFrame.set_token(surfaceFrameToken); - protoSurfaceFrame.set_display_frame_token(displayFrameToken1); - protoSurfaceFrame.set_present_type(ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); - protoSurfaceFrame.set_on_time_finish(true); - protoSurfaceFrame.set_gpu_composition(false); - protoSurfaceFrame.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); - protoSurfaceFrame.set_expected_start_ns(10); - protoSurfaceFrame.set_expected_end_ns(25); - protoSurfaceFrame.set_actual_start_ns(0); - protoSurfaceFrame.set_actual_end_ns(20); - protoSurfaceFrame.set_layer_name(sLayerNameOne); - protoSurfaceFrame.set_pid(sPidOne); + // First 2 cookies will be used by the DisplayFrame + int64_t traceCookie = snoopCurrentTraceCookie() + 2; + + ProtoExpectedSurfaceFrameStart protoExpectedSurfaceFrameStart; + protoExpectedSurfaceFrameStart.set_cookie(traceCookie + 1); + protoExpectedSurfaceFrameStart.set_token(surfaceFrameToken); + protoExpectedSurfaceFrameStart.set_display_frame_token(displayFrameToken1); + protoExpectedSurfaceFrameStart.set_pid(sPidOne); + protoExpectedSurfaceFrameStart.set_layer_name(sLayerNameOne); + + ProtoFrameEnd protoExpectedSurfaceFrameEnd; + protoExpectedSurfaceFrameEnd.set_cookie(traceCookie + 1); + + ProtoActualSurfaceFrameStart protoActualSurfaceFrameStart; + protoActualSurfaceFrameStart.set_cookie(traceCookie + 2); + protoActualSurfaceFrameStart.set_token(surfaceFrameToken); + protoActualSurfaceFrameStart.set_display_frame_token(displayFrameToken1); + protoActualSurfaceFrameStart.set_pid(sPidOne); + protoActualSurfaceFrameStart.set_layer_name(sLayerNameOne); + protoActualSurfaceFrameStart.set_present_type( + ProtoPresentType(FrameTimelineEvent::PRESENT_ON_TIME)); + protoActualSurfaceFrameStart.set_on_time_finish(true); + protoActualSurfaceFrameStart.set_gpu_composition(false); + protoActualSurfaceFrameStart.set_jank_type(ProtoJankType(FrameTimelineEvent::JANK_NONE)); + + ProtoFrameEnd protoActualSurfaceFrameEnd; + protoActualSurfaceFrameEnd.set_cookie(traceCookie + 2); // Set up the display frame mFrameTimeline->setSfWakeUp(displayFrameToken1, 20, 11); @@ -812,24 +900,55 @@ TEST_F(FrameTimelineTest, traceSurfaceFrame_emitsValidTracePacket) { mFrameTimeline->setSfPresent(55, presentFence2); presentFence2->signalForTest(55); - addEmptyDisplayFrame(); + flushTrace(); tracingSession->StopBlocking(); auto packets = readFrameTimelinePacketsBlocking(tracingSession.get()); - // Display Frame 1 has one packet - DisplayFrame and a SurfaceFrame. - // Display Frame 2 has one packet - DisplayFrame. However, this packet has been emitted but not - // flushed through traced, so this is not counted. - EXPECT_EQ(packets.size(), 2); - - const auto& packet = packets[1]; - ASSERT_TRUE(packet.has_timestamp()); - ASSERT_TRUE(packet.has_frame_timeline_event()); - - const auto& event = packet.frame_timeline_event(); - ASSERT_TRUE(!event.has_display_frame()); - ASSERT_TRUE(event.has_surface_frame()); - const auto& surfaceFrameEvent = event.surface_frame(); - validateSurfaceFrameEvent(surfaceFrameEvent, protoSurfaceFrame); + EXPECT_EQ(packets.size(), 8); + + // Packet - 4 : ExpectedSurfaceFrameStart + const auto& packet4 = packets[4]; + ASSERT_TRUE(packet4.has_timestamp()); + EXPECT_EQ(packet4.timestamp(), 10); + ASSERT_TRUE(packet4.has_frame_timeline_event()); + + const auto& event4 = packet4.frame_timeline_event(); + ASSERT_TRUE(event4.has_expected_surface_frame_start()); + const auto& expectedSurfaceFrameStart = event4.expected_surface_frame_start(); + validateTraceEvent(expectedSurfaceFrameStart, protoExpectedSurfaceFrameStart); + + // Packet - 5 : FrameEnd (ExpectedSurfaceFrame) + const auto& packet5 = packets[5]; + ASSERT_TRUE(packet5.has_timestamp()); + EXPECT_EQ(packet5.timestamp(), 25); + ASSERT_TRUE(packet5.has_frame_timeline_event()); + + const auto& event5 = packet5.frame_timeline_event(); + ASSERT_TRUE(event5.has_frame_end()); + const auto& expectedSurfaceFrameEnd = event5.frame_end(); + validateTraceEvent(expectedSurfaceFrameEnd, protoExpectedSurfaceFrameEnd); + + // Packet - 6 : ActualSurfaceFrameStart + const auto& packet6 = packets[6]; + ASSERT_TRUE(packet6.has_timestamp()); + EXPECT_EQ(packet6.timestamp(), 10); + ASSERT_TRUE(packet6.has_frame_timeline_event()); + + const auto& event6 = packet6.frame_timeline_event(); + ASSERT_TRUE(event6.has_actual_surface_frame_start()); + const auto& actualSurfaceFrameStart = event6.actual_surface_frame_start(); + validateTraceEvent(actualSurfaceFrameStart, protoActualSurfaceFrameStart); + + // Packet - 7 : FrameEnd (ActualSurfaceFrame) + const auto& packet7 = packets[7]; + ASSERT_TRUE(packet7.has_timestamp()); + EXPECT_EQ(packet7.timestamp(), 20); + ASSERT_TRUE(packet7.has_frame_timeline_event()); + + const auto& event7 = packet7.frame_timeline_event(); + ASSERT_TRUE(event7.has_frame_end()); + const auto& actualSurfaceFrameEnd = event7.frame_end(); + validateTraceEvent(actualSurfaceFrameEnd, protoActualSurfaceFrameEnd); } // Tests for Jank classification diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index 45f0ee6d38..0813968334 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -56,6 +56,21 @@ protected: return refreshRateConfigs.mKnownFrameRates; } + RefreshRate getMinRefreshRateByPolicy(const RefreshRateConfigs& refreshRateConfigs) { + std::lock_guard lock(refreshRateConfigs.mLock); + return refreshRateConfigs.getMinRefreshRateByPolicyLocked(); + } + + RefreshRate getMinSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) { + std::lock_guard lock(refreshRateConfigs.mLock); + return *refreshRateConfigs.mMinSupportedRefreshRate; + } + + RefreshRate getMaxSupportedRefreshRate(const RefreshRateConfigs& refreshRateConfigs) { + std::lock_guard lock(refreshRateConfigs.mLock); + return *refreshRateConfigs.mMaxSupportedRefreshRate; + } + // Test config IDs static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0); static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1); @@ -209,13 +224,13 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) { std::make_unique<RefreshRateConfigs>(m60_90Device, /*currentConfigId=*/HWC_CONFIG_ID_60); - const auto& minRate = refreshRateConfigs->getMinRefreshRate(); - const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate(); + const auto& minRate = getMinSupportedRefreshRate(*refreshRateConfigs); + const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); ASSERT_EQ(mExpected60Config, minRate); ASSERT_EQ(mExpected90Config, performanceRate); - const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy(); + const auto& minRateByPolicy = getMinRefreshRateByPolicy(*refreshRateConfigs); const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(minRateByPolicy, minRate); ASSERT_EQ(performanceRateByPolicy, performanceRate); @@ -226,9 +241,9 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differe std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentGroups, /*currentConfigId=*/HWC_CONFIG_ID_60); - const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy(); - const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate(); - const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy(); + const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); + const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); + const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected60Config, minRate); @@ -239,7 +254,7 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differe 0); refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); - const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy(); + const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs); const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected90DifferentGroupConfig, performanceRate); @@ -252,9 +267,9 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differe std::make_unique<RefreshRateConfigs>(m60_90DeviceWithDifferentResolutions, /*currentConfigId=*/HWC_CONFIG_ID_60); - const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy(); - const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate(); - const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy(); + const auto& minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); + const auto& performanceRate = getMaxSupportedRefreshRate(*refreshRateConfigs); + const auto& minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected60Config, minRate); @@ -265,7 +280,7 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differe 0); refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); - const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy(); + const auto& minRate90 = getMinRefreshRateByPolicy(*refreshRateConfigs); const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected90DifferentResolutionConfig, performanceRate); @@ -278,8 +293,8 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) { std::make_unique<RefreshRateConfigs>(m60_90Device, /*currentConfigId=*/HWC_CONFIG_ID_60); - auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy(); - auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy(); + auto minRate = getMinRefreshRateByPolicy(*refreshRateConfigs); + auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected60Config, minRate); ASSERT_EQ(mExpected90Config, performanceRate); @@ -287,8 +302,8 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) { ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_60, {Fps(60), Fps(60)}}), 0); - auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy(); - auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); + auto minRate60 = getMinRefreshRateByPolicy(*refreshRateConfigs); + auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy(); ASSERT_EQ(mExpected60Config, minRate60); ASSERT_EQ(mExpected60Config, performanceRate60); } @@ -298,20 +313,20 @@ TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) { std::make_unique<RefreshRateConfigs>(m60_90Device, /*currentConfigId=*/HWC_CONFIG_ID_60); { - auto& current = refreshRateConfigs->getCurrentRefreshRate(); + auto current = refreshRateConfigs->getCurrentRefreshRate(); EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_60); } refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90); { - auto& current = refreshRateConfigs->getCurrentRefreshRate(); + auto current = refreshRateConfigs->getCurrentRefreshRate(); EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90); } ASSERT_GE(refreshRateConfigs->setDisplayManagerPolicy({HWC_CONFIG_ID_90, {Fps(90), Fps(90)}}), 0); { - auto& current = refreshRateConfigs->getCurrentRefreshRate(); + auto current = refreshRateConfigs->getCurrentRefreshRate(); EXPECT_EQ(current.getConfigId(), HWC_CONFIG_ID_90); } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp index 65efc85b1d..8552e15bb8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HandleTransactionLockedTest.cpp @@ -604,11 +604,11 @@ TEST_F(HandleTransactionLockedTest, processesDisplayLayerStackRectChanges) { EXPECT_EQ(newLayerStackRect, display.mutableDisplayDevice()->getLayerStackSpaceRect()); } -TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) { +TEST_F(HandleTransactionLockedTest, processesDisplayRectChanges) { using Case = NonHwcVirtualDisplayCase; - const Rect oldFrame(0, 0, 0, 0); - const Rect newFrame(0, 0, 123, 456); + const Rect oldDisplayRect(0, 0); + const Rect newDisplayRect(123, 456); // -------------------------------------------------------------------- // Preconditions @@ -618,8 +618,8 @@ TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) { display.inject(); // There is a change to the layerStackSpaceRect state - display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldFrame; - display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newFrame; + display.mutableDrawingDisplayState().orientedDisplaySpaceRect = oldDisplayRect; + display.mutableCurrentDisplayState().orientedDisplaySpaceRect = newDisplayRect; // -------------------------------------------------------------------- // Invocation @@ -629,7 +629,7 @@ TEST_F(HandleTransactionLockedTest, processesDisplayFrameChanges) { // -------------------------------------------------------------------- // Postconditions - EXPECT_EQ(newFrame, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect()); + EXPECT_EQ(newDisplayRect, display.mutableDisplayDevice()->getOrientedDisplaySpaceRect()); } TEST_F(HandleTransactionLockedTest, processesDisplayWidthChanges) { @@ -722,5 +722,61 @@ TEST_F(HandleTransactionLockedTest, processesDisplayHeightChanges) { mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); } +TEST_F(HandleTransactionLockedTest, processesDisplaySizeDisplayRectAndLayerStackRectChanges) { + using Case = NonHwcVirtualDisplayCase; + + constexpr uint32_t kOldWidth = 567; + constexpr uint32_t kOldHeight = 456; + const auto kOldSize = Rect(kOldWidth, kOldHeight); + + constexpr uint32_t kNewWidth = 234; + constexpr uint32_t kNewHeight = 123; + const auto kNewSize = Rect(kNewWidth, kNewHeight); + + // -------------------------------------------------------------------- + // Preconditions + + // A display is set up + auto nativeWindow = new mock::NativeWindow(); + auto displaySurface = new compositionengine::mock::DisplaySurface(); + sp<GraphicBuffer> buf = new GraphicBuffer(); + auto display = Case::Display::makeFakeExistingDisplayInjector(this); + display.setNativeWindow(nativeWindow); + display.setDisplaySurface(displaySurface); + // Setup injection expectations + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_WIDTH, _)) + .WillOnce(DoAll(SetArgPointee<1>(kOldWidth), Return(0))); + EXPECT_CALL(*nativeWindow, query(NATIVE_WINDOW_HEIGHT, _)) + .WillOnce(DoAll(SetArgPointee<1>(kOldHeight), Return(0))); + display.inject(); + + // There is a change to the layerStackSpaceRect state + display.mutableDrawingDisplayState().width = kOldWidth; + display.mutableDrawingDisplayState().height = kOldHeight; + display.mutableDrawingDisplayState().layerStackSpaceRect = kOldSize; + display.mutableDrawingDisplayState().orientedDisplaySpaceRect = kOldSize; + + display.mutableCurrentDisplayState().width = kNewWidth; + display.mutableCurrentDisplayState().height = kNewHeight; + display.mutableCurrentDisplayState().layerStackSpaceRect = kNewSize; + display.mutableCurrentDisplayState().orientedDisplaySpaceRect = kNewSize; + + // -------------------------------------------------------------------- + // Call Expectations + + EXPECT_CALL(*displaySurface, resizeBuffers(kNewWidth, kNewHeight)).Times(1); + + // -------------------------------------------------------------------- + // Invocation + + mFlinger.handleTransactionLocked(eDisplayTransactionNeeded); + + EXPECT_EQ(display.mutableDisplayDevice()->getBounds(), kNewSize); + EXPECT_EQ(display.mutableDisplayDevice()->getWidth(), kNewWidth); + EXPECT_EQ(display.mutableDisplayDevice()->getHeight(), kNewHeight); + EXPECT_EQ(display.mutableDisplayDevice()->getOrientedDisplaySpaceRect(), kNewSize); + EXPECT_EQ(display.mutableDisplayDevice()->getLayerStackSpaceRect(), kNewSize); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index b57d4733d2..739a9b2764 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -23,6 +23,7 @@ #include <compositionengine/impl/Display.h> #include <compositionengine/impl/OutputLayerCompositionState.h> #include <compositionengine/mock/DisplaySurface.h> +#include <gui/ScreenCaptureResults.h> #include "BufferQueueLayer.h" #include "BufferStateLayer.h" @@ -365,7 +366,7 @@ public: return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } - auto& getTransactionQueue() { return mFlinger->mTransactionQueues; } + auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; } auto setTransactionState(int64_t frameTimelineVsyncId, const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, @@ -381,7 +382,7 @@ public: listenerCallbacks, transactionId); } - auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); }; + auto flushPendingTransactionQueues() { return mFlinger->flushPendingTransactionQueues(); }; auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { return mFlinger->onTransact(code, data, reply, flags); diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp index 06275c6b5b..a4a44085f3 100644 --- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp @@ -127,7 +127,7 @@ public: } void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size()); // called in SurfaceFlinger::signalTransaction EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillOnce(Return(systemTime())); @@ -154,12 +154,12 @@ public: } else { EXPECT_LE(returnedTime, applicationTime + s2ns(5)); } - auto transactionQueue = mFlinger.getTransactionQueue(); + auto transactionQueue = mFlinger.getPendingTransactionQueue(); EXPECT_EQ(0, transactionQueue.size()); } void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size()); // called in SurfaceFlinger::signalTransaction EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); @@ -183,12 +183,12 @@ public: nsecs_t returnedTime = systemTime(); EXPECT_LE(returnedTime, applicationSentTime + s2ns(5)); // This transaction should have been placed on the transaction queue - auto transactionQueue = mFlinger.getTransactionQueue(); + auto transactionQueue = mFlinger.getPendingTransactionQueue(); EXPECT_EQ(1, transactionQueue.size()); } void BlockedByPriorTransaction(uint32_t flags, bool syncInputWindows) { - ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size()); // called in SurfaceFlinger::signalTransaction nsecs_t time = systemTime(); EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); @@ -239,7 +239,7 @@ public: } // check that there is one binder on the pending queue. - auto transactionQueue = mFlinger.getTransactionQueue(); + auto transactionQueue = mFlinger.getPendingTransactionQueue(); EXPECT_EQ(1, transactionQueue.size()); auto& [applyToken, transactionStates] = *(transactionQueue.begin()); @@ -258,7 +258,7 @@ public: }; TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { - ASSERT_EQ(0, mFlinger.getTransactionQueue().size()); + ASSERT_EQ(0, mFlinger.getPendingTransactionQueue().size()); // called in SurfaceFlinger::signalTransaction EXPECT_CALL(*mMessageQueue, invalidate()).Times(1); @@ -275,7 +275,7 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { transactionA.isAutoTimestamp, transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks, transactionA.id); - auto& transactionQueue = mFlinger.getTransactionQueue(); + auto& transactionQueue = mFlinger.getPendingTransactionQueue(); ASSERT_EQ(1, transactionQueue.size()); auto& [applyToken, transactionStates] = *(transactionQueue.begin()); @@ -294,9 +294,9 @@ TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) { empty.desiredPresentTime, empty.isAutoTimestamp, empty.uncacheBuffer, mHasListenerCallbacks, mCallbacks, empty.id); - // flush transaction queue should flush as desiredPresentTime has + // flush pending transaction queue should flush as desiredPresentTime has // passed - mFlinger.flushTransactionQueues(); + mFlinger.flushPendingTransactionQueues(); EXPECT_EQ(0, transactionQueue.size()); } |