diff options
251 files changed, 4878 insertions, 1860 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 3e6d2e01f8..a3e29a81f1 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -317,7 +317,7 @@ on late-init && property:ro.boot.fastboot.boottrace= # Only create the tracing instance if persist.mm_events.enabled # Attempting to remove the tracing instance after it has been created # will likely fail with EBUSY as it would be in use by traced_probes. -on post-fs-data && property:persist.mm_events.enabled=true +on mm_events_property_available && property:persist.mm_events.enabled=true # Create MM Events Tracing Instance for Kmem Activity Trigger mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system mkdir /sys/kernel/tracing/instances/mm_events 0755 system system @@ -402,6 +402,9 @@ on post-fs-data && property:persist.mm_events.enabled=true chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace +on property:ro.persistent_properties.ready=true + trigger mm_events_property_available + # Handle hyp tracing instance on late-init && property:ro.boot.hypervisor.vm.supported=1 diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 481c28ef6b..5ee6b155f2 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -1563,6 +1563,13 @@ static void DumpstateLimitedOnly() { RunDumpsys("DROPBOX SYSTEM SERVER CRASHES", {"dropbox", "-p", "system_server_crash"}); RunDumpsys("DROPBOX SYSTEM APP CRASHES", {"dropbox", "-p", "system_app_crash"}); + + printf("========================================================\n"); + printf("== ANR Traces\n"); + printf("========================================================\n"); + + AddAnrTraceFiles(); + printf("========================================================\n"); printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(), ds.progress_->GetMax(), ds.progress_->GetInitialMax()); diff --git a/cmds/evemu-record/README.md b/cmds/evemu-record/README.md new file mode 100644 index 0000000000..5d16d51da0 --- /dev/null +++ b/cmds/evemu-record/README.md @@ -0,0 +1,48 @@ +# `evemu-record` + +This is a Rust implementation of the `evemu-record` command from the [FreeDesktop project's evemu +suite][FreeDesktop]. It records the descriptor and events produced by a single input device in a +[simple text-based format][format] that can be replayed using the [`uinput` command on +Android][uinput] or the FreeDesktop evemu tools on other Linux-based platforms. It is included by +default with `userdebug` and `eng` builds of Android. + +The command-line interface is the same as that of the FreeDesktop version, except for +Android-specific features. For usage instructions, run `evemu-record --help`. + +## Usage example + +From a computer connected to the device over ADB, you can start a recording: + +``` +$ adb shell evemu-record > my-recording.evemu +Available devices: +/dev/input/event0: gpio_keys +/dev/input/event1: s2mpg12-power-keys +/dev/input/event2: NVTCapacitiveTouchScreen +/dev/input/event3: NVTCapacitivePen +/dev/input/event4: uinput-folio +/dev/input/event5: ACME Touchpad +Select the device event number [0-5]: 5 +``` + +...then use the input device for a while, and press Ctrl+C to finish. You will now have a +`my-recording.evemu` file that you can examine in a text editor. To replay it, use the [`uinput` +command][uinput]: + +``` +$ adb shell uinput - < my-recording.evemu +``` + +## Android-specific features + +### Timestamp bases + +By default, event timestamps are recorded relative to the time of the first event received during +the recording. Passing `--timestamp-base=boot` causes the timestamps to be recorded relative to the +system boot time instead. While this does not affect the playback of the recording, it can be useful +for matching recorded events with other logs that use such timestamps, such as `dmesg` or the +touchpad gesture debug logs emitted by `TouchpadInputMapper`. + +[FreeDesktop]: https://gitlab.freedesktop.org/libevdev/evemu +[format]: https://gitlab.freedesktop.org/libevdev/evemu#device-description-format +[uinput]: https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/cmds/uinput/README.md diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 7478f291dd..4486bd6d3e 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -4017,24 +4017,37 @@ binder::Status InstalldNativeService::enableFsverity(const sp<IFsveritySetupAuth return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Received a null auth token"); } - // Authenticate to check the targeting file is the same inode as the authFd. + // Authenticate to check the targeting file is the same inode as the authFd. With O_PATH, we + // prevent a malicious client from blocking installd by providing a path to FIFO. After the + // authentication, the actual open is safe. sp<IBinder> authTokenBinder = IInterface::asBinder(authToken)->localBinder(); if (authTokenBinder == nullptr) { return exception(binder::Status::EX_SECURITY, "Received a non-local auth token"); } - auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder); - unique_fd rfd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); + unique_fd pathFd(open(filePath.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_PATH)); + // Returns a constant errno to avoid one app probing file existence of the others, before the + // authentication is done. + const int kFixedErrno = EPERM; + if (pathFd.get() < 0) { + PLOG(DEBUG) << "Failed to open the path"; + *_aidl_return = kFixedErrno; + return ok(); + } + std::string procFdPath(StringPrintf("/proc/self/fd/%d", pathFd.get())); struct stat stFromPath; - if (fstat(rfd.get(), &stFromPath) < 0) { - *_aidl_return = errno; + if (stat(procFdPath.c_str(), &stFromPath) < 0) { + PLOG(DEBUG) << "Failed to stat proc fd " << pathFd.get() << " -> " << filePath; + *_aidl_return = kFixedErrno; return ok(); } + auto authTokenInstance = sp<FsveritySetupAuthToken>::cast(authTokenBinder); if (!authTokenInstance->isSameStat(stFromPath)) { LOG(DEBUG) << "FD authentication failed"; - *_aidl_return = EPERM; + *_aidl_return = kFixedErrno; return ok(); } + unique_fd rfd(open(procFdPath.c_str(), O_RDONLY | O_CLOEXEC)); fsverity_enable_arg arg = {}; arg.version = 1; arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index f2b578a8d2..023491fc10 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -194,6 +194,12 @@ static bool exists_renamed_deleted_dir(const std::string& rootDirectory) { }); } +static void unlink_path(const std::string& path) { + if (unlink(path.c_str()) < 0) { + PLOG(DEBUG) << "Failed to unlink " + path; + } +} + class ServiceTest : public testing::Test { protected: InstalldNativeService* service; @@ -555,7 +561,7 @@ protected: TEST_F(FsverityTest, enableFsverity) { const std::string path = kTestPath + "/foo"; create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); - UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); // Expect to fs-verity setup to succeed sp<IFsveritySetupAuthToken> authToken; @@ -573,7 +579,7 @@ TEST_F(FsverityTest, enableFsverity) { TEST_F(FsverityTest, enableFsverity_nullAuthToken) { const std::string path = kTestPath + "/foo"; create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); - UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); // Verity null auth token fails sp<IFsveritySetupAuthToken> authToken; @@ -586,7 +592,7 @@ TEST_F(FsverityTest, enableFsverity_nullAuthToken) { TEST_F(FsverityTest, enableFsverity_differentFile) { const std::string path = kTestPath + "/foo"; create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); - UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); // Expect to fs-verity setup to succeed sp<IFsveritySetupAuthToken> authToken; @@ -597,17 +603,36 @@ TEST_F(FsverityTest, enableFsverity_differentFile) { // Verity auth token does not work for a different file const std::string anotherPath = kTestPath + "/bar"; ASSERT_TRUE(android::base::WriteStringToFile("content", anotherPath)); - UniqueFile raii2(/*fd=*/-1, anotherPath, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii2(/*fd=*/-1, anotherPath, &unlink_path); int32_t errno_local; status = service->enableFsverity(authToken, anotherPath, "fake.package.name", &errno_local); EXPECT_TRUE(status.isOk()); EXPECT_NE(errno_local, 0); } +TEST_F(FsverityTest, enableFsverity_errnoBeforeAuthenticated) { + const std::string path = kTestPath + "/foo"; + create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); + + // Expect to fs-verity setup to succeed + sp<IFsveritySetupAuthToken> authToken; + binder::Status status = createFsveritySetupAuthToken(path, O_RDWR, &authToken); + EXPECT_TRUE(status.isOk()); + EXPECT_TRUE(authToken != nullptr); + + // Verity errno before the fd authentication is constant (EPERM) + int32_t errno_local; + status = service->enableFsverity(authToken, path + "-non-exist", "fake.package.name", + &errno_local); + EXPECT_TRUE(status.isOk()); + EXPECT_EQ(errno_local, EPERM); +} + TEST_F(FsverityTest, createFsveritySetupAuthToken_ReadonlyFdDoesNotAuthenticate) { const std::string path = kTestPath + "/foo"; create_with_content(path, kTestAppUid, kTestAppUid, 0600, "content"); - UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); // Expect the fs-verity setup to fail sp<IFsveritySetupAuthToken> authToken; @@ -619,7 +644,7 @@ TEST_F(FsverityTest, createFsveritySetupAuthToken_UnownedFile) { const std::string path = kTestPath + "/foo"; // Simulate world-writable file owned by another app create_with_content(path, kTestAppUid + 1, kTestAppUid + 1, 0666, "content"); - UniqueFile raii(/*fd=*/-1, path, [](const std::string& path) { unlink(path.c_str()); }); + UniqueFile raii(/*fd=*/-1, path, &unlink_path); // Expect the fs-verity setup to fail sp<IFsveritySetupAuthToken> authToken; diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index 6115da75b2..5cdcb23d13 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -232,6 +232,11 @@ Status Lshal::main(const Arg &arg) { return static_cast<HelpCommand*>(help)->usageOfCommand(mCommand); } + // After Lshal::main() finishes, caller may call _exit(), causing debug + // information to prematurely ends. Hence flush(). + err().flush(); + out().flush(); + return status; } diff --git a/cmds/lshal/NullableOStream.h b/cmds/lshal/NullableOStream.h index 7cffcf8193..1576486c1d 100644 --- a/cmds/lshal/NullableOStream.h +++ b/cmds/lshal/NullableOStream.h @@ -59,6 +59,11 @@ public: operator bool() const { // NOLINT(google-explicit-constructor) return mOs != nullptr; } + void flush() { + if (mOs) { + mOs->flush(); + } + } private: template<typename> friend class NullableOStream; diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index e00c2a2b5a..3897197bd5 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -95,6 +95,16 @@ cc_test { static_libs: ["libgmock"], } +cc_test_host { + name: "servicemanager_unittest", + test_suites: ["general-tests"], + defaults: ["servicemanager_defaults"], + srcs: [ + "ServiceManagerUnittest.cpp", + ], + static_libs: ["libgmock"], +} + cc_fuzz { name: "servicemanager_fuzzer", defaults: [ diff --git a/cmds/servicemanager/NameUtil.h b/cmds/servicemanager/NameUtil.h new file mode 100644 index 0000000000..b08093960d --- /dev/null +++ b/cmds/servicemanager/NameUtil.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> +#include <string_view> + +#include <android-base/strings.h> + +namespace android { + +#ifndef VENDORSERVICEMANAGER + +struct NativeName { + std::string package; + std::string instance; + + // Parse {package}/{instance} + static bool fill(std::string_view name, NativeName* nname) { + size_t slash = name.find('/'); + if (slash == std::string_view::npos) { + return false; + } + // no extra slashes + if (name.find('/', slash + 1) != std::string_view::npos) { + return false; + } + // every part should be non-empty + if (slash == 0 || slash + 1 == name.size()) { + return false; + } + // no dots in package + if (name.rfind('.', slash) != std::string_view::npos) { + return false; + } + nname->package = name.substr(0, slash); + nname->instance = name.substr(slash + 1); + return true; + } +}; + +#endif + +} // namespace android diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index a4018385c7..a828b52cde 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -35,6 +35,8 @@ #include <vintf/constants.h> #endif // !VENDORSERVICEMANAGER +#include "NameUtil.h" + using ::android::binder::Status; using ::android::internal::Stability; @@ -84,6 +86,10 @@ static bool forEachManifest(const std::function<bool(const ManifestWithDescripti return false; } +static std::string getNativeInstanceName(const vintf::ManifestInstance& instance) { + return instance.package() + "/" + instance.instance(); +} + struct AidlName { std::string package; std::string iface; @@ -105,7 +111,26 @@ struct AidlName { } }; +static std::string getAidlInstanceName(const vintf::ManifestInstance& instance) { + return instance.package() + "." + instance.interface() + "/" + instance.instance(); +} + static bool isVintfDeclared(const std::string& name) { + NativeName nname; + if (NativeName::fill(name, &nname)) { + bool found = forEachManifest([&](const ManifestWithDescription& mwd) { + if (mwd.manifest->hasNativeInstance(nname.package, nname.instance)) { + ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description); + return true; // break + } + return false; // continue + }); + if (!found) { + ALOGI("Could not find %s in the VINTF manifest.", name.c_str()); + } + return found; + } + AidlName aname; if (!AidlName::fill(name, &aname)) return false; @@ -144,13 +169,31 @@ static bool isVintfDeclared(const std::string& name) { } static std::optional<std::string> getVintfUpdatableApex(const std::string& name) { + NativeName nname; + if (NativeName::fill(name, &nname)) { + std::optional<std::string> updatableViaApex; + + forEachManifest([&](const ManifestWithDescription& mwd) { + bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) { + if (manifestInstance.format() != vintf::HalFormat::NATIVE) return true; + if (manifestInstance.package() != nname.package) return true; + if (manifestInstance.instance() != nname.instance) return true; + updatableViaApex = manifestInstance.updatableViaApex(); + return false; // break (libvintf uses opposite convention) + }); + return !cont; + }); + + return updatableViaApex; + } + AidlName aname; if (!AidlName::fill(name, &aname)) return std::nullopt; std::optional<std::string> updatableViaApex; forEachManifest([&](const ManifestWithDescription& mwd) { - mwd.manifest->forEachInstance([&](const auto& manifestInstance) { + bool cont = mwd.manifest->forEachInstance([&](const auto& manifestInstance) { if (manifestInstance.format() != vintf::HalFormat::AIDL) return true; if (manifestInstance.package() != aname.package) return true; if (manifestInstance.interface() != aname.iface) return true; @@ -158,31 +201,31 @@ static std::optional<std::string> getVintfUpdatableApex(const std::string& name) updatableViaApex = manifestInstance.updatableViaApex(); return false; // break (libvintf uses opposite convention) }); - if (updatableViaApex.has_value()) return true; // break (found match) - return false; // continue + return !cont; }); return updatableViaApex; } -static std::vector<std::string> getVintfUpdatableInstances(const std::string& apexName) { - std::vector<std::string> instances; +static std::vector<std::string> getVintfUpdatableNames(const std::string& apexName) { + std::vector<std::string> names; forEachManifest([&](const ManifestWithDescription& mwd) { mwd.manifest->forEachInstance([&](const auto& manifestInstance) { - if (manifestInstance.format() == vintf::HalFormat::AIDL && - manifestInstance.updatableViaApex().has_value() && + if (manifestInstance.updatableViaApex().has_value() && manifestInstance.updatableViaApex().value() == apexName) { - std::string aname = manifestInstance.package() + "." + - manifestInstance.interface() + "/" + manifestInstance.instance(); - instances.push_back(aname); + if (manifestInstance.format() == vintf::HalFormat::NATIVE) { + names.push_back(getNativeInstanceName(manifestInstance)); + } else if (manifestInstance.format() == vintf::HalFormat::AIDL) { + names.push_back(getAidlInstanceName(manifestInstance)); + } } return true; // continue (libvintf uses opposite convention) }); return false; // continue }); - return instances; + return names; } static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& name) { @@ -217,6 +260,18 @@ static std::optional<ConnectionInfo> getVintfConnectionInfo(const std::string& n static std::vector<std::string> getVintfInstances(const std::string& interface) { size_t lastDot = interface.rfind('.'); if (lastDot == std::string::npos) { + // This might be a package for native instance. + std::vector<std::string> ret; + (void)forEachManifest([&](const ManifestWithDescription& mwd) { + auto instances = mwd.manifest->getNativeInstances(interface); + ret.insert(ret.end(), instances.begin(), instances.end()); + return false; // continue + }); + // If found, return it without error log. + if (!ret.empty()) { + return ret; + } + ALOGE("VINTF interfaces require names in Java package format (e.g. some.package.foo.IFoo) " "but got: %s", interface.c_str()); @@ -596,20 +651,20 @@ Status ServiceManager::getUpdatableNames([[maybe_unused]] const std::string& ape std::vector<std::string>* outReturn) { auto ctx = mAccess->getCallingContext(); - std::vector<std::string> apexUpdatableInstances; + std::vector<std::string> apexUpdatableNames; #ifndef VENDORSERVICEMANAGER - apexUpdatableInstances = getVintfUpdatableInstances(apexName); + apexUpdatableNames = getVintfUpdatableNames(apexName); #endif outReturn->clear(); - for (const std::string& instance : apexUpdatableInstances) { - if (mAccess->canFind(ctx, instance)) { - outReturn->push_back(instance); + for (const std::string& name : apexUpdatableNames) { + if (mAccess->canFind(ctx, name)) { + outReturn->push_back(name); } } - if (outReturn->size() == 0 && apexUpdatableInstances.size() != 0) { + if (outReturn->size() == 0 && apexUpdatableNames.size() != 0) { return Status::fromExceptionCode(Status::EX_SECURITY, "SELinux denied."); } diff --git a/cmds/servicemanager/ServiceManagerUnittest.cpp b/cmds/servicemanager/ServiceManagerUnittest.cpp new file mode 100644 index 0000000000..39d20b0025 --- /dev/null +++ b/cmds/servicemanager/ServiceManagerUnittest.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "NameUtil.h" + +namespace android { + +TEST(ServiceManager, NativeName) { + NativeName nname; + EXPECT_TRUE(NativeName::fill("mapper/default", &nname)); + EXPECT_EQ("mapper", nname.package); + EXPECT_EQ("default", nname.instance); +} + +TEST(ServiceManager, NativeName_Malformed) { + NativeName nname; + EXPECT_FALSE(NativeName::fill("mapper", &nname)); + EXPECT_FALSE(NativeName::fill("mapper/", &nname)); + EXPECT_FALSE(NativeName::fill("/default", &nname)); + EXPECT_FALSE(NativeName::fill("mapper/default/0", &nname)); + EXPECT_FALSE(NativeName::fill("aidl.like.IType/default", &nname)); +} + +} // namespace android diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index 97e500d0a7..b57505302c 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -361,6 +361,24 @@ TEST(Vintf, GetUpdatableNames_InvalidApexNameReturnsEmpty) { EXPECT_EQ(std::vector<std::string>{}, names); } +TEST(Vintf, IsDeclared_native) { + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; + + auto sm = getPermissiveServiceManager(); + bool declared = false; + EXPECT_TRUE(sm->isDeclared("mapper/minigbm", &declared).isOk()); + EXPECT_TRUE(declared); +} + +TEST(Vintf, GetDeclaredInstances_native) { + if (!isCuttlefishPhone()) GTEST_SKIP() << "Skipping non-Cuttlefish-phone devices"; + + auto sm = getPermissiveServiceManager(); + std::vector<std::string> instances; + EXPECT_TRUE(sm->getDeclaredInstances("mapper", &instances).isOk()); + EXPECT_EQ(std::vector<std::string>{"minigbm"}, instances); +} + class CallbackHistorian : public BnServiceCallback { Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { registrations.push_back(name); diff --git a/data/etc/android.hardware.location.gps.xml b/data/etc/android.hardware.location.gps.xml index 72ab73228e..2a55370832 100644 --- a/data/etc/android.hardware.location.gps.xml +++ b/data/etc/android.hardware.location.gps.xml @@ -17,6 +17,5 @@ <!-- These are the location-related features for devices that include GPS. --> <permissions> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.location.gps" /> </permissions> diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 95b8110b6e..beb69f81dd 100644 --- a/data/etc/car_core_hardware.xml +++ b/data/etc/car_core_hardware.xml @@ -28,7 +28,6 @@ <feature name="android.hardware.audio.output" /> <feature name="android.hardware.location" /> - <feature name="android.hardware.location.network" /> <feature name="android.hardware.bluetooth" /> <feature name="android.hardware.touchscreen" /> <feature name="android.hardware.microphone" /> diff --git a/data/etc/wearable_core_hardware.xml b/data/etc/wearable_core_hardware.xml index 855b11005f..4c9932d4fb 100644 --- a/data/etc/wearable_core_hardware.xml +++ b/data/etc/wearable_core_hardware.xml @@ -36,6 +36,7 @@ <feature name="android.hardware.security.model.compatible" /> <!-- basic system services --> + <feature name="android.software.credentials" /> <feature name="android.software.home_screen" /> <feature name="android.software.secure_lock_screen" /> diff --git a/include/android/input.h b/include/android/input.h index 16d86af44c..b5c1e5c354 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -1490,6 +1490,14 @@ int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) */ const AInputEvent* AMotionEvent_fromJava(JNIEnv* env, jobject motionEvent) __INTRODUCED_IN(31); +/** + * Creates a java android.view.InputEvent object that is a copy of the specified native + * {@link AInputEvent}. Returns null on error + * + * Available since API level 35. + */ +jobject AInputEvent_toJava(JNIEnv* env, const AInputEvent* aInputEvent) __INTRODUCED_IN(35); + struct AInputQueue; /** * Input queue diff --git a/include/android/keycodes.h b/include/android/keycodes.h index f8fb256fae..79cdbcaf7b 100644 --- a/include/android/keycodes.h +++ b/include/android/keycodes.h @@ -839,6 +839,10 @@ enum { AKEYCODE_MACRO_3 = 315, /** User customizable key #4. */ AKEYCODE_MACRO_4 = 316, + /** Open Emoji picker */ + AKEYCODE_EMOJI_PICKER = 317, + /** Take Screenshot */ + AKEYCODE_SCREENSHOT = 318, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h index 08d339b176..3c82d888b5 100644 --- a/include/android/performance_hint.h +++ b/include/android/performance_hint.h @@ -233,11 +233,11 @@ int APerformanceHint_setPreferPowerEfficiency( * @param workDuration The {@link AWorkDuration} structure of times the thread group took to * complete its last task in nanoseconds breaking down into different components. * - * The work period start timestamp, actual total duration and actual CPU duration must be - * positive. + * The work period start timestamp and actual total duration must be greater than zero. * - * The actual GPU duration must be non-negative. If the actual GPU duration is 0, it means - * the actual GPU duration is not measured. + * The actual CPU and GPU durations must be greater than or equal to zero, and at least one + * of them must be greater than zero. When one of them is equal to zero, it means that type + * of work was not measured for this workload. * * @return 0 on success. * EINVAL if any duration is an invalid number. @@ -289,7 +289,8 @@ void AWorkDuration_setActualTotalDurationNanos(AWorkDuration* _Nonnull aWorkDura * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()} * @param actualCpuDurationNanos The actual CPU work duration in nanoseconds. This number must be - * greater than zero. + * greater than or equal to zero. If it is equal to zero, that means the CPU was not + * measured. */ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, int64_t actualCpuDurationNanos) __INTRODUCED_IN(__ANDROID_API_V__); @@ -299,7 +300,7 @@ void AWorkDuration_setActualCpuDurationNanos(AWorkDuration* _Nonnull aWorkDurati * * @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}. * @param actualGpuDurationNanos The actual GPU work duration in nanoseconds, the number must be - * non-negative. If the actual GPU duration is 0, it means the actual GPU duration is + * greater than or equal to zero. If it is equal to zero, that means the GPU was not * measured. */ void AWorkDuration_setActualGpuDurationNanos(AWorkDuration* _Nonnull aWorkDuration, diff --git a/include/ftl/expected.h b/include/ftl/expected.h new file mode 100644 index 0000000000..12b6102b6f --- /dev/null +++ b/include/ftl/expected.h @@ -0,0 +1,65 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <android-base/expected.h> +#include <ftl/optional.h> + +#include <utility> + +namespace android::ftl { + +// Superset of base::expected<T, E> with monadic operations. +// +// TODO: Extend std::expected<T, E> in C++23. +// +template <typename T, typename E> +struct Expected final : base::expected<T, E> { + using Base = base::expected<T, E>; + using Base::expected; + + using Base::error; + using Base::has_value; + using Base::value; + + template <typename P> + constexpr bool has_error(P predicate) const { + return !has_value() && predicate(error()); + } + + constexpr Optional<T> value_opt() const& { + return has_value() ? Optional(value()) : std::nullopt; + } + + constexpr Optional<T> value_opt() && { + return has_value() ? Optional(std::move(value())) : std::nullopt; + } + + // Delete new for this class. Its base doesn't have a virtual destructor, and + // if it got deleted via base class pointer, it would cause undefined + // behavior. There's not a good reason to allocate this object on the heap + // anyway. + static void* operator new(size_t) = delete; + static void* operator new[](size_t) = delete; +}; + +template <typename E> +constexpr auto Unexpected(E&& error) { + return base::unexpected(std::forward<E>(error)); +} + +} // namespace android::ftl diff --git a/include/ftl/fake_guard.h b/include/ftl/fake_guard.h index bacd1b29ef..e6012516fc 100644 --- a/include/ftl/fake_guard.h +++ b/include/ftl/fake_guard.h @@ -85,6 +85,5 @@ struct [[clang::scoped_lockable]] FakeGuard final { #define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard -// The void argument suppresses a warning about zero variadic macro arguments. #define FTL_FAKE_GUARD(...) \ - FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, void)(__VA_ARGS__) + FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, )(__VA_ARGS__) diff --git a/include/ftl/small_map.h b/include/ftl/small_map.h index 49cde7fedc..83d5967464 100644 --- a/include/ftl/small_map.h +++ b/include/ftl/small_map.h @@ -107,12 +107,20 @@ class SmallMap final { template <typename Q, typename W, std::size_t M, typename E> SmallMap(SmallMap<Q, W, M, E> other) : map_(std::move(other.map_)) {} + static constexpr size_type static_capacity() { return N; } + size_type max_size() const { return map_.max_size(); } size_type size() const { return map_.size(); } bool empty() const { return map_.empty(); } // Returns whether the map is backed by static or dynamic storage. - bool dynamic() const { return map_.dynamic(); } + bool dynamic() const { + if constexpr (static_capacity() > 0) { + return map_.dynamic(); + } else { + return true; + } + } iterator begin() { return map_.begin(); } const_iterator begin() const { return cbegin(); } @@ -171,9 +179,15 @@ class SmallMap final { return {it, false}; } - auto& ref = map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward<Args>(args)...)); - return {&ref, true}; + decltype(auto) ref_or_it = + map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), + std::forward_as_tuple(std::forward<Args>(args)...)); + + if constexpr (static_capacity() > 0) { + return {&ref_or_it, true}; + } else { + return {ref_or_it, true}; + } } // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator diff --git a/include/ftl/small_vector.h b/include/ftl/small_vector.h index 11294c3ac8..43e9fac5e2 100644 --- a/include/ftl/small_vector.h +++ b/include/ftl/small_vector.h @@ -124,30 +124,29 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma DISPATCH(size_type, size, const) DISPATCH(bool, empty, const) - // noexcept to suppress warning about zero variadic macro arguments. - DISPATCH(iterator, begin, noexcept) + DISPATCH(iterator, begin, ) DISPATCH(const_iterator, begin, const) DISPATCH(const_iterator, cbegin, const) - DISPATCH(iterator, end, noexcept) + DISPATCH(iterator, end, ) DISPATCH(const_iterator, end, const) DISPATCH(const_iterator, cend, const) - DISPATCH(reverse_iterator, rbegin, noexcept) + DISPATCH(reverse_iterator, rbegin, ) DISPATCH(const_reverse_iterator, rbegin, const) DISPATCH(const_reverse_iterator, crbegin, const) - DISPATCH(reverse_iterator, rend, noexcept) + DISPATCH(reverse_iterator, rend, ) DISPATCH(const_reverse_iterator, rend, const) DISPATCH(const_reverse_iterator, crend, const) - DISPATCH(iterator, last, noexcept) + DISPATCH(iterator, last, ) DISPATCH(const_iterator, last, const) - DISPATCH(reference, front, noexcept) + DISPATCH(reference, front, ) DISPATCH(const_reference, front, const) - DISPATCH(reference, back, noexcept) + DISPATCH(reference, back, ) DISPATCH(const_reference, back, const) reference operator[](size_type i) { @@ -211,13 +210,13 @@ class SmallVector final : details::ArrayTraits<T>, details::ArrayComparators<Sma // // The last() and end() iterators are invalidated. // - DISPATCH(void, pop_back, noexcept) + DISPATCH(void, pop_back, ) // Removes all elements. // // All iterators are invalidated. // - DISPATCH(void, clear, noexcept) + DISPATCH(void, clear, ) #undef DISPATCH diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index b7751f704a..57b659d9ee 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -75,6 +75,17 @@ struct InputDeviceIdentifier { bool operator!=(const InputDeviceIdentifier&) const = default; }; +/** + * Holds View related behaviors for an InputDevice. + */ +struct InputDeviceViewBehavior { + /** + * The smooth scroll behavior that applies for all source/axis, if defined by the device. + * Empty optional if the device has not specified the default smooth scroll behavior. + */ + std::optional<bool> shouldSmoothScroll; +}; + /* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ enum class InputDeviceSensorType : int32_t { ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, @@ -266,7 +277,8 @@ public: void initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId); + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior = {{}}); inline int32_t getId() const { return mId; } inline int32_t getControllerNumber() const { return mControllerNumber; } @@ -298,6 +310,8 @@ public: return mKeyboardLayoutInfo; } + inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; } + inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) { mKeyCharacterMap = value; } @@ -359,6 +373,8 @@ private: std::unordered_map<int32_t, InputDeviceLightInfo> mLights; /* Map from battery ID to battery info */ std::unordered_map<int32_t, InputDeviceBatteryInfo> mBatteries; + /** The View related behaviors for the device. */ + InputDeviceViewBehavior mViewBehavior; }; /* Types of input device configuration files. */ diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 2d64872beb..42dcd3c394 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -35,18 +35,16 @@ #include <android-base/result.h> #include <android-base/unique_fd.h> +#include <android/os/InputChannelCore.h> #include <binder/IBinder.h> -#include <binder/Parcelable.h> #include <input/Input.h> #include <input/InputVerifier.h> #include <sys/stat.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <utils/Errors.h> -#include <utils/RefBase.h> #include <utils/Timers.h> - namespace android { class Parcel; @@ -231,18 +229,15 @@ struct InputMessage { * input messages across processes. Each channel has a descriptive name for debugging purposes. * * Each endpoint has its own InputChannel object that specifies its file descriptor. + * For parceling, this relies on android::os::InputChannelCore, defined in aidl. * * The input channel is closed when all references to it are released. */ -class InputChannel : public Parcelable { +class InputChannel : private android::os::InputChannelCore { public: - static std::unique_ptr<InputChannel> create(const std::string& name, - android::base::unique_fd fd, sp<IBinder> token); - InputChannel() = default; - InputChannel(const InputChannel& other) - : mName(other.mName), mFd(other.dupFd()), mToken(other.mToken){}; - InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); - ~InputChannel() override; + static std::unique_ptr<InputChannel> create(android::os::InputChannelCore&& parceledChannel); + ~InputChannel(); + /** * Create a pair of input channels. * The two returned input channels are equivalent, and are labeled as "server" and "client" @@ -254,9 +249,8 @@ public: std::unique_ptr<InputChannel>& outServerChannel, std::unique_ptr<InputChannel>& outClientChannel); - inline std::string getName() const { return mName; } - inline const android::base::unique_fd& getFd() const { return mFd; } - inline sp<IBinder> getToken() const { return mToken; } + inline std::string getName() const { return name; } + inline int getFd() const { return fd.get(); } /* Send a message to the other endpoint. * @@ -304,10 +298,16 @@ public: /* Return a new object that has a duplicate of this channel's fd. */ std::unique_ptr<InputChannel> dup() const; - void copyTo(InputChannel& outChannel) const; + void copyTo(android::os::InputChannelCore& outChannel) const; - status_t readFromParcel(const android::Parcel* parcel) override; - status_t writeToParcel(android::Parcel* parcel) const override; + /** + * Similar to "copyTo", but it takes ownership of the provided InputChannel (and after this is + * called, it destroys it). + * @param from the InputChannel that should be converted to InputChannelCore + * @param outChannel the pre-allocated InputChannelCore to which to transfer the 'from' channel + */ + static void moveChannel(std::unique_ptr<InputChannel> from, + android::os::InputChannelCore& outChannel); /** * The connection token is used to identify the input connection, i.e. @@ -323,26 +323,11 @@ public: */ sp<IBinder> getConnectionToken() const; - bool operator==(const InputChannel& inputChannel) const { - struct stat lhs, rhs; - if (fstat(mFd.get(), &lhs) != 0) { - return false; - } - if (fstat(inputChannel.getFd().get(), &rhs) != 0) { - return false; - } - // If file descriptors are pointing to same inode they are duplicated fds. - return inputChannel.getName() == getName() && inputChannel.getConnectionToken() == mToken && - lhs.st_ino == rhs.st_ino; - } - private: - base::unique_fd dupFd() const; - - std::string mName; - base::unique_fd mFd; + static std::unique_ptr<InputChannel> create(const std::string& name, + android::base::unique_fd fd, sp<IBinder> token); - sp<IBinder> mToken; + InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token); }; /* @@ -357,7 +342,7 @@ public: ~InputPublisher(); /* Gets the underlying input channel. */ - inline std::shared_ptr<InputChannel> getChannel() { return mChannel; } + inline InputChannel& getChannel() const { return *mChannel; } /* Publishes a key event to the input channel. * diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index ae0fb018ef..941e0918f7 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -320,6 +320,24 @@ cc_defaults { "ServiceManagerHost.cpp", ], }, + android: { + shared_libs: [ + "libapexsupport", + "libvndksupport", + ], + }, + recovery: { + exclude_shared_libs: [ + "libapexsupport", + "libvndksupport", + ], + }, + native_bridge: { + exclude_shared_libs: [ + "libapexsupport", + "libvndksupport", + ], + }, }, cflags: [ "-DBINDER_WITH_KERNEL_IPC", diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index fe566fccb2..39573ec54d 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -40,6 +40,11 @@ #include "ServiceManagerHost.h" #endif +#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) && !defined(__ANDROID_NATIVE_BRIDGE__) +#include <android/apexsupport.h> +#include <vndksupport/linker.h> +#endif + #include "Static.h" namespace android { @@ -259,6 +264,27 @@ bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logP } } +void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag) { +#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__) && !defined(__ANDROID_NATIVE_BRIDGE__) + sp<IServiceManager> sm = defaultServiceManager(); + String16 name = interface + String16("/") + instance; + if (!sm->isDeclared(name)) { + return nullptr; + } + String16 libraryName = interface + String16(".") + instance + String16(".so"); + if (auto updatableViaApex = sm->updatableViaApex(name); updatableViaApex.has_value()) { + return AApexSupport_loadLibrary(String8(libraryName).c_str(), + String8(*updatableViaApex).c_str(), flag); + } + return android_load_sphal_library(String8(libraryName).c_str(), flag); +#else + (void)interface; + (void)instance; + (void)flag; + return nullptr; +#endif +} + #endif //__ANDROID_VNDK__ // ---------------------------------------------------------------------- diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 7de94e3c6c..fb2781be59 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -492,6 +492,7 @@ bool ProcessState::isDriverFeatureEnabled(const DriverFeature feature) { if (read(fd, &on, sizeof(on)) == -1) { ALOGE("%s: error reading to %s: %s", __func__, names[static_cast<int>(feature)], strerror(errno)); + close(fd); return false; } close(fd); diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 525ba2e9b9..de2a69f7cf 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -114,8 +114,8 @@ static_assert(PADDING8(8) == 0); RecordedTransaction::RecordedTransaction(RecordedTransaction&& t) noexcept { mData = t.mData; - mSent.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); - mReply.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); + mSentDataOnly.setData(t.getDataParcel().data(), t.getDataParcel().dataSize()); + mReplyDataOnly.setData(t.getReplyParcel().data(), t.getReplyParcel().dataSize()); } std::optional<RecordedTransaction> RecordedTransaction::fromDetails( @@ -136,12 +136,21 @@ std::optional<RecordedTransaction> RecordedTransaction::fromDetails( return std::nullopt; } - if (t.mSent.setData(dataParcel.data(), dataParcel.dataBufferSize()) != android::NO_ERROR) { + if (const auto* kernelFields = dataParcel.maybeKernelFields()) { + for (size_t i = 0; i < kernelFields->mObjectsSize; i++) { + uint64_t offset = kernelFields->mObjects[i]; + t.mData.mSentObjectData.push_back(offset); + } + } + + if (t.mSentDataOnly.setData(dataParcel.data(), dataParcel.dataBufferSize()) != + android::NO_ERROR) { ALOGE("Failed to set sent parcel data."); return std::nullopt; } - if (t.mReply.setData(replyParcel.data(), replyParcel.dataBufferSize()) != android::NO_ERROR) { + if (t.mReplyDataOnly.setData(replyParcel.data(), replyParcel.dataBufferSize()) != + android::NO_ERROR) { ALOGE("Failed to set reply parcel data."); return std::nullopt; } @@ -154,6 +163,7 @@ enum { DATA_PARCEL_CHUNK = 2, REPLY_PARCEL_CHUNK = 3, INTERFACE_NAME_CHUNK = 4, + DATA_PARCEL_OBJECT_CHUNK = 5, END_CHUNK = 0x00ffffff, }; @@ -265,21 +275,30 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd break; } case DATA_PARCEL_CHUNK: { - if (t.mSent.setData(reinterpret_cast<const unsigned char*>(payloadMap), - chunk.dataSize) != android::NO_ERROR) { + if (t.mSentDataOnly.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { ALOGE("Failed to set sent parcel data."); return std::nullopt; } break; } case REPLY_PARCEL_CHUNK: { - if (t.mReply.setData(reinterpret_cast<const unsigned char*>(payloadMap), - chunk.dataSize) != android::NO_ERROR) { + if (t.mReplyDataOnly.setData(reinterpret_cast<const unsigned char*>(payloadMap), + chunk.dataSize) != android::NO_ERROR) { ALOGE("Failed to set reply parcel data."); return std::nullopt; } break; } + case DATA_PARCEL_OBJECT_CHUNK: { + const uint64_t* objects = reinterpret_cast<const uint64_t*>(payloadMap); + size_t metaDataSize = (chunk.dataSize / sizeof(uint64_t)); + ALOGI("Total objects found in saved parcel %zu", metaDataSize); + for (size_t index = 0; index < metaDataSize; ++index) { + t.mData.mSentObjectData.push_back(objects[index]); + } + break; + } case END_CHUNK: break; default: @@ -343,14 +362,26 @@ android::status_t RecordedTransaction::dumpToFile(const unique_fd& fd) const { return UNKNOWN_ERROR; } - if (NO_ERROR != writeChunk(fd, DATA_PARCEL_CHUNK, mSent.dataBufferSize(), mSent.data())) { + if (NO_ERROR != + writeChunk(fd, DATA_PARCEL_CHUNK, mSentDataOnly.dataBufferSize(), mSentDataOnly.data())) { ALOGE("Failed to write sent Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } - if (NO_ERROR != writeChunk(fd, REPLY_PARCEL_CHUNK, mReply.dataBufferSize(), mReply.data())) { + + if (NO_ERROR != + writeChunk(fd, REPLY_PARCEL_CHUNK, mReplyDataOnly.dataBufferSize(), + mReplyDataOnly.data())) { ALOGE("Failed to write reply Parcel to fd %d", fd.get()); return UNKNOWN_ERROR; } + + if (NO_ERROR != + writeChunk(fd, DATA_PARCEL_OBJECT_CHUNK, mData.mSentObjectData.size() * sizeof(uint64_t), + reinterpret_cast<const uint8_t*>(mData.mSentObjectData.data()))) { + ALOGE("Failed to write sent parcel object metadata to fd %d", fd.get()); + return UNKNOWN_ERROR; + } + if (NO_ERROR != writeChunk(fd, END_CHUNK, 0, NULL)) { ALOGE("Failed to write end chunk to fd %d", fd.get()); return UNKNOWN_ERROR; @@ -384,10 +415,14 @@ uint32_t RecordedTransaction::getVersion() const { return mData.mHeader.version; } +const std::vector<uint64_t>& RecordedTransaction::getObjectOffsets() const { + return mData.mSentObjectData; +} + const Parcel& RecordedTransaction::getDataParcel() const { - return mSent; + return mSentDataOnly; } const Parcel& RecordedTransaction::getReplyParcel() const { - return mReply; + return mReplyDataOnly; } diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 55167a7db0..486bdfba78 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -207,6 +207,8 @@ status_t getService(const String16& name, sp<INTERFACE>* outService) return NAME_NOT_FOUND; } +void* openDeclaredPassthroughHal(const String16& interface, const String16& instance, int flag); + bool checkCallingPermission(const String16& permission); bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t* outUid); diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 09da6e3c4a..d7096d8a75 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -55,6 +55,9 @@ class String8; class TextOutput; namespace binder { class Status; +namespace debug { +class RecordedTransaction; +} } class Parcel { @@ -1443,6 +1446,9 @@ private: // TODO(b/202029388): Remove 'getBlobAshmemSize' once no prebuilts reference // this size_t getBlobAshmemSize() const; + + // Needed so that we can save object metadata to the disk + friend class android::binder::debug::RecordedTransaction; }; // --------------------------------------------------------------------------- diff --git a/libs/binder/include/binder/RecordedTransaction.h b/libs/binder/include/binder/RecordedTransaction.h index 505c1992b9..f0bee7f39e 100644 --- a/libs/binder/include/binder/RecordedTransaction.h +++ b/libs/binder/include/binder/RecordedTransaction.h @@ -50,6 +50,7 @@ public: uint32_t getVersion() const; const Parcel& getDataParcel() const; const Parcel& getReplyParcel() const; + const std::vector<uint64_t>& getObjectOffsets() const; private: RecordedTransaction() = default; @@ -75,10 +76,11 @@ private: struct MovableData { // movable TransactionHeader mHeader; std::string mInterfaceName; + std::vector<uint64_t> mSentObjectData; /* Object Offsets */ }; MovableData mData; - Parcel mSent; - Parcel mReply; + Parcel mSentDataOnly; + Parcel mReplyDataOnly; }; } // namespace binder::debug diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 316a79cfee..b34b30d638 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -243,6 +243,18 @@ void AServiceManager_getUpdatableApexName(const char* instance, void* context, __INTRODUCED_IN(__ANDROID_API_U__); /** + * Opens a declared passthrough HAL. + * + * \param instance identifier of the passthrough service (e.g. "mapper") + * \param instance identifier of the implemenatation (e.g. "default") + * \param flag passed to dlopen() + */ +void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance, + int flag) + // TODO(b/302113279) use __INTRODUCED_LLNDK for vendor variants + __INTRODUCED_IN(__ANDROID_API_V__); + +/** * Prevent lazy services without client from shutting down their process * * This should only be used if it is every eventually set to false. If a diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 0843a8e648..de624e44f6 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -204,6 +204,7 @@ LIBBINDER_NDK35 { # introduced=VanillaIceCream APersistableBundle_getDoubleVectorKeys; APersistableBundle_getStringVectorKeys; APersistableBundle_getPersistableBundleKeys; + AServiceManager_openDeclaredPassthroughHal; # systemapi llndk }; LIBBINDER_NDK_PLATFORM { diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 3bfdc59ec2..5529455cc6 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -200,6 +200,13 @@ void AServiceManager_getUpdatableApexName(const char* instance, void* context, callback(String8(updatableViaApex.value()).c_str(), context); } } +void* AServiceManager_openDeclaredPassthroughHal(const char* interface, const char* instance, + int flag) { + LOG_ALWAYS_FATAL_IF(interface == nullptr, "interface == nullptr"); + LOG_ALWAYS_FATAL_IF(instance == nullptr, "instance == nullptr"); + + return openDeclaredPassthroughHal(String16(interface), String16(instance), flag); +} void AServiceManager_forceLazyServicesPersist(bool persist) { auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); serviceRegistrar.forcePersist(persist); diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index cab1a60370..966ec959b6 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -984,7 +984,7 @@ int main(int argc, char* argv[]) { return generatedFlaggedService(test_flags, kBinderNdkUnitTestServiceFlagged); } - ABinderProcess_setThreadPoolMaxThreadCount(1); // to receive death notifications/callbacks + ABinderProcess_setThreadPoolMaxThreadCount(0); ABinderProcess_startThreadPool(); return RUN_ALL_TESTS(); diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index dd2be94a76..2f0987fd1a 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -69,10 +69,14 @@ cc_test { cc_test { name: "binderRecordReplayTest", srcs: ["binderRecordReplayTest.cpp"], + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], shared_libs: [ "libbinder", "libcutils", "libutils", + "liblog", ], static_libs: [ "binderRecordReplayTestIface-cpp", @@ -96,6 +100,14 @@ aidl_interface { enabled: true, platform_apis: true, }, + + // TODO: switch from FileDescriptor to ParcelFileDescriptor + ndk: { + enabled: false, + }, + rust: { + enabled: false, + }, }, } diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl index bd6b03c6e0..29267e91c6 100644 --- a/libs/binder/tests/IBinderRecordReplayTest.aidl +++ b/libs/binder/tests/IBinderRecordReplayTest.aidl @@ -69,4 +69,10 @@ interface IBinderRecordReplayTest { void setSingleDataParcelableArray(in SingleDataParcelable[] input); SingleDataParcelable[] getSingleDataParcelableArray(); + + void setBinder(in IBinder binder); + IBinder getBinder(); + + void setFileDescriptor(in FileDescriptor fd); + FileDescriptor getFileDescriptor(); } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index cb1a1ee443..0ee96e7317 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -70,7 +70,7 @@ MATCHER_P(StatusEq, expected, (negation ? "not " : "") + statusToString(expected } static ::testing::AssertionResult IsPageAligned(void *buf) { - if (((unsigned long)buf & ((unsigned long)PAGE_SIZE - 1)) == 0) + if (((unsigned long)buf & ((unsigned long)getpagesize() - 1)) == 0) return ::testing::AssertionSuccess(); else return ::testing::AssertionFailure() << buf << " is not page aligned"; diff --git a/libs/binder/tests/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp index 73c0a94ce2..b975fad2c9 100644 --- a/libs/binder/tests/binderRecordReplayTest.cpp +++ b/libs/binder/tests/binderRecordReplayTest.cpp @@ -24,7 +24,10 @@ #include <binder/RecordedTransaction.h> #include <binder/unique_fd.h> +#include <cutils/ashmem.h> + #include <fuzzbinder/libbinder_driver.h> +#include <fuzzbinder/random_binder.h> #include <fuzzer/FuzzedDataProvider.h> #include <fuzzseeds/random_parcel_seeds.h> @@ -37,6 +40,7 @@ using namespace android; using android::generateSeedsFromRecording; +using android::RandomBinder; using android::binder::borrowed_fd; using android::binder::Status; using android::binder::unique_fd; @@ -44,6 +48,7 @@ using android::binder::debug::RecordedTransaction; using parcelables::SingleDataParcelable; const String16 kServerName = String16("binderRecordReplay"); +extern std::string kRandomInterfaceName; #define GENERATE_GETTER_SETTER_PRIMITIVE(name, T) \ Status set##name(T input) { \ @@ -81,6 +86,7 @@ public: GENERATE_GETTER_SETTER(String, String16); GENERATE_GETTER_SETTER(SingleDataParcelable, SingleDataParcelable); + GENERATE_GETTER_SETTER(Binder, sp<IBinder>); GENERATE_GETTER_SETTER(BooleanArray, std::vector<bool>); GENERATE_GETTER_SETTER(ByteArray, std::vector<uint8_t>); @@ -91,12 +97,22 @@ public: GENERATE_GETTER_SETTER(DoubleArray, std::vector<double>); GENERATE_GETTER_SETTER(StringArray, std::vector<::android::String16>); GENERATE_GETTER_SETTER(SingleDataParcelableArray, std::vector<SingleDataParcelable>); + + Status setFileDescriptor(unique_fd input) { + mFd = std::move(unique_fd(dup(input))); + return Status::ok(); + } + + Status getFileDescriptor(unique_fd* output) { + *output = std::move(unique_fd(dup(mFd))); + return Status::ok(); + } + unique_fd mFd; }; std::vector<uint8_t> retrieveData(borrowed_fd fd) { struct stat fdStat; EXPECT_TRUE(fstat(fd.get(), &fdStat) != -1); - EXPECT_TRUE(fdStat.st_size != 0); std::vector<uint8_t> buffer(fdStat.st_size); auto readResult = android::base::ReadFully(fd, buffer.data(), fdStat.st_size); @@ -115,6 +131,7 @@ void replayFuzzService(const sp<BpBinder>& binder, const RecordedTransaction& tr // Read the data which has been written to seed corpus ASSERT_EQ(0, lseek(seedFd.get(), 0, SEEK_SET)); std::vector<uint8_t> seedData = retrieveData(seedFd); + EXPECT_TRUE(seedData.size() != 0); // use fuzzService to replay the corpus FuzzedDataProvider provider(seedData.data(), seedData.size()); @@ -148,7 +165,14 @@ public: template <typename T, typename U> void recordReplay(Status (IBinderRecordReplayTest::*set)(T), U recordedValue, Status (IBinderRecordReplayTest::*get)(U*), U changedValue) { - auto replayFunctions = {&replayBinder, &replayFuzzService}; + using ReplayFunc = decltype(&replayFuzzService); + vector<ReplayFunc> replayFunctions = {&replayFuzzService}; + if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) { + // Parcel retrieved from record replay doesn't have object information. use it for + // replaying primitive types only. + replayFunctions.push_back(&replayBinder); + } + for (auto replayFunc : replayFunctions) { unique_fd fd(open("/data/local/tmp/binderRecordReplayTest.rec", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); @@ -156,7 +180,7 @@ public: // record a transaction mBpBinder->startRecordingBinder(fd); - auto status = (*mInterface.*set)(recordedValue); + auto status = (*mInterface.*set)(std::move(recordedValue)); EXPECT_TRUE(status.isOk()); mBpBinder->stopRecordingBinder(); @@ -164,16 +188,22 @@ public: U output; status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); - EXPECT_EQ(output, recordedValue); + + // Expect this equal only if types are primitives + if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) { + EXPECT_EQ(output, recordedValue); + } // write over the existing state - status = (*mInterface.*set)(changedValue); + status = (*mInterface.*set)(std::move(changedValue)); EXPECT_TRUE(status.isOk()); status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); - EXPECT_EQ(output, changedValue); + if (!std::is_same_v<U, unique_fd> && !std::is_same_v<U, sp<IBinder>>) { + EXPECT_EQ(output, changedValue); + } // replay transaction ASSERT_EQ(0, lseek(fd.get(), 0, SEEK_SET)); @@ -186,7 +216,23 @@ public: status = (*mInterface.*get)(&output); EXPECT_TRUE(status.isOk()); - EXPECT_EQ(output, recordedValue); + + // FDs and binders will be replaced with random fd and random binders + if constexpr (std::is_same_v<U, unique_fd>) { + // verify that replayed fd is /dev/null. This is being replayed from random_fd.cpp + // and choosing /dav/null while generating seed in binder2corpus + std::string fdPath = "/proc/self/fd/" + std::to_string(output.get()); + char path[PATH_MAX]; + ASSERT_GT(readlink(fdPath.c_str(), path, sizeof(path)), 0); + EXPECT_EQ(strcmp("/dev/null", path), 0); + } else if constexpr (std::is_same_v<U, sp<IBinder>>) { + // This is binder is replayed from random_binder.cpp using seed data which writes + // this interface. + EXPECT_EQ(String16(kRandomInterfaceName.c_str(), kRandomInterfaceName.size()), + output->getInterfaceDescriptor()); + } else { + ASSERT_EQ(recordedValue, output); + } } } @@ -319,6 +365,32 @@ TEST_F(BinderRecordReplayTest, ReplaySingleDataParcelableArray) { &IBinderRecordReplayTest::getSingleDataParcelableArray, changed); } +TEST_F(BinderRecordReplayTest, ReplayBinder) { + vector<uint8_t> data = {0x8A, 0x19, 0x0D, 0x44, 0x37, 0x0D, 0x38, 0x5E, 0x9B, 0xAA, 0xF3, 0xDA}; + sp<IBinder> saved = new RandomBinder(String16("random_interface"), std::move(data)); + sp<IBinder> changed = IInterface::asBinder(defaultServiceManager()); + recordReplay(&IBinderRecordReplayTest::setBinder, saved, &IBinderRecordReplayTest::getBinder, + changed); +} + +TEST_F(BinderRecordReplayTest, ReplayFd) { + // Write something to both fds we are setting + unique_fd saved(open("/data/local/tmp/test_fd", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); + std::string contentSaved = "This will be never read again for recorded fd!"; + CHECK(android::base::WriteFully(saved, contentSaved.data(), contentSaved.size())) + << saved.get(); + + unique_fd changed(open("/data/local/tmp/test_des", O_RDWR | O_CREAT | O_CLOEXEC, 0666)); + std::string contentChanged = "This will be never read again from changed fd!"; + CHECK(android::base::WriteFully(changed, contentChanged.data(), contentChanged.size())) + << changed.get(); + + // When fds are replayed, it will be replaced by /dev/null..reading from it should yield + // null data + recordReplay(&IBinderRecordReplayTest::setFileDescriptor, std::move(unique_fd(dup(saved))), + &IBinderRecordReplayTest::getFileDescriptor, std::move(unique_fd(dup(changed)))); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 83db6c9b6d..fbab8f08a9 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -131,6 +131,13 @@ cc_library { "libcutils", "libutils", ], + static_libs: [ + "libbinder_random_parcel", + ], + include_dirs: [ + "bionic/libc/kernel/android/uapi/", + "bionic/libc/kernel/uapi/", + ], local_include_dirs: [ "include_random_parcel_seeds", ], @@ -140,8 +147,12 @@ cc_library { cc_binary_host { name: "binder2corpus", static_libs: [ + "libbinder_random_parcel", "libbinder_random_parcel_seeds", ], + cflags: [ + "-DBINDER_WITH_KERNEL_IPC", + ], srcs: [ "binder2corpus/binder2corpus.cpp", ], @@ -149,5 +160,6 @@ cc_binary_host { "libbase", "libbinder", "libutils", + "libcutils", ], } diff --git a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h index 8fc9263b98..7a1688b7b7 100644 --- a/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h +++ b/libs/binder/tests/parcel_fuzzer/include_random_parcel/fuzzbinder/random_binder.h @@ -16,11 +16,25 @@ #pragma once +#include <binder/Binder.h> #include <binder/IBinder.h> #include <fuzzer/FuzzedDataProvider.h> namespace android { +class RandomBinder : public BBinder { +public: + RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes); + const String16& getInterfaceDescriptor() const override; + status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override; + +private: + String16 mDescriptor; + // note may not all be used + std::vector<uint8_t> mBytes; + FuzzedDataProvider mProvider; +}; + // Get a random binder object for use in fuzzing. // // May return nullptr. diff --git a/libs/binder/tests/parcel_fuzzer/random_binder.cpp b/libs/binder/tests/parcel_fuzzer/random_binder.cpp index 8a1fecb212..f41c35bfbe 100644 --- a/libs/binder/tests/parcel_fuzzer/random_binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_binder.cpp @@ -21,56 +21,52 @@ #include <binder/IInterface.h> #include <binder/IServiceManager.h> +size_t kRandomInterfaceLength = 50; namespace android { -class RandomBinder : public BBinder { -public: - RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes) - : mDescriptor(descriptor), - mBytes(std::move(bytes)), - mProvider(mBytes.data(), mBytes.size()) {} - const String16& getInterfaceDescriptor() const override { return mDescriptor; } +RandomBinder::RandomBinder(const String16& descriptor, std::vector<uint8_t>&& bytes) + : mDescriptor(descriptor), + mBytes(std::move(bytes)), + mProvider(mBytes.data(), mBytes.size()) {} - status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override { - (void)code; - (void)data; - (void)reply; - (void)flags; // note - for maximum coverage even ignore if oneway +const String16& RandomBinder::getInterfaceDescriptor() const { + return mDescriptor; +} - if (mProvider.ConsumeBool()) { - return mProvider.ConsumeIntegral<status_t>(); - } +status_t RandomBinder::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + (void)code; + (void)data; + (void)reply; + (void)flags; // note - for maximum coverage even ignore if oneway - if (reply == nullptr) return OK; + if (mProvider.ConsumeBool()) { + return mProvider.ConsumeIntegral<status_t>(); + } - // TODO: things we could do to increase state space - // - also pull FDs and binders from 'data' - // (optionally combine these into random parcel 'options') - // - also pull FDs and binders from random parcel 'options' - RandomParcelOptions options; + if (reply == nullptr) return OK; - // random output - std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>( - mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes())); - fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options); + // TODO: things we could do to increase state space + // - also pull FDs and binders from 'data' + // (optionally combine these into random parcel 'options') + // - also pull FDs and binders from random parcel 'options' + RandomParcelOptions options; - return OK; - } + // random output + std::vector<uint8_t> subData = mProvider.ConsumeBytes<uint8_t>( + mProvider.ConsumeIntegralInRange<size_t>(0, mProvider.remaining_bytes())); + fillRandomParcel(reply, FuzzedDataProvider(subData.data(), subData.size()), &options); -private: - String16 mDescriptor; - - // note may not all be used - std::vector<uint8_t> mBytes; - FuzzedDataProvider mProvider; -}; + return OK; +} sp<IBinder> getRandomBinder(FuzzedDataProvider* provider) { auto makeFunc = provider->PickValueInArray<const std::function<sp<IBinder>()>>({ [&]() { // descriptor is the length of a class name, e.g. // "some.package.Foo" - std::string str = provider->ConsumeRandomLengthString(100 /*max length*/); + std::string str = + provider->ConsumeRandomLengthString(kRandomInterfaceLength /*max length*/); // arbitrarily consume remaining data to create a binder that can return // random results - coverage guided fuzzer should ensure all of the remaining diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp index 4e58dc4899..62b84330cd 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp @@ -73,7 +73,7 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti return; } - if (options->extraFds.size() > 0 && provider.ConsumeBool()) { + if (provider.ConsumeBool() && options->extraFds.size() > 0) { const unique_fd& fd = options->extraFds.at( provider.ConsumeIntegralInRange<size_t>(0, options->extraFds.size() - @@ -102,7 +102,7 @@ void fillRandomParcel(Parcel* p, FuzzedDataProvider&& provider, RandomParcelOpti } sp<IBinder> binder; - if (options->extraBinders.size() > 0 && provider.ConsumeBool()) { + if (provider.ConsumeBool() && options->extraBinders.size() > 0) { binder = options->extraBinders.at( provider.ConsumeIntegralInRange<size_t>(0, options->extraBinders diff --git a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp index 7b3c80642b..fd9777a916 100644 --- a/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp +++ b/libs/binder/tests/parcel_fuzzer/random_parcel_seeds.cpp @@ -14,16 +14,26 @@ * limitations under the License. */ +#include <linux/android/binder.h> + #include <android-base/logging.h> +#include <binder/Parcel.h> #include <binder/RecordedTransaction.h> #include <fuzzseeds/random_parcel_seeds.h> +#include <stack> +#include <string> #include "../../file.h" using android::binder::borrowed_fd; using android::binder::WriteFully; +using std::stack; + +extern size_t kRandomInterfaceLength; +// Keep this in sync with max_length in random_binder.cpp while creating a RandomBinder +std::string kRandomInterfaceName(kRandomInterfaceLength, 'i'); namespace android { namespace impl { @@ -66,6 +76,162 @@ void writeReversedBuffer(std::vector<uint8_t>& integralBuffer, T val) { } // namespace impl +struct ProviderMetadata { + size_t position; + size_t value; + + ProviderMetadata() { + value = 0; + position = 0; + } +}; + +// Assuming current seed path is inside the fillRandomParcel function, start of the loop. +void writeRandomBinder(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, + stack<ProviderMetadata>& remainingPositions) { + // Choose 2 index in array + size_t fillFuncIndex = 2; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + // navigate to getRandomBinder. provide consume bool false + bool flag = false; + impl::writeReversedBuffer(fillParcelBuffer, flag); + + // selecting RandomBinder, other binders in the list are not recorded as KernelObjects + size_t randomBinderIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + randomBinderIndex); + + // write random string of length 100 in actual buffer array. + CHECK(WriteFully(fd, kRandomInterfaceName.c_str(), kRandomInterfaceName.size())) << fd.get(); + + // These will be bytes which are used inside of RandomBinder + // simplest path for these bytes is going to be consume bool -> return random status + vector<uint8_t> randomBinderBuffer; + + bool returnRandomInt = true; + impl::writeReversedBuffer(randomBinderBuffer, returnRandomInt); + + status_t randomStatus = 0; + impl::writeReversedBuffer(randomBinderBuffer, randomStatus); + + // write integral in range to consume bytes for random binder + ProviderMetadata providerData; + providerData.position = fillParcelBuffer.size(); + providerData.value = randomBinderBuffer.size(); + remainingPositions.push(providerData); + + // Write to fd + CHECK(WriteFully(fd, randomBinderBuffer.data(), randomBinderBuffer.size())) << fd.get(); +} + +// Assuming current seed path is inside the fillRandomParcelFunction, start of the loop. +void writeRandomFd(vector<uint8_t>& fillParcelBuffer) { + // path to random fd + size_t fillFuncIndex = 1; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + bool flag = false; + impl::writeReversedBuffer(fillParcelBuffer, flag); + + // go for /dev/null index 1 + size_t fdIndex = 1; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(3), + fdIndex); +} + +void writeParcelData(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, + stack<ProviderMetadata>& remainingPositions, const uint8_t* data, size_t start, + size_t length) { + // need to write parcel data till next offset with instructions to pick random bytes till offset + size_t fillFuncIndex = 0; + impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), + fillFuncIndex); + + // provide how much bytes to read in control buffer + ProviderMetadata providerData; + providerData.position = fillParcelBuffer.size(); + providerData.value = length; + remainingPositions.push(providerData); + + // provide actual bytes + CHECK(WriteFully(fd, data + start, length)) << fd.get(); +} + +/** + * Generate sequence of copy data, write fd and write binder instructions and required data. + * Data which will be read using consumeBytes is written to fd directly. Data which is read in + * form integer is consumed from rear end FuzzedDataProvider. So insert it in fillParcelBuffer and + * then write to fd + */ +size_t regenerateParcel(borrowed_fd fd, vector<uint8_t>& fillParcelBuffer, const Parcel& p, + size_t dataSize, const vector<uint64_t>& objectOffsets) { + stack<ProviderMetadata> remainingPositions; + size_t copiedDataPosition = 0; + const uint8_t* parcelData = p.data(); + size_t numBinders = 0; + size_t numFds = 0; + + for (auto offset : objectOffsets) { + // Check what type of object is present here + const flat_binder_object* flatObject = + reinterpret_cast<const flat_binder_object*>(parcelData + offset); + // Copy till the object offset + writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition, + offset - copiedDataPosition); + copiedDataPosition = offset; + if (flatObject->hdr.type == BINDER_TYPE_BINDER || + flatObject->hdr.type == BINDER_TYPE_HANDLE) { + writeRandomBinder(fd, fillParcelBuffer, remainingPositions); + numBinders++; + // In case of binder, stability is written after the binder object. + // We want to move the copiedDataPosition further to account for this stability field + copiedDataPosition += sizeof(int32_t) + sizeof(flat_binder_object); + } else if (flatObject->hdr.type == BINDER_TYPE_FD) { + writeRandomFd(fillParcelBuffer); + numFds++; + copiedDataPosition += sizeof(flat_binder_object); + } + } + + if (copiedDataPosition < dataSize) { + // copy remaining data from recorded parcel -> last Object to end of the data + writeParcelData(fd, fillParcelBuffer, remainingPositions, parcelData, copiedDataPosition, + dataSize - copiedDataPosition); + } + + // We need to write bytes for selecting integer within range of 0 to provide.remaining_bytes() + // is called. + size_t totalWrittenBytes = dataSize - (sizeof(flat_binder_object) * objectOffsets.size()) - + (sizeof(int32_t) * numBinders) + + (kRandomInterfaceName.size() /*Interface String*/ + sizeof(bool) + sizeof(status_t)) * + numBinders; + + // Code in fuzzService relies on provider.remaining_bytes() to select random bytes using + // consume integer. use the calculated remaining_bytes to generate byte buffer which can + // generate required fds and binders in fillRandomParcel function. + while (!remainingPositions.empty()) { + auto meta = remainingPositions.top(); + remainingPositions.pop(); + size_t remainingBytes = totalWrittenBytes + fillParcelBuffer.size() - meta.position; + + vector<uint8_t> remReversedBytes; + impl::writeReversedBuffer(remReversedBytes, static_cast<size_t>(0), remainingBytes, + meta.value); + // Check the order of buffer which is being written + fillParcelBuffer.insert(fillParcelBuffer.end() - meta.position, remReversedBytes.begin(), + remReversedBytes.end()); + } + + return totalWrittenBytes; +} + +/** + * Current corpus format + * |Reserved bytes(8)|parcel data|fillParcelBuffer|integralBuffer| + */ void generateSeedsFromRecording(borrowed_fd fd, const binder::debug::RecordedTransaction& transaction) { // Write Reserved bytes for future use @@ -123,17 +289,9 @@ void generateSeedsFromRecording(borrowed_fd fd, uint8_t writeHeaderInternal = 0; impl::writeReversedBuffer(fillParcelBuffer, writeHeaderInternal); - // Choose to write data in parcel - size_t fillFuncIndex = 0; - impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), static_cast<size_t>(2), - fillFuncIndex); - - // Write parcel data size from recorded transaction - size_t toWrite = transaction.getDataParcel().dataBufferSize(); - impl::writeReversedBuffer(fillParcelBuffer, static_cast<size_t>(0), toWrite, toWrite); - - // Write parcel data with size towrite from recorded transaction - CHECK(WriteFully(fd, dataParcel.data(), toWrite)) << fd.get(); + auto objectMetadata = transaction.getObjectOffsets(); + size_t toWrite = regenerateParcel(fd, fillParcelBuffer, dataParcel, dataParcel.dataBufferSize(), + objectMetadata); // Write Fill Parcel buffer size in integralBuffer so that fuzzService knows size of data size_t subDataSize = toWrite + fillParcelBuffer.size(); diff --git a/libs/bufferstreams/Android.bp b/libs/bufferstreams/Android.bp index 365fc457d1..6c2a980f71 100644 --- a/libs/bufferstreams/Android.bp +++ b/libs/bufferstreams/Android.bp @@ -19,6 +19,7 @@ package { aconfig_declarations { name: "bufferstreams_flags", package: "com.android.graphics.bufferstreams.flags", + container: "system", srcs: [ "aconfig/bufferstreams_flags.aconfig", ], diff --git a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig index e258725e3d..d0f7812d21 100644 --- a/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig +++ b/libs/bufferstreams/aconfig/bufferstreams_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.bufferstreams.flags" +container: "system" flag { name: "bufferstreams_steel_thread" diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 918680d6a7..5ac965f566 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -10,11 +10,15 @@ package { cc_test { name: "ftl_test", test_suites: ["device-tests"], + header_libs: [ + "libbase_headers", + ], srcs: [ "algorithm_test.cpp", "cast_test.cpp", "concat_test.cpp", "enum_test.cpp", + "expected_test.cpp", "fake_guard_test.cpp", "flags_test.cpp", "function_test.cpp", diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp new file mode 100644 index 0000000000..8cb07e4696 --- /dev/null +++ b/libs/ftl/expected_test.cpp @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ftl/expected.h> +#include <gtest/gtest.h> + +#include <string> +#include <system_error> + +namespace android::test { + +using IntExp = ftl::Expected<int, std::errc>; +using StringExp = ftl::Expected<std::string, std::errc>; + +using namespace std::string_literals; + +TEST(Expected, Construct) { + // Default value. + EXPECT_TRUE(IntExp().has_value()); + EXPECT_EQ(IntExp(), IntExp(0)); + + EXPECT_TRUE(StringExp().has_value()); + EXPECT_EQ(StringExp(), StringExp("")); + + // Value. + ASSERT_TRUE(IntExp(42).has_value()); + EXPECT_EQ(42, IntExp(42).value()); + + ASSERT_TRUE(StringExp("test").has_value()); + EXPECT_EQ("test"s, StringExp("test").value()); + + // Error. + const auto exp = StringExp(ftl::Unexpected(std::errc::invalid_argument)); + ASSERT_FALSE(exp.has_value()); + EXPECT_EQ(std::errc::invalid_argument, exp.error()); +} + +TEST(Expected, HasError) { + EXPECT_FALSE(IntExp(123).has_error([](auto) { return true; })); + EXPECT_FALSE(IntExp(ftl::Unexpected(std::errc::io_error)).has_error([](auto) { return false; })); + + EXPECT_TRUE(StringExp(ftl::Unexpected(std::errc::permission_denied)).has_error([](auto e) { + return e == std::errc::permission_denied; + })); +} + +TEST(Expected, ValueOpt) { + EXPECT_EQ(ftl::Optional(-1), IntExp(-1).value_opt()); + EXPECT_EQ(std::nullopt, IntExp(ftl::Unexpected(std::errc::broken_pipe)).value_opt()); + + { + const StringExp exp("foo"s); + EXPECT_EQ(ftl::Optional('f'), + exp.value_opt().transform([](const auto& s) { return s.front(); })); + EXPECT_EQ("foo"s, exp.value()); + } + { + StringExp exp("foobar"s); + EXPECT_EQ(ftl::Optional(6), std::move(exp).value_opt().transform(&std::string::length)); + EXPECT_TRUE(exp.value().empty()); + } +} + +} // namespace android::test diff --git a/libs/ftl/small_map_test.cpp b/libs/ftl/small_map_test.cpp index 634877f672..e96d70d8ad 100644 --- a/libs/ftl/small_map_test.cpp +++ b/libs/ftl/small_map_test.cpp @@ -189,9 +189,20 @@ TEST(SmallMap, Get) { } } -TEST(SmallMap, TryEmplace) { - SmallMap<int, std::string, 3> map; - using Pair = decltype(map)::value_type; +template <typename Capacity> +struct SmallMapTest : testing::Test { + static constexpr std::size_t kCapacity = Capacity{}(); +}; + +template <std::size_t N> +using Capacity = std::integral_constant<std::size_t, N>; + +using Capacities = testing::Types<Capacity<3>, Capacity<0>>; +TYPED_TEST_SUITE(SmallMapTest, Capacities, ); + +TYPED_TEST(SmallMapTest, TryEmplace) { + SmallMap<int, std::string, TestFixture::kCapacity> map; + using Pair = typename decltype(map)::value_type; { const auto [it, ok] = map.try_emplace(123, "abc"); @@ -207,14 +218,22 @@ TEST(SmallMap, TryEmplace) { const auto [it, ok] = map.try_emplace(-1); ASSERT_TRUE(ok); EXPECT_EQ(*it, Pair(-1, std::string())); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion fails if mapping exists. const auto [it, ok] = map.try_emplace(42, "!!!"); EXPECT_FALSE(ok); EXPECT_EQ(*it, Pair(42, "???")); - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } } { // Insertion at capacity promotes the map. @@ -240,9 +259,9 @@ struct String { } // namespace -TEST(SmallMap, TryReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, TryReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // Replacing fails unless mapping exists. @@ -260,7 +279,12 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_TRUE(map.try_emplace(3, "abc").second); EXPECT_TRUE(map.try_emplace(4, "d").second); EXPECT_TRUE(map.dynamic()); @@ -284,9 +308,9 @@ TEST(SmallMap, TryReplace) { EXPECT_EQ(map, SmallMap(ftl::init::map(4, "d"s)(3, "c"s)(2, "b"s)(1, "a"s))); } -TEST(SmallMap, EmplaceOrReplace) { - SmallMap<int, String, 3> map = ftl::init::map(1, "a")(2, "B"); - using Pair = decltype(map)::value_type; +TYPED_TEST(SmallMapTest, EmplaceOrReplace) { + SmallMap<int, String, TestFixture::kCapacity> map = ftl::init::map(1, "a")(2, "B"); + using Pair = typename decltype(map)::value_type; { // New mapping is emplaced. @@ -305,7 +329,12 @@ TEST(SmallMap, EmplaceOrReplace) { EXPECT_EQ(*it, Pair(2, "b")); } - EXPECT_FALSE(map.dynamic()); + if constexpr (map.static_capacity() > 0) { + EXPECT_FALSE(map.dynamic()); + } else { + EXPECT_TRUE(map.dynamic()); + } + EXPECT_FALSE(map.emplace_or_replace(3, "abc").second); // Replace. EXPECT_TRUE(map.emplace_or_replace(4, "d").second); // Emplace. EXPECT_TRUE(map.dynamic()); diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index eb4d3df21d..4c3cc6cc3d 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -23,6 +23,7 @@ package { aconfig_declarations { name: "libgui_flags", package: "com.android.graphics.libgui.flags", + container: "system", srcs: ["libgui_flags.aconfig"], } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 19693e37cf..fb69fda32d 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -887,6 +887,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, int callbackTicket = 0; uint64_t currentFrameNumber = 0; BufferItem item; + int connectedApi; + sp<Fence> lastQueuedFence; + { // Autolock scope std::lock_guard<std::mutex> lock(mCore->mMutex); @@ -1056,6 +1059,13 @@ status_t BufferQueueProducer::queueBuffer(int slot, callbackTicket = mNextCallbackTicket++; VALIDATE_CONSISTENCY(); + + connectedApi = mCore->mConnectedApi; + lastQueuedFence = std::move(mLastQueueBufferFence); + + mLastQueueBufferFence = std::move(acquireFence); + mLastQueuedCrop = item.mCrop; + mLastQueuedTransform = item.mTransform; } // Autolock scope // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because @@ -1079,9 +1089,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, // Call back without the main BufferQueue lock held, but with the callback // lock held so we can ensure that callbacks occur in order - int connectedApi; - sp<Fence> lastQueuedFence; - { // scope for the lock std::unique_lock<std::mutex> lock(mCallbackMutex); while (callbackTicket != mCurrentCallbackTicket) { @@ -1094,13 +1101,6 @@ status_t BufferQueueProducer::queueBuffer(int slot, frameReplacedListener->onFrameReplaced(item); } - connectedApi = mCore->mConnectedApi; - lastQueuedFence = std::move(mLastQueueBufferFence); - - mLastQueueBufferFence = std::move(acquireFence); - mLastQueuedCrop = item.mCrop; - mLastQueuedTransform = item.mTransform; - ++mCurrentCallbackTicket; mCallbackCondition.notify_all(); } @@ -1653,9 +1653,10 @@ status_t BufferQueueProducer::setLegacyBufferDrop(bool drop) { status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, float outTransformMatrix[16]) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; @@ -1679,10 +1680,11 @@ status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, status_t BufferQueueProducer::getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence, Rect* outRect, uint32_t* outTransform) { ATRACE_CALL(); - BQ_LOGV("getLastQueuedBuffer"); std::lock_guard<std::mutex> lock(mCore->mMutex); - if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT) { + BQ_LOGV("getLastQueuedBuffer, slot=%d", mCore->mLastQueuedSlot); + if (mCore->mLastQueuedSlot == BufferItem::INVALID_BUFFER_SLOT || + mSlots[mCore->mLastQueuedSlot].mBufferState.isDequeued()) { *outBuffer = nullptr; *outFence = Fence::NO_FENCE; return NO_ERROR; diff --git a/libs/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp index 7d37fd37cb..4518b67d4c 100644 --- a/libs/gui/Choreographer.cpp +++ b/libs/gui/Choreographer.cpp @@ -116,7 +116,7 @@ Choreographer::~Choreographer() { std::lock_guard<std::mutex> _l(gChoreographers.lock); gChoreographers.ptrs.erase(std::remove_if(gChoreographers.ptrs.begin(), gChoreographers.ptrs.end(), - [=](Choreographer* c) { return c == this; }), + [=, this](Choreographer* c) { return c == this; }), gChoreographers.ptrs.end()); // Only poke DisplayManagerGlobal to unregister if we previously registered // callbacks. diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 9d7d304afb..1e0aacddab 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -199,7 +199,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeParcelable, trustedPresentationListener); SAFE_PARCEL(output.writeFloat, currentHdrSdrRatio); SAFE_PARCEL(output.writeFloat, desiredHdrSdrRatio); - SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)) + SAFE_PARCEL(output.writeInt32, static_cast<int32_t>(cachingHint)); return NO_ERROR; } @@ -484,6 +484,12 @@ void layer_state_t::sanitize(int32_t permissions) { flags &= ~eLayerIsDisplayDecoration; ALOGE("Stripped attempt to set LayerIsDisplayDecoration in sanitize"); } + if ((mask & eCanOccludePresentation) && + !(permissions & Permission::ACCESS_SURFACE_FLINGER)) { + flags &= ~eCanOccludePresentation; + mask &= ~eCanOccludePresentation; + ALOGE("Stripped attempt to set eCanOccludePresentation in sanitize"); + } } if (what & layer_state_t::eInputInfoChanged) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3a02c644bf..4f1356bebb 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -56,6 +56,7 @@ #include <android-base/thread_annotations.h> #include <gui/LayerStatePermissions.h> +#include <gui/ScreenCaptureResults.h> #include <private/gui/ComposerService.h> #include <private/gui/ComposerServiceAIDL.h> @@ -3152,11 +3153,19 @@ status_t ScreenshotClient::captureDisplay(DisplayId displayId, const gui::Captur } status_t ScreenshotClient::captureLayers(const LayerCaptureArgs& captureArgs, - const sp<IScreenCaptureListener>& captureListener) { + const sp<IScreenCaptureListener>& captureListener, + bool sync) { sp<gui::ISurfaceComposer> s(ComposerServiceAIDL::getComposerService()); if (s == nullptr) return NO_INIT; - binder::Status status = s->captureLayers(captureArgs, captureListener); + binder::Status status; + if (sync) { + gui::ScreenCaptureResults captureResults; + status = s->captureLayersSync(captureArgs, &captureResults); + captureListener->onScreenCaptureCompleted(captureResults); + } else { + status = s->captureLayers(captureArgs, captureListener); + } return statusTFromBinderStatus(status); } diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp index 95b2641f04..9429d2cc15 100644 --- a/libs/gui/WindowInfo.cpp +++ b/libs/gui/WindowInfo.cpp @@ -109,7 +109,8 @@ bool WindowInfo::operator==(const WindowInfo& info) const { info.inputConfig == inputConfig && info.displayId == displayId && info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && info.applicationInfo == applicationInfo && info.layoutParamsType == layoutParamsType && - info.layoutParamsFlags == layoutParamsFlags; + info.layoutParamsFlags == layoutParamsFlags && + info.canOccludePresentation == canOccludePresentation; } status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { @@ -158,8 +159,9 @@ status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { parcel->write(touchableRegion) ?: parcel->writeBool(replaceTouchableRegionWithCrop) ?: parcel->writeStrongBinder(touchableRegionCropHandle.promote()) ?: - parcel->writeStrongBinder(windowToken); - parcel->writeStrongBinder(focusTransferTarget); + parcel->writeStrongBinder(windowToken) ?: + parcel->writeStrongBinder(focusTransferTarget) ?: + parcel->writeBool(canOccludePresentation); // clang-format on return status; } @@ -210,7 +212,8 @@ status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { parcel->readBool(&replaceTouchableRegionWithCrop) ?: parcel->readNullableStrongBinder(&touchableRegionCropHandleSp) ?: parcel->readNullableStrongBinder(&windowToken) ?: - parcel->readNullableStrongBinder(&focusTransferTarget); + parcel->readNullableStrongBinder(&focusTransferTarget) ?: + parcel->readBool(&canOccludePresentation); // clang-format on @@ -273,6 +276,7 @@ std::ostream& operator<<(std::ostream& out, const WindowInfo& info) { << "ms, token=" << info.token.get() << ", touchOcclusionMode=" << ftl::enum_string(info.touchOcclusionMode) << "\n" << transform; + if (info.canOccludePresentation) out << " canOccludePresentation"; return out; } diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl index e3122bc300..51e01930d3 100644 --- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl +++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl @@ -46,6 +46,7 @@ import android.gui.LayerCaptureArgs; import android.gui.LayerDebugInfo; import android.gui.OverlayProperties; import android.gui.PullAtomData; +import android.gui.ScreenCaptureResults; import android.gui.ARect; import android.gui.SchedulingPolicy; import android.gui.StalledTransactionInfo; @@ -245,6 +246,16 @@ interface ISurfaceComposer { /** * Capture a subtree of the layer hierarchy, potentially ignoring the root node. * This requires READ_FRAME_BUFFER permission. This function will fail if there + * is a secure window on screen. This is a blocking call and will return the + * ScreenCaptureResults, including the captured buffer. Because this is blocking, the + * caller doesn't care about the fence and the binder thread in SurfaceFlinger will wait + * on the fence to fire before returning the results. + */ + ScreenCaptureResults captureLayersSync(in LayerCaptureArgs args); + + /** + * Capture a subtree of the layer hierarchy, potentially ignoring the root node. + * This requires READ_FRAME_BUFFER permission. This function will fail if there * is a secure window on screen */ oneway void captureLayers(in LayerCaptureArgs args, IScreenCaptureListener listener); diff --git a/libs/gui/fuzzer/libgui_fuzzer_utils.h b/libs/gui/fuzzer/libgui_fuzzer_utils.h index c952ba2e10..2bdbd43233 100644 --- a/libs/gui/fuzzer/libgui_fuzzer_utils.h +++ b/libs/gui/fuzzer/libgui_fuzzer_utils.h @@ -104,6 +104,8 @@ public: (int64_t, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&), (override)); MOCK_METHOD(binder::Status, captureLayers, (const LayerCaptureArgs&, const sp<IScreenCaptureListener>&), (override)); + MOCK_METHOD(binder::Status, captureLayersSync, + (const LayerCaptureArgs&, gui::ScreenCaptureResults*), (override)); MOCK_METHOD(binder::Status, clearAnimationFrameStats, (), (override)); MOCK_METHOD(binder::Status, getAnimationFrameStats, (gui::FrameStats*), (override)); MOCK_METHOD(binder::Status, overrideHdrTypes, (const sp<IBinder>&, const std::vector<int32_t>&), diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 9350c95178..0fedea7b9e 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -161,6 +161,9 @@ struct layer_state_t { // See SurfaceView scaling behavior for more details. eIgnoreDestinationFrame = 0x400, eLayerIsRefreshRateIndicator = 0x800, // REFRESH_RATE_INDICATOR + // Sets a property on this layer indicating that its visible region should be considered + // when computing TrustedPresentation Thresholds. + eCanOccludePresentation = 0x1000, }; enum { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 7118883411..288882695a 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -850,7 +850,8 @@ public: static status_t captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); static status_t captureDisplay(DisplayId, const gui::CaptureArgs&, const sp<IScreenCaptureListener>&); - static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); + static status_t captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&, + bool sync); [[deprecated]] static status_t captureDisplay(DisplayId id, const sp<IScreenCaptureListener>& listener) { diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index e72fd59147..32d60be612 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -246,6 +246,10 @@ struct WindowInfo : public Parcelable { // any other window. sp<IBinder> focusTransferTarget; + // Sets a property on this window indicating that its visible region should be considered when + // computing TrustedPresentation Thresholds. + bool canOccludePresentation = false; + void setInputConfig(ftl::Flags<InputConfig> config, bool value); void addTouchableRegion(const Rect& region); diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig index b081030c9f..38646992da 100644 --- a/libs/gui/libgui_flags.aconfig +++ b/libs/gui/libgui_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.libgui.flags" +container: "system" flag { name: "bq_setframerate" diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 6dcd501404..e606b9941e 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -3,6 +3,7 @@ // Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE) // to integrate with auto-test framework. package { + default_team: "trendy_team_android_core_graphics_stack", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index b9ab803101..a9d6e8d3bf 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -63,8 +63,7 @@ namespace android::test { using Transaction = SurfaceComposerClient::Transaction; sp<IInputFlinger> getInputFlinger() { - sp<IBinder> input(defaultServiceManager()->getService( - String16("inputflinger"))); + sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); if (input == nullptr) { ALOGE("Failed to link to input service"); } else { ALOGE("Linked to input"); } @@ -104,8 +103,13 @@ public: if (noInputChannel) { mInputInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, true); } else { - mClientChannel = std::make_shared<InputChannel>(); - mInputFlinger->createInputChannel("testchannels", mClientChannel.get()); + android::os::InputChannelCore tempChannel; + android::binder::Status result = + mInputFlinger->createInputChannel("testchannels", &tempChannel); + if (!result.isOk()) { + ADD_FAILURE() << "binder call to createInputChannel failed"; + } + mClientChannel = InputChannel::create(std::move(tempChannel)); mInputInfo.token = mClientChannel->getConnectionToken(); mInputConsumer = new InputConsumer(mClientChannel); } diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index c6ea317949..577d2394c6 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -791,6 +791,10 @@ public: return binder::Status::ok(); } + binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults*) override { + return binder::Status::ok(); + } + binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override { return binder::Status::ok(); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 3d3bf1355f..8b693390d0 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -39,6 +39,7 @@ filegroup { aconfig_declarations { name: "com.android.input.flags-aconfig", package: "com.android.input.flags", + container: "system", srcs: ["input_flags.aconfig"], } @@ -175,6 +176,7 @@ cc_library { ], srcs: [ "android/os/IInputFlinger.aidl", + "android/os/InputChannelCore.aidl", "AccelerationCurve.cpp", "Input.cpp", "InputDevice.cpp", diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8eaff00cbd..0d29b4db96 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -374,7 +374,7 @@ std::ostream& operator<<(std::ostream& out, const KeyEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } @@ -1051,7 +1051,7 @@ std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { out << ", deviceId=" << event.getDeviceId(); out << ", source=" << inputEventSourceToString(event.getSource()); out << ", displayId=" << event.getDisplayId(); - out << ", eventId=" << event.getId(); + out << ", eventId=0x" << std::hex << event.getId() << std::dec; out << "}"; return out; } diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 9c7c0c19ed..d4dbc45090 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -190,14 +190,16 @@ InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) mHasSensor(other.mHasSensor), mMotionRanges(other.mMotionRanges), mSensors(other.mSensors), - mLights(other.mLights) {} + mLights(other.mLights), + mViewBehavior(other.mViewBehavior) {} InputDeviceInfo::~InputDeviceInfo() { } void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, const InputDeviceIdentifier& identifier, const std::string& alias, - bool isExternal, bool hasMic, int32_t associatedDisplayId) { + bool isExternal, bool hasMic, int32_t associatedDisplayId, + InputDeviceViewBehavior viewBehavior) { mId = id; mGeneration = generation; mControllerNumber = controllerNumber; @@ -212,6 +214,7 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control mHasBattery = false; mHasButtonUnderPad = false; mHasSensor = false; + mViewBehavior = viewBehavior; mUsiVersion.reset(); mMotionRanges.clear(); mSensors.clear(); diff --git a/libs/input/InputEventLabels.cpp b/libs/input/InputEventLabels.cpp index 0e627e56fd..8db0ca588b 100644 --- a/libs/input/InputEventLabels.cpp +++ b/libs/input/InputEventLabels.cpp @@ -348,7 +348,9 @@ namespace android { DEFINE_KEYCODE(MACRO_1), \ DEFINE_KEYCODE(MACRO_2), \ DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) + DEFINE_KEYCODE(MACRO_4), \ + DEFINE_KEYCODE(EMOJI_PICKER), \ + DEFINE_KEYCODE(SCREENSHOT) // NOTE: If you add a new axis here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 37de00cf35..e49f4eb6f6 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -95,6 +95,21 @@ bool debugResampling() { return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO); } +android::base::unique_fd dupChannelFd(int fd) { + android::base::unique_fd newFd(::dup(fd)); + if (!newFd.ok()) { + ALOGE("Could not duplicate fd %i : %s", fd, strerror(errno)); + const bool hitFdLimit = errno == EMFILE || errno == ENFILE; + // If this process is out of file descriptors, then throwing that might end up exploding + // on the other side of a binder call, which isn't really helpful. + // Better to just crash here and hope that the FD leak is slow. + // Other failures could be client errors, so we still propagate those back to the caller. + LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel"); + return {}; + } + return newFd; +} + } // namespace using android::base::Result; @@ -395,15 +410,23 @@ std::unique_ptr<InputChannel> InputChannel::create(const std::string& name, return std::unique_ptr<InputChannel>(new InputChannel(name, std::move(fd), token)); } -InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) - : mName(std::move(name)), mFd(std::move(fd)), mToken(std::move(token)) { +std::unique_ptr<InputChannel> InputChannel::create( + android::os::InputChannelCore&& parceledChannel) { + return InputChannel::create(parceledChannel.name, parceledChannel.fd.release(), + parceledChannel.token); +} + +InputChannel::InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token) { + this->name = std::move(name); + this->fd.reset(std::move(fd)); + this->token = std::move(token); ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel constructed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } InputChannel::~InputChannel() { ALOGD_IF(DEBUG_CHANNEL_LIFECYCLE, "Input channel destroyed: name='%s', fd=%d", - getName().c_str(), getFd().get()); + getName().c_str(), getFd()); } status_t InputChannel::openInputChannelPair(const std::string& name, @@ -441,19 +464,19 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { ATRACE_NAME_IF(ATRACE_ENABLED(), StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type)); + name.c_str(), msg->header.seq, msg->header.type)); const size_t msgLength = msg->size(); InputMessage cleanMsg; msg->getSanitizedCopy(&cleanMsg); ssize_t nWrite; do { - nWrite = ::send(getFd().get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); + nWrite = ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); if (nWrite < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ error sending message of type %s, %s", - mName.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); + name.c_str(), ftl::enum_string(msg->header.type).c_str(), strerror(error)); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -465,12 +488,12 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { if (size_t(nWrite) != msgLength) { ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ error sending message type %s, send was incomplete", mName.c_str(), + "channel '%s' ~ error sending message type %s, send was incomplete", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return DEAD_OBJECT; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ sent message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); return OK; @@ -479,13 +502,13 @@ status_t InputChannel::sendMessage(const InputMessage* msg) { status_t InputChannel::receiveMessage(InputMessage* msg) { ssize_t nRead; do { - nRead = ::recv(getFd().get(), msg, sizeof(InputMessage), MSG_DONTWAIT); + nRead = ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); } while (nRead == -1 && errno == EINTR); if (nRead < 0) { int error = errno; ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ receive message failed, errno=%d", - mName.c_str(), errno); + name.c_str(), errno); if (error == EAGAIN || error == EWOULDBLOCK) { return WOULD_BLOCK; } @@ -497,29 +520,29 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { if (nRead == 0) { // check for EOF ALOGD_IF(DEBUG_CHANNEL_MESSAGES, - "channel '%s' ~ receive message failed because peer was closed", mName.c_str()); + "channel '%s' ~ receive message failed because peer was closed", name.c_str()); return DEAD_OBJECT; } if (!msg->isValid(nRead)) { - ALOGE("channel '%s' ~ received invalid message of size %zd", mName.c_str(), nRead); + ALOGE("channel '%s' ~ received invalid message of size %zd", name.c_str(), nRead); return BAD_VALUE; } - ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", mName.c_str(), + ALOGD_IF(DEBUG_CHANNEL_MESSAGES, "channel '%s' ~ received message of type %s", name.c_str(), ftl::enum_string(msg->header.type).c_str()); if (ATRACE_ENABLED()) { // Add an additional trace point to include data about the received message. std::string message = StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32 ")", - mName.c_str(), msg->header.seq, msg->header.type); + name.c_str(), msg->header.seq, msg->header.type); ATRACE_NAME(message.c_str()); } return OK; } bool InputChannel::probablyHasInput() const { - struct pollfd pfds = {.fd = mFd, .events = POLLIN}; + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) { // This can be a false negative because EINTR and ENOMEM are not handled. The latter should // be extremely rare. The EINTR is also unlikely because it happens only when the signal @@ -538,7 +561,7 @@ void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { if (timeout < 0ms) { LOG(FATAL) << "Timeout cannot be negative, received " << timeout.count(); } - struct pollfd pfds = {.fd = mFd, .events = POLLIN}; + struct pollfd pfds = {.fd = fd.get(), .events = POLLIN}; int ret; std::chrono::time_point<std::chrono::steady_clock> stopTime = std::chrono::steady_clock::now() + timeout; @@ -551,59 +574,31 @@ void InputChannel::waitForMessage(std::chrono::milliseconds timeout) const { } std::unique_ptr<InputChannel> InputChannel::dup() const { - base::unique_fd newFd(dupFd()); + base::unique_fd newFd(dupChannelFd(fd.get())); return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); } -void InputChannel::copyTo(InputChannel& outChannel) const { - outChannel.mName = getName(); - outChannel.mFd = dupFd(); - outChannel.mToken = getConnectionToken(); -} - -status_t InputChannel::writeToParcel(android::Parcel* parcel) const { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - return parcel->writeStrongBinder(mToken) - ?: parcel->writeUtf8AsUtf16(mName) ?: parcel->writeUniqueFileDescriptor(mFd); +void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const { + outChannel.name = getName(); + outChannel.fd.reset(dupChannelFd(fd.get())); + outChannel.token = getConnectionToken(); } -status_t InputChannel::readFromParcel(const android::Parcel* parcel) { - if (parcel == nullptr) { - ALOGE("%s: Null parcel", __func__); - return BAD_VALUE; - } - mToken = parcel->readStrongBinder(); - return parcel->readUtf8FromUtf16(&mName) ?: parcel->readUniqueFileDescriptor(&mFd); +void InputChannel::moveChannel(std::unique_ptr<InputChannel> from, + android::os::InputChannelCore& outChannel) { + outChannel.name = from->getName(); + outChannel.fd = android::os::ParcelFileDescriptor(std::move(from->fd)); + outChannel.token = from->getConnectionToken(); } sp<IBinder> InputChannel::getConnectionToken() const { - return mToken; -} - -base::unique_fd InputChannel::dupFd() const { - base::unique_fd newFd(::dup(getFd().get())); - if (!newFd.ok()) { - ALOGE("Could not duplicate fd %i for channel %s: %s", getFd().get(), getName().c_str(), - strerror(errno)); - const bool hitFdLimit = errno == EMFILE || errno == ENFILE; - // If this process is out of file descriptors, then throwing that might end up exploding - // on the other side of a binder call, which isn't really helpful. - // Better to just crash here and hope that the FD leak is slow. - // Other failures could be client errors, so we still propagate those back to the caller. - LOG_ALWAYS_FATAL_IF(hitFdLimit, "Too many open files, could not duplicate input channel %s", - getName().c_str()); - return {}; - } - return newFd; + return token; } // --- InputPublisher --- InputPublisher::InputPublisher(const std::shared_ptr<InputChannel>& channel) - : mChannel(channel), mInputVerifier(channel->getName()) {} + : mChannel(channel), mInputVerifier(mChannel->getName()) {} InputPublisher::~InputPublisher() { } diff --git a/libs/input/android/os/IInputFlinger.aidl b/libs/input/android/os/IInputFlinger.aidl index 00ebd4d34f..c1aacfbc53 100644 --- a/libs/input/android/os/IInputFlinger.aidl +++ b/libs/input/android/os/IInputFlinger.aidl @@ -16,14 +16,13 @@ package android.os; -import android.InputChannel; +import android.os.InputChannelCore; import android.gui.FocusRequest; -import android.gui.WindowInfo; /** @hide */ interface IInputFlinger { - InputChannel createInputChannel(in @utf8InCpp String name); + InputChannelCore createInputChannel(in @utf8InCpp String name); void removeInputChannel(in IBinder connectionToken); /** * Sets focus to the window identified by the token. This must be called diff --git a/libs/input/android/InputChannel.aidl b/libs/input/android/os/InputChannelCore.aidl index c2d1112dd3..888a553268 100644 --- a/libs/input/android/InputChannel.aidl +++ b/libs/input/android/os/InputChannelCore.aidl @@ -15,6 +15,16 @@ ** limitations under the License. */ -package android; +package android.os; -parcelable InputChannel cpp_header "input/InputTransport.h"; +import android.os.ParcelFileDescriptor; + +/** + * Input channel struct for sending InputChannel between processes. + * @hide + */ +parcelable InputChannelCore { + @utf8InCpp String name; + ParcelFileDescriptor fd; + IBinder token; +} diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 0c850fe0ee..5af48550c6 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.input.flags" +container: "system" flag { name: "enable_outbound_event_verification" @@ -26,6 +27,13 @@ flag { namespace: "input" description: "Set to true to enable timer support for the touchpad Gestures library" bug: "297192727" + } + + flag { + name: "enable_input_event_tracing" + namespace: "input" + description: "Set to true to enable input event tracing, including always-on tracing on non-user builds" + bug: "210460522" } flag { @@ -104,3 +112,10 @@ flag { description: "Move user-activity poke rate-limiting from PowerManagerService to InputDispatcher." bug: "320499729" } + +flag { + name: "input_device_view_behavior_api" + namespace: "input" + description: "Controls the API to provide InputDevice view behavior." + bug: "246946631" +} diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index 867af793bc..b1d7760682 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -79,7 +79,7 @@ impl InputVerifier { logger::init( logger::Config::default() .with_tag_on_device("InputVerifier") - .with_min_level(log::Level::Trace), + .with_max_level(log::LevelFilter::Trace), ); Self { name: name.to_owned(), diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 650c93034c..60feb53dcc 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -32,37 +32,31 @@ namespace android { +namespace { +bool operator==(const InputChannel& left, const InputChannel& right) { + struct stat lhs, rhs; + if (fstat(left.getFd(), &lhs) != 0) { + return false; + } + if (fstat(right.getFd(), &rhs) != 0) { + return false; + } + // If file descriptors are pointing to same inode they are duplicated fds. + return left.getName() == right.getName() && + left.getConnectionToken() == right.getConnectionToken() && lhs.st_ino == rhs.st_ino; +} +} // namespace + class InputChannelTest : public testing::Test { }; +TEST_F(InputChannelTest, ClientAndServerTokensMatch) { + std::unique_ptr<InputChannel> serverChannel, clientChannel; -TEST_F(InputChannelTest, ConstructorAndDestructor_TakesOwnershipOfFileDescriptors) { - // Our purpose here is to verify that the input channel destructor closes the - // file descriptor provided to it. One easy way is to provide it with one end - // of a pipe and to check for EPIPE on the other end after the channel is destroyed. - Pipe pipe; - - android::base::unique_fd sendFd(pipe.sendFd); - - std::unique_ptr<InputChannel> inputChannel = - InputChannel::create("channel name", std::move(sendFd), new BBinder()); - - EXPECT_NE(inputChannel, nullptr) << "channel should be successfully created"; - EXPECT_STREQ("channel name", inputChannel->getName().c_str()) - << "channel should have provided name"; - EXPECT_NE(-1, inputChannel->getFd()) << "channel should have valid fd"; - - // InputChannel should be the owner of the file descriptor now - ASSERT_FALSE(sendFd.ok()); -} - -TEST_F(InputChannelTest, SetAndGetToken) { - Pipe pipe; - sp<IBinder> token = new BBinder(); - std::unique_ptr<InputChannel> channel = - InputChannel::create("test channel", android::base::unique_fd(pipe.sendFd), token); - - EXPECT_EQ(token, channel->getConnectionToken()); + status_t result = + InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + EXPECT_EQ(serverChannel->getConnectionToken(), clientChannel->getConnectionToken()); } TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { @@ -71,8 +65,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); - ASSERT_EQ(OK, result) - << "should have successfully opened a channel pair"; + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; // Name EXPECT_STREQ("channel name (server)", serverChannel->getName().c_str()) @@ -235,25 +228,6 @@ TEST_F(InputChannelTest, SendAndReceive_MotionClassification) { } } -TEST_F(InputChannelTest, InputChannelParcelAndUnparcel) { - std::unique_ptr<InputChannel> serverChannel, clientChannel; - - status_t result = - InputChannel::openInputChannelPair("channel parceling", serverChannel, clientChannel); - - ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; - - InputChannel chan; - Parcel parcel; - ASSERT_EQ(OK, serverChannel->writeToParcel(&parcel)); - parcel.setDataPosition(0); - chan.readFromParcel(&parcel); - - EXPECT_EQ(chan == *serverChannel, true) - << "inputchannel should be equal after parceling and unparceling.\n" - << "name " << chan.getName() << " name " << serverChannel->getName(); -} - TEST_F(InputChannelTest, DuplicateChannelAndAssertEqual) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 2000335521..35430207f9 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -220,7 +220,6 @@ void waitUntilInputAvailable(const InputConsumer& inputConsumer) { class InputPublisherAndConsumerTest : public testing::Test { protected: - std::shared_ptr<InputChannel> mServerChannel, mClientChannel; std::unique_ptr<InputPublisher> mPublisher; std::unique_ptr<InputConsumer> mConsumer; PreallocatedInputEventFactory mEventFactory; @@ -230,11 +229,9 @@ protected: status_t result = InputChannel::openInputChannelPair("channel name", serverChannel, clientChannel); ASSERT_EQ(OK, result); - mServerChannel = std::move(serverChannel); - mClientChannel = std::move(clientChannel); - mPublisher = std::make_unique<InputPublisher>(mServerChannel); - mConsumer = std::make_unique<InputConsumer>(mClientChannel); + mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel)); + mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } void publishAndConsumeKeyEvent(); @@ -254,11 +251,7 @@ private: }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { - ASSERT_NE(nullptr, mPublisher->getChannel()); - ASSERT_NE(nullptr, mConsumer->getChannel()); - EXPECT_EQ(mServerChannel.get(), mPublisher->getChannel().get()); - EXPECT_EQ(mClientChannel.get(), mConsumer->getChannel().get()); - ASSERT_EQ(mPublisher->getChannel()->getConnectionToken(), + ASSERT_EQ(mPublisher->getChannel().getConnectionToken(), mConsumer->getChannel()->getConnectionToken()); } diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 745b6f30cc..52612870ab 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -586,7 +586,7 @@ int AHardwareBuffer_createFromHandle(const AHardwareBuffer_Desc* desc, return NO_ERROR; } -enum AHardwareBufferStatus AHardwareBuffer_allocate2( +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( const AHardwareBuffer_Desc* desc, const AHardwareBufferLongOptions* additionalOptions, size_t additionalOptionsSize, AHardwareBuffer** outBuffer) { (void)additionalOptions; diff --git a/libs/nativewindow/include/vndk/hardware_buffer.h b/libs/nativewindow/include/vndk/hardware_buffer.h index 491860125f..bcfae10201 100644 --- a/libs/nativewindow/include/vndk/hardware_buffer.h +++ b/libs/nativewindow/include/vndk/hardware_buffer.h @@ -150,7 +150,7 @@ enum AHardwareBufferStatus : int32_t { * AHARDWAREBUFFER_STATUS_UNKNOWN_ERROR for any other error * any reason. The returned buffer has a reference count of 1. */ -enum AHardwareBufferStatus AHardwareBuffer_allocate2( +enum AHardwareBufferStatus AHardwareBuffer_allocateWithOptions( const AHardwareBuffer_Desc* _Nonnull desc, const AHardwareBufferLongOptions* _Nullable additionalOptions, size_t additionalOptionsSize, AHardwareBuffer* _Nullable* _Nonnull outBuffer) __INTRODUCED_IN(__ANDROID_API_V__); diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index 8bc1292986..e29d5a6bb4 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,7 +2,7 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_allocate2; # llndk systemapi + AHardwareBuffer_allocateWithOptions; # llndk systemapi AHardwareBuffer_createFromHandle; # llndk systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 diff --git a/libs/nativewindow/rust/src/surface.rs b/libs/nativewindow/rust/src/surface.rs index c812612d40..25fea807b5 100644 --- a/libs/nativewindow/rust/src/surface.rs +++ b/libs/nativewindow/rust/src/surface.rs @@ -127,6 +127,9 @@ impl_serialize_for_unstructured_parcelable!(Surface); // SAFETY: The underlying *ANativeWindow can be moved between threads. unsafe impl Send for Surface {} +// SAFETY: The underlying *ANativeWindow can be used from multiple threads concurrently. +unsafe impl Sync for Surface {} + /// An error code returned by methods on [`Surface`]. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct ErrorCode(i32); diff --git a/libs/nativewindow/tests/AHardwareBufferTest.cpp b/libs/nativewindow/tests/AHardwareBufferTest.cpp index 1f0128a48e..136395aac0 100644 --- a/libs/nativewindow/tests/AHardwareBufferTest.cpp +++ b/libs/nativewindow/tests/AHardwareBufferTest.cpp @@ -188,7 +188,7 @@ TEST(AHardwareBufferTest, Allocate2NoExtras) { }; AHardwareBuffer* buffer = nullptr; - ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, nullptr, 0, &buffer)); + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, nullptr, 0, &buffer)); uint64_t id = 0; EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); EXPECT_NE(0, id); @@ -219,7 +219,7 @@ TEST(AHardwareBufferTest, Allocate2WithExtras) { std::array<AHardwareBufferLongOptions, 1> extras = {{ {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3}, }}; - ASSERT_EQ(0, AHardwareBuffer_allocate2(&desc, extras.data(), extras.size(), &buffer)); + ASSERT_EQ(0, AHardwareBuffer_allocateWithOptions(&desc, extras.data(), extras.size(), &buffer)); uint64_t id = 0; EXPECT_EQ(0, AHardwareBuffer_getId(buffer, &id)); EXPECT_NE(0, id); diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp index ba2eb7d224..fd45840cf8 100644 --- a/libs/renderengine/Android.bp +++ b/libs/renderengine/Android.bp @@ -48,11 +48,19 @@ cc_defaults { static_libs: [ "libshaders", "libtonemap", + "libsurfaceflinger_common", ], local_include_dirs: ["include"], export_include_dirs: ["include"], } +// Needed by FlagManager to access a #define. +cc_library_static { + name: "librenderengine_includes", + local_include_dirs: ["include"], + export_include_dirs: ["include"], +} + filegroup { name: "librenderengine_sources", srcs: [ diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp index 55c34cd059..87e21c2d70 100644 --- a/libs/renderengine/benchmark/Android.bp +++ b/libs/renderengine/benchmark/Android.bp @@ -37,6 +37,7 @@ cc_benchmark { static_libs: [ "librenderengine", "libshaders", + "libsurfaceflinger_common", "libtonemap", ], cflags: [ @@ -54,6 +55,7 @@ cc_benchmark { "libsync", "libui", "libutils", + "server_configurable_flags", ], data: ["resources/*"], diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp index fc84bbf283..6e393f03fa 100644 --- a/libs/renderengine/skia/SkiaRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaRenderEngine.cpp @@ -53,6 +53,7 @@ #include <SkSurface.h> #include <SkTileMode.h> #include <android-base/stringprintf.h> +#include <common/FlagManager.h> #include <gui/FenceMonitor.h> #include <gui/TraceUtils.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> @@ -419,6 +420,9 @@ void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, mGraphicBufferExternalRefs[buffer->getId()]++; if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { + if (FlagManager::getInstance().renderable_buffer_usage()) { + isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER; + } std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = std::make_shared<AutoBackendTexture::LocalRef>(grContext, buffer->toAHardwareBuffer(), @@ -760,10 +764,11 @@ void SkiaRenderEngine::drawLayersInternal( // save a snapshot of the activeSurface to use as input to the blur shaders blurInput = activeSurface->makeImageSnapshot(); - // blit the offscreen framebuffer into the destination AHB, but only - // if there are blur regions. backgroundBlurRadius blurs the entire - // image below, so it can skip this step. - if (layer.blurRegions.size()) { + // blit the offscreen framebuffer into the destination AHB. This ensures that + // even if the blurred image does not cover the screen (for example, during + // a rotation animation, or if blur regions are used), the entire screen is + // initialized. + if (layer.blurRegions.size() || FlagManager::getInstance().restore_blur_step()) { SkPaint paint; paint.setBlendMode(SkBlendMode::kSrc); if (CC_UNLIKELY(mCapture->isCaptureRunning())) { @@ -1016,7 +1021,7 @@ void SkiaRenderEngine::drawLayersInternal( .fakeOutputDataspace = fakeDataspace})); // Turn on dithering when dimming beyond this (arbitrary) threshold... - static constexpr float kDimmingThreshold = 0.2f; + static constexpr float kDimmingThreshold = 0.9f; // ...or we're rendering an HDR layer down to an 8-bit target // Most HDR standards require at least 10-bits of color depth for source content, so we // can just extract the transfer function rather than dig into precise gralloc layout. diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp index 3af85c064e..bff12ce7ff 100644 --- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp +++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp @@ -34,6 +34,8 @@ #include <cstdint> #include <memory> +#include <sstream> +#include <string> #include <vector> #include <vulkan/vulkan.h> @@ -67,6 +69,13 @@ struct DestroySemaphoreInfo { DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {} }; +namespace { +void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData); +} // anonymous namespace + struct VulkanInterface { bool initialized = false; VkInstance instance; @@ -79,6 +88,7 @@ struct VulkanInterface { VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr; VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr; VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr; + VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr; GrVkGetProc grGetProc; bool isProtected; bool isRealtimePriority; @@ -100,6 +110,8 @@ struct VulkanInterface { backendContext.fDeviceFeatures2 = physicalDeviceFeatures2; backendContext.fGetProc = grGetProc; backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo; + backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived + backendContext.fDeviceLostProc = onVkDeviceFault; return backendContext; }; @@ -178,6 +190,68 @@ struct VulkanInterface { } }; +namespace { +void onVkDeviceFault(void* callbackContext, const std::string& description, + const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos, + const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos, + const std::vector<std::byte>& vendorBinaryData) { + VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext); + const std::string protectedStr = interface->isProtected ? "protected" : "non-protected"; + // The final crash string should contain as much differentiating info as possible, up to 1024 + // bytes. As this final message is constructed, the same information is also dumped to the logs + // but in a more verbose format. Building the crash string is unsightly, so the clearer logging + // statement is always placed first to give context. + ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str()); + std::stringstream crashMsg; + crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr; + + if (!addressInfos.empty()) { + ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size()); + crashMsg << ", " << addressInfos.size() << " address info ("; + for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) { + ALOGE(" addressType: %d", (int)addressInfo.addressType); + ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress); + ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision); + crashMsg << addressInfo.addressType << ":" + << addressInfo.reportedAddress << ":" + << addressInfo.addressPrecision << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorInfos.empty()) { + ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size()); + crashMsg << ", " << vendorInfos.size() << " vendor info ("; + for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) { + ALOGE(" description: %s", vendorInfo.description); + ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode); + ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData); + // Omit descriptions for individual vendor info structs in the crash string, as the + // fault code and fault data fields should be enough for clustering, and the verbosity + // isn't worth it. Additionally, vendors may just set the general description field of + // the overall fault to the description of the first element in this list, and that + // overall description will be placed at the end of the crash string. + crashMsg << vendorInfo.vendorFaultCode << ":" + << vendorInfo.vendorFaultData << ", "; + } + crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", " + crashMsg << ")"; + } + + if (!vendorBinaryData.empty()) { + // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports + ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics" + " Stack team if you observe this message).", + vendorBinaryData.size()); + crashMsg << ", " << vendorBinaryData.size() << " bytes binary"; + } + + crashMsg << "): " << description; + LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str()); +}; +} // anonymous namespace + static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) { if (device != VK_NULL_HANDLE) { return vkGetDeviceProcAddr(device, proc_name); @@ -429,6 +503,14 @@ VulkanInterface initVulkanInterface(bool protectedContent = false) { tailPnext = &interface.protectedMemoryFeatures->pNext; } + if (interface.grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) { + interface.deviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT; + interface.deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; + interface.deviceFaultFeatures->pNext = nullptr; + *tailPnext = interface.deviceFaultFeatures; + tailPnext = &interface.deviceFaultFeatures->pNext; + } + vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2); // Looks like this would slow things down and we can't depend on it on all platforms interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE; @@ -545,6 +627,10 @@ void teardownVulkanInterface(VulkanInterface* interface) { delete interface->physicalDeviceFeatures2; } + if (interface->deviceFaultFeatures) { + delete interface->deviceFaultFeatures; + } + interface->samplerYcbcrConversionFeatures = nullptr; interface->physicalDeviceFeatures2 = nullptr; interface->protectedMemoryFeatures = nullptr; diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp index 5c9820cdc5..09f09a697a 100644 --- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp +++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp @@ -111,7 +111,7 @@ sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, constexpr int kSampleCount = 1; constexpr bool kMipmapped = false; constexpr SkSurfaceProps* kProps = nullptr; - sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, scaledInfo, + sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, scaledInfo, kSampleCount, kTopLeft_GrSurfaceOrigin, kProps, kMipmapped, input->isProtected()); LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__); diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp index 50e166d2a7..473e1d46b8 100644 --- a/libs/renderengine/tests/Android.bp +++ b/libs/renderengine/tests/Android.bp @@ -44,6 +44,7 @@ cc_test { "librenderengine_mocks", "libshaders", "libtonemap", + "libsurfaceflinger_common", ], header_libs: [ "libtonemap_headers", @@ -61,5 +62,6 @@ cc_test { "libsync", "libui", "libutils", + "server_configurable_flags", ], } diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp index d992aa5105..cc92bc30a0 100644 --- a/libs/sensor/Android.bp +++ b/libs/sensor/Android.bp @@ -21,6 +21,18 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "libsensor_flags", + package: "com.android.hardware.libsensor.flags", + srcs: ["libsensor_flags.aconfig"], +} + +cc_aconfig_library { + name: "libsensor_flags_c_lib", + host_supported: true, + aconfig_declarations: "libsensor_flags", +} + cc_library { name: "libsensor", @@ -52,6 +64,10 @@ cc_library { "android.companion.virtual.virtualdevice_aidl-cpp", ], + static_libs: [ + "libsensor_flags_c_lib", + ], + export_include_dirs: ["include"], export_shared_lib_headers: [ diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp index b82a79f501..9411e204e9 100644 --- a/libs/sensor/SensorManager.cpp +++ b/libs/sensor/SensorManager.cpp @@ -37,6 +37,8 @@ #include <sensor/Sensor.h> #include <sensor/SensorEventQueue.h> +#include <com_android_hardware_libsensor_flags.h> + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- @@ -192,6 +194,9 @@ void SensorManager::sensorManagerDied() { } status_t SensorManager::assertStateLocked() { +#if COM_ANDROID_HARDWARE_LIBSENSOR_FLAGS(SENSORMANAGER_PING_BINDER) + if (mSensorServer == nullptr) { +#else bool initSensorManager = false; if (mSensorServer == nullptr) { initSensorManager = true; @@ -203,6 +208,7 @@ status_t SensorManager::assertStateLocked() { } } if (initSensorManager) { +#endif waitForSensorService(&mSensorServer); LOG_ALWAYS_FATAL_IF(mSensorServer == nullptr, "getService(SensorService) NULL"); diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig new file mode 100644 index 0000000000..ef4d7370b5 --- /dev/null +++ b/libs/sensor/libsensor_flags.aconfig @@ -0,0 +1,9 @@ +package: "com.android.hardware.libsensor.flags" + +flag { + name: "sensormanager_ping_binder" + namespace: "sensors" + description: "Whether to pingBinder on SensorManager init" + bug: "322228259" + is_fixed_read_only: true +}
\ No newline at end of file diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp index 00514c4417..1e7e70775a 100644 --- a/libs/sensorprivacy/Android.bp +++ b/libs/sensorprivacy/Android.bp @@ -57,6 +57,7 @@ cc_library_shared { filegroup { name: "libsensorprivacy_aidl", srcs: [ + "aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl", "aidl/android/hardware/ISensorPrivacyListener.aidl", "aidl/android/hardware/ISensorPrivacyManager.aidl", ], diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp index 57c74ee565..fe9378616d 100644 --- a/libs/sensorprivacy/SensorPrivacyManager.cpp +++ b/libs/sensorprivacy/SensorPrivacyManager.cpp @@ -108,7 +108,7 @@ bool SensorPrivacyManager::isSensorPrivacyEnabled() bool SensorPrivacyManager::isToggleSensorPrivacyEnabled(int sensor) { - sp<hardware::ISensorPrivacyManager> service = getService(); + sp<hardware::ISensorPrivacyManager> service = getService(); if (service != nullptr) { bool result; service->isCombinedToggleSensorPrivacyEnabled(sensor, &result); @@ -143,6 +143,39 @@ status_t SensorPrivacyManager::isToggleSensorPrivacyEnabled(int toggleType, int return UNKNOWN_ERROR; } +int SensorPrivacyManager::getToggleSensorPrivacyState(int toggleType, int sensor) +{ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + int result; + service->getToggleSensorPrivacyState(toggleType, sensor, &result); + return result; + } + // if the SensorPrivacyManager is not available then assume sensor privacy is disabled + return DISABLED; +} + +std::vector<hardware::CameraPrivacyAllowlistEntry> + SensorPrivacyManager::getCameraPrivacyAllowlist(){ + sp<hardware::ISensorPrivacyManager> service = getService(); + std::vector<hardware::CameraPrivacyAllowlistEntry> result; + if (service != nullptr) { + service->getCameraPrivacyAllowlist(&result); + return result; + } + return result; +} + +bool SensorPrivacyManager::isCameraPrivacyEnabled(String16 packageName){ + sp<hardware::ISensorPrivacyManager> service = getService(); + if (service != nullptr) { + bool result; + service->isCameraPrivacyEnabled(packageName, &result); + return result; + } + return false; +} + status_t SensorPrivacyManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) { sp<hardware::ISensorPrivacyManager> service = getService(); diff --git a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl new file mode 100644 index 0000000000..03e153704b --- /dev/null +++ b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2024, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware; + +parcelable CameraPrivacyAllowlistEntry { + String packageName; + boolean isMandatory; +} diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl index eccd54c3eb..694af00a87 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyListener.aidl @@ -21,4 +21,5 @@ package android.hardware; */ oneway interface ISensorPrivacyListener { void onSensorPrivacyChanged(int toggleType, int sensor, boolean enabled); + void onSensorPrivacyStateChanged(int toggleType, int sensor, int state); } diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl index 49a1e1ea05..b6bd39e557 100644 --- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl +++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl @@ -16,6 +16,7 @@ package android.hardware; +import android.hardware.CameraPrivacyAllowlistEntry; import android.hardware.ISensorPrivacyListener; /** @hide */ @@ -41,4 +42,15 @@ interface ISensorPrivacyManager { void setToggleSensorPrivacy(int userId, int source, int sensor, boolean enable); void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable); + + List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist(); + + int getToggleSensorPrivacyState(int toggleType, int sensor); + + void setToggleSensorPrivacyState(int userId, int source, int sensor, int state); + + void setToggleSensorPrivacyStateForProfileGroup(int userId, int source, int sensor, int state); + + boolean isCameraPrivacyEnabled(String packageName); + } diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h index fc5fdf7900..9e97e166be 100644 --- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h +++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h @@ -32,12 +32,22 @@ class SensorPrivacyManager public: enum { TOGGLE_SENSOR_MICROPHONE = 1, - TOGGLE_SENSOR_CAMERA = 2 + TOGGLE_SENSOR_CAMERA = 2, + TOGGLE_SENSOR_UNKNOWN = -1 }; enum { TOGGLE_TYPE_SOFTWARE = 1, - TOGGLE_TYPE_HARDWARE = 2 + TOGGLE_TYPE_HARDWARE = 2, + TOGGLE_TYPE_UNKNOWN = -1 + }; + + enum { + ENABLED = 1, + DISABLED = 2, + AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = 3, + AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = 4, + AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = 5 }; SensorPrivacyManager(); @@ -51,6 +61,9 @@ public: bool isToggleSensorPrivacyEnabled(int sensor); bool isToggleSensorPrivacyEnabled(int toggleType, int sensor); status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result); + int getToggleSensorPrivacyState(int toggleType, int sensor); + std::vector<hardware::CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist(); + bool isCameraPrivacyEnabled(String16 packageName); status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient); status_t unlinkToDeath(const sp<IBinder::DeathRecipient>& recipient); diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index 16ed82af7c..a45ffe1a19 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -21,6 +21,7 @@ #include <cctype> #include <numeric> #include <optional> +#include <span> #include <log/log.h> @@ -81,7 +82,7 @@ uint64_t hash64Len0To16(const char* s, uint64_t len) { return k2; } -using byte_view = std::basic_string_view<uint8_t>; +using byte_view = std::span<const uint8_t>; constexpr size_t kEdidBlockSize = 128; constexpr size_t kEdidHeaderLength = 5; @@ -89,7 +90,8 @@ constexpr size_t kEdidHeaderLength = 5; constexpr uint16_t kVirtualEdidManufacturerId = 0xffffu; std::optional<uint8_t> getEdidDescriptorType(const byte_view& view) { - if (view.size() < kEdidHeaderLength || view[0] || view[1] || view[2] || view[4]) { + if (static_cast<size_t>(view.size()) < kEdidHeaderLength || view[0] || view[1] || view[2] || + view[4]) { return {}; } @@ -164,7 +166,7 @@ Cea861ExtensionBlock parseCea861Block(const byte_view& block) { constexpr size_t kDataBlockHeaderSize = 1; const size_t dataBlockSize = bodyLength + kDataBlockHeaderSize; - if (block.size() < dataBlockOffset + dataBlockSize) { + if (static_cast<size_t>(block.size()) < dataBlockOffset + dataBlockSize) { ALOGW("Invalid EDID: CEA 861 data block is truncated."); break; } @@ -264,7 +266,7 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { } byte_view view(edid.data(), edid.size()); - view.remove_prefix(kDescriptorOffset); + view = view.subspan(kDescriptorOffset); std::string_view displayName; std::string_view serialNumber; @@ -274,13 +276,13 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { constexpr size_t kDescriptorLength = 18; for (size_t i = 0; i < kDescriptorCount; i++) { - if (view.size() < kDescriptorLength) { + if (static_cast<size_t>(view.size()) < kDescriptorLength) { break; } if (const auto type = getEdidDescriptorType(view)) { byte_view descriptor(view.data(), kDescriptorLength); - descriptor.remove_prefix(kEdidHeaderLength); + descriptor = descriptor.subspan(kEdidHeaderLength); switch (*type) { case 0xfc: @@ -295,7 +297,7 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { } } - view.remove_prefix(kDescriptorLength); + view = view.subspan(kDescriptorLength); } std::string_view modelString = displayName; @@ -327,8 +329,8 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { const size_t numExtensions = edid[kNumExtensionsOffset]; view = byte_view(edid.data(), edid.size()); for (size_t blockNumber = 1; blockNumber <= numExtensions; blockNumber++) { - view.remove_prefix(kEdidBlockSize); - if (view.size() < kEdidBlockSize) { + view = view.subspan(kEdidBlockSize); + if (static_cast<size_t>(view.size()) < kEdidBlockSize) { ALOGW("Invalid EDID: block %zu is truncated.", blockNumber); break; } diff --git a/libs/ui/Gralloc5.cpp b/libs/ui/Gralloc5.cpp index b07e15534a..f217810b6a 100644 --- a/libs/ui/Gralloc5.cpp +++ b/libs/ui/Gralloc5.cpp @@ -89,10 +89,18 @@ static void *loadIMapperLibrary() { return nullptr; } - std::string lib_name = "mapper." + mapperSuffix + ".so"; - void *so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW); + void* so = nullptr; + // TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized + // TODO(b/302113279) use __ANDROID_VENDOR_API__ for vendor variant + if (__builtin_available(android __ANDROID_API_FUTURE__, *)) { + so = AServiceManager_openDeclaredPassthroughHal("mapper", mapperSuffix.c_str(), + RTLD_LOCAL | RTLD_NOW); + } else { + std::string lib_name = "mapper." + mapperSuffix + ".so"; + so = android_load_sphal_library(lib_name.c_str(), RTLD_LOCAL | RTLD_NOW); + } if (!so) { - ALOGE("Failed to load %s", lib_name.c_str()); + ALOGE("Failed to load mapper.%s.so", mapperSuffix.c_str()); } return so; }(); diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp index 8ce017d7a3..9a202150c5 100644 --- a/libs/ui/tests/Android.bp +++ b/libs/ui/tests/Android.bp @@ -54,6 +54,17 @@ cc_test { } cc_test { + name: "DisplayIdentification_test", + shared_libs: ["libui"], + static_libs: ["libgmock"], + srcs: ["DisplayIdentification_test.cpp"], + cflags: [ + "-Wall", + "-Werror", + ], +} + +cc_test { name: "FlattenableHelpers_test", shared_libs: ["libui"], srcs: ["FlattenableHelpers_test.cpp"], diff --git a/libs/vibrator/fuzzer/Android.bp b/libs/vibrator/fuzzer/Android.bp index cb063af2f6..faa77ca9df 100644 --- a/libs/vibrator/fuzzer/Android.bp +++ b/libs/vibrator/fuzzer/Android.bp @@ -18,6 +18,7 @@ */ package { + default_team: "trendy_team_haptics_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h index 2d0a4ea6ec..371ed89fc9 100644 --- a/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h +++ b/libs/vr/libpdx/private/pdx/rpc/string_wrapper.h @@ -17,12 +17,12 @@ namespace rpc { // C strings more efficient by avoiding unnecessary copies when remote method // signatures specify std::basic_string arguments or return values. template <typename CharT = std::string::value_type, - typename Traits = std::char_traits<CharT>> + typename Traits = std::char_traits<std::remove_cv_t<CharT>>> class StringWrapper { public: // Define types in the style of STL strings to support STL operators. typedef Traits traits_type; - typedef typename Traits::char_type value_type; + typedef CharT value_type; typedef std::size_t size_type; typedef value_type& reference; typedef const value_type& const_reference; diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index 7fd9c3a735..f73fa5d63c 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -22,8 +22,10 @@ mkdir out mkdir -p out/javax/microedition/khronos/opengles mkdir -p out/com/google/android/gles_jni +mkdir -p out/android/annotation mkdir -p out/android/app mkdir -p out/android/graphics +mkdir -p out/android/hardware mkdir -p out/android/view mkdir -p out/android/opengl mkdir -p out/android/content @@ -34,18 +36,20 @@ mkdir -p out/android/util echo "package android.graphics;" > out/android/graphics/Canvas.java echo "public interface Canvas {}" >> out/android/graphics/Canvas.java +echo "package android.annotation; public @interface NonNull {}" > out/android/annotation/NonNull.java echo "package android.app; import android.content.pm.IPackageManager; public class AppGlobals { public static IPackageManager getPackageManager() { return null;} }" > out/android/app/AppGlobals.java # echo "package android.content; import android.content.pm.PackageManager; public interface Context { public PackageManager getPackageManager(); }" > out/android/content/Context.java echo "package android.content.pm; public class ApplicationInfo {public int targetSdkVersion;}" > out/android/content/pm/ApplicationInfo.java echo "package android.content.pm; public interface IPackageManager {ApplicationInfo getApplicationInfo(java.lang.String packageName, int flags, java.lang.String userId) throws android.os.RemoteException;}" > out/android/content/pm/IPackageManager.java -echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 3;}; }" > out/android/os/Build.java +echo "package android.hardware; import android.os.ParcelFileDescriptor; public class SyncFence { public static SyncFence create(ParcelFileDescriptor w) { return null; } public static SyncFence createEmpty() { return null; } }" > out/android/hardware/SyncFence.java +echo "package android.os; public class Build {public static class VERSION_CODES { public static final int CUPCAKE = 0; public static final int R = 0; }; }" > out/android/os/Build.java +echo "package android.os; public class ParcelFileDescriptor { public static ParcelFileDescriptor adoptFd(int fd) { return null; } }" > out/android/os/ParcelFileDescriptor.java echo "package android.os; public class UserHandle {public static String myUserId() { return \"\"; } }" > out/android/os/UserHandle.java echo "package android.os; public class RemoteException extends Exception {}" > out/android/os/RemoteException.java -echo "package android.util; public class Log {public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java +echo "package android.util; public class Log {public static void d(String a, String b) {} public static void w(String a, String b) {} public static void e(String a, String b) {}}" > out/android/util/Log.java echo "package android.opengl; public abstract class EGLObjectHandle { public int getHandle() { return 0; } }" > out/android/opengl/EGLObjectHandle.java - echo "package android.graphics;" > out/android/graphics/SurfaceTexture.java echo "public interface SurfaceTexture {}" >> out/android/graphics/SurfaceTexture.java echo "package android.view;" > out/android/view/SurfaceView.java diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if index 951ecffc32..695b571a92 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if +++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if @@ -20,6 +20,7 @@ package android.opengl; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; +import android.os.Build; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp index b2ea041cd5..ea55179bbd 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp index 6dffac5945..8e452fb9cd 100644 --- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if index 523bc574be..75e17044c6 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if +++ b/opengl/tools/glgen/stubs/egl/EGLExtHeader.java-if @@ -18,6 +18,11 @@ package android.opengl; +import android.annotation.NonNull; +import android.hardware.SyncFence; +import android.os.ParcelFileDescriptor; +import android.util.Log; + /** * EGL Extensions */ @@ -30,8 +35,44 @@ public class EGLExt { public static final int EGL_OPENGL_ES3_BIT_KHR = 0x0040; public static final int EGL_RECORDABLE_ANDROID = 0x3142; + // EGL_ANDROID_native_fence_sync + public static final int EGL_SYNC_NATIVE_FENCE_ANDROID = 0x3144; + public static final int EGL_SYNC_NATIVE_FENCE_FD_ANDROID = 0x3145; + public static final int EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID = 0x3146; + public static final int EGL_NO_NATIVE_FENCE_FD_ANDROID = -1; + native private static void _nativeClassInit(); static { _nativeClassInit(); } + /** + * Retrieves the SyncFence for an EGLSync created with EGL_SYNC_NATIVE_FENCE_ANDROID + * + * See <a href="https://www.khronos.org/registry/EGL/extensions/ANDROID/EGL_ANDROID_native_fence_sync.txt"> + * EGL_ANDROID_native_fence_sync</a> extension for more details + * @param display The EGLDisplay connection + * @param sync The EGLSync to fetch the SyncFence from + * @return A SyncFence representing the native fence. + * * If <sync> is not a valid sync object for <display>, + * an {@link SyncFence#isValid() invalid} SyncFence is returned and an EGL_BAD_PARAMETER + * error is generated. + * * If the EGL_SYNC_NATIVE_FENCE_FD_ANDROID attribute of <sync> is + * EGL_NO_NATIVE_FENCE_FD_ANDROID, an {@link SyncFence#isValid() invalid} SyncFence is + * returned and an EGL_BAD_PARAMETER error is generated. + * * If <display> does not match the display passed to eglCreateSync + * when <sync> was created, the behaviour is undefined. + */ + public static @NonNull SyncFence eglDupNativeFenceFDANDROID(@NonNull EGLDisplay display, + @NonNull EGLSync sync) { + int fd = eglDupNativeFenceFDANDROIDImpl(display, sync); + Log.d("EGL", "eglDupNativeFence returned " + fd); + if (fd >= 0) { + return SyncFence.create(ParcelFileDescriptor.adoptFd(fd)); + } else { + return SyncFence.createEmpty(); + } + } + + private static native int eglDupNativeFenceFDANDROIDImpl(EGLDisplay display, EGLSync sync); + diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp index be8b3e3977..f1f0ac574b 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" @@ -37,25 +38,12 @@ #include <ui/ANativeObjectBase.h> static jclass egldisplayClass; -static jclass eglcontextClass; static jclass eglsurfaceClass; -static jclass eglconfigClass; +static jclass eglsyncClass; static jmethodID egldisplayGetHandleID; -static jmethodID eglcontextGetHandleID; static jmethodID eglsurfaceGetHandleID; -static jmethodID eglconfigGetHandleID; - -static jmethodID egldisplayConstructor; -static jmethodID eglcontextConstructor; -static jmethodID eglsurfaceConstructor; -static jmethodID eglconfigConstructor; - -static jobject eglNoContextObject; -static jobject eglNoDisplayObject; -static jobject eglNoSurfaceObject; - - +static jmethodID eglsyncGetHandleID; /* Cache method IDs each time the class is loaded. */ @@ -64,37 +52,14 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass) { jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal); - jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); - eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal); jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal); - jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); - eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal); + jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); + eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal); egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); - eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); - eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); - - - egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); - eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); - eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); - eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); - - - jclass eglClass = _env->FindClass("android/opengl/EGL14"); - jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); - jobject localeglNoContextObject = _env->GetStaticObjectField(eglClass, noContextFieldID); - eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); - - jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); - jobject localeglNoDisplayObject = _env->GetStaticObjectField(eglClass, noDisplayFieldID); - eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); - - jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); - jobject localeglNoSurfaceObject = _env->GetStaticObjectField(eglClass, noSurfaceFieldID); - eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); + eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); } static void * @@ -108,24 +73,12 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid)); } -static jobject -toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) { - if (cls == eglcontextClass && - (EGLContext)handle == EGL_NO_CONTEXT) { - return eglNoContextObject; - } - - if (cls == egldisplayClass && - (EGLDisplay)handle == EGL_NO_DISPLAY) { - return eglNoDisplayObject; - } - - if (cls == eglsurfaceClass && - (EGLSurface)handle == EGL_NO_SURFACE) { - return eglNoSurfaceObject; - } +// TODO: this should be generated from the .spec file, but needs to be renamed and made private +static jint android_eglDupNativeFenceFDANDROID(JNIEnv *env, jobject, jobject dpy, jobject sync) { + EGLDisplay dpy_native = (EGLDisplay)fromEGLHandle(env, egldisplayGetHandleID, dpy); + EGLSync sync_native = (EGLSync)fromEGLHandle(env, eglsyncGetHandleID, sync); - return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); + return eglDupNativeFenceFDANDROID(dpy_native, sync_native); } // -------------------------------------------------------------------------- diff --git a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java index 85f743d815..78b0819374 100755 --- a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java +++ b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java @@ -7,7 +7,7 @@ /** * {@hide} */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public static native EGLDisplay eglGetDisplay( long display_id ); diff --git a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES10ExtcHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES10cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtcHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp index dd17ca458a..1fa92758ad 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES11cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES/gl.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp index b2bbdf6fe9..4004a7dfcf 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES20cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES2/gl2.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp index b039bc9a83..c5bdf323fc 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES30cHeader.cpp @@ -18,6 +18,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES3/gl3.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp index dd00e9205c..2260a80bb4 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES31ExtcHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <GLES3/gl31.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp index 88e00bef6e..130612d7cc 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES31cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <stdint.h> diff --git a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp index 3e7ec8b2de..5446fc2fc3 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp +++ b/opengl/tools/glgen/stubs/gles11/GLES32cHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include <stdint.h> diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp index 9cab1d6a59..c3534bff31 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp +++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp @@ -17,6 +17,7 @@ // This source file is automatically generated #pragma GCC diagnostic ignored "-Wunused-variable" +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index a7955cff84..d244b1abc4 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -14,6 +14,7 @@ // Default flags to be used throughout all libraries in inputflinger. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" @@ -110,6 +111,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libperfetto_client_experimental", "libpalmrejection", "libui-types", ], diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 4863513eb5..ae066c0f4a 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -260,13 +260,16 @@ void InputManager::dump(std::string& dump) { } // Used by tests only. -binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { +binder::Status InputManager::createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); - const int uid = ipc->getCallingUid(); + const uid_t uid = ipc->getCallingUid(); if (uid != AID_SHELL && uid != AID_ROOT) { - ALOGE("Invalid attempt to register input channel over IPC" - "from non shell/root entity (PID: %d)", ipc->getCallingPid()); - return binder::Status::ok(); + LOG(ERROR) << __func__ << " can only be called by SHELL or ROOT users, " + << "but was called from UID " << uid; + return binder::Status:: + fromExceptionCode(EX_SECURITY, + "This uid is not allowed to call createInputChannel"); } base::Result<std::unique_ptr<InputChannel>> channel = mDispatcher->createInputChannel(name); @@ -274,7 +277,7 @@ binder::Status InputManager::createInputChannel(const std::string& name, InputCh return binder::Status::fromExceptionCode(exceptionCodeFromStatusT(channel.error().code()), channel.error().message().c_str()); } - (*channel)->copyTo(*outChannel); + InputChannel::moveChannel(std::move(*channel), *outChannel); return binder::Status::ok(); } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index df944ef807..c479aaf9b6 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -136,7 +136,8 @@ public: void dump(std::string& dump) override; status_t dump(int fd, const Vector<String16>& args) override; - binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; + binder::Status createInputChannel(const std::string& name, + android::os::InputChannelCore* outChannel) override; binder::Status removeInputChannel(const sp<IBinder>& connectionToken) override; binder::Status setFocusedWindow(const gui::FocusRequest&) override; diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp index e200f8b303..2d1257478d 100644 --- a/services/inputflinger/benchmarks/Android.bp +++ b/services/inputflinger/benchmarks/Android.bp @@ -1,4 +1,5 @@ package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index c7baceea70..6d71acc775 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" @@ -49,6 +50,7 @@ filegroup { "Monitor.cpp", "TouchedWindow.cpp", "TouchState.cpp", + "trace/*.cpp", ], } @@ -72,6 +74,7 @@ cc_defaults { static_libs: [ "libattestation", "libgui_window_info_static", + "libperfetto_client_experimental", ], target: { android: { diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp index f304712e8c..9dee66f0f8 100644 --- a/services/inputflinger/dispatcher/Connection.cpp +++ b/services/inputflinger/dispatcher/Connection.cpp @@ -20,22 +20,15 @@ namespace android::inputdispatcher { -Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, +Connection::Connection(std::unique_ptr<InputChannel> inputChannel, bool monitor, const IdGenerator& idGenerator) : status(Status::NORMAL), - inputChannel(inputChannel), monitor(monitor), - inputPublisher(inputChannel), + inputPublisher(std::move(inputChannel)), inputState(idGenerator) {} -const std::string Connection::getWindowName() const { - if (inputChannel != nullptr) { - return inputChannel->getName(); - } - if (monitor) { - return "monitor"; - } - return "?"; -} +sp<IBinder> Connection::getToken() const { + return inputPublisher.getChannel().getConnectionToken(); +}; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h index c17baea0e5..a834a8cf86 100644 --- a/services/inputflinger/dispatcher/Connection.h +++ b/services/inputflinger/dispatcher/Connection.h @@ -42,7 +42,6 @@ public: }; Status status; - std::shared_ptr<InputChannel> inputChannel; // never null bool monitor; InputPublisher inputPublisher; InputState inputState; @@ -59,12 +58,14 @@ public: // yet received a "finished" response from the application. std::deque<std::unique_ptr<DispatchEntry>> waitQueue; - Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor, + Connection(std::unique_ptr<InputChannel> inputChannel, bool monitor, const IdGenerator& idGenerator); - inline const std::string getInputChannelName() const { return inputChannel->getName(); } + inline const std::string getInputChannelName() const { + return inputPublisher.getChannel().getName(); + } - const std::string getWindowName() const; + sp<IBinder> getToken() const; }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 2153d8a3f5..264dc03e70 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -282,9 +282,10 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, const ui::Transform& rawTransform, - float globalScaleFactor) + float globalScaleFactor, gui::Uid targetUid, int64_t vsyncId, + std::optional<int32_t> windowId) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), @@ -292,7 +293,10 @@ DispatchEntry::DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, rawTransform(rawTransform), globalScaleFactor(globalScaleFactor), deliveryTime(0), - resolvedFlags(0) { + resolvedFlags(0), + targetUid(targetUid), + vsyncId(vsyncId), + windowId(windowId) { switch (this->eventEntry->type) { case EventEntry::Type::KEY: { const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*this->eventEntry); diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index a915805182..1298b5d511 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -17,7 +17,7 @@ #pragma once #include "InjectionState.h" -#include "InputTarget.h" +#include "InputTargetFlags.h" #include "trace/EventTrackerInterface.h" #include <gui/InputApplication.h> @@ -215,7 +215,7 @@ struct DispatchEntry { const uint32_t seq; // unique sequence number, never 0 std::shared_ptr<const EventEntry> eventEntry; // the event to dispatch - const ftl::Flags<InputTarget::Flags> targetFlags; + const ftl::Flags<InputTargetFlags> targetFlags; ui::Transform transform; ui::Transform rawTransform; float globalScaleFactor; @@ -227,17 +227,27 @@ struct DispatchEntry { int32_t resolvedFlags; + // Information about the dispatch window used for tracing. We avoid holding a window handle + // here because information in a window handle may be dynamically updated within the lifespan + // of this dispatch entry. + gui::Uid targetUid; + int64_t vsyncId; + // The window that this event is targeting. The only case when this windowId is not populated + // is when dispatching an event to a global monitor. + std::optional<int32_t> windowId; + DispatchEntry(std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> targetFlags, const ui::Transform& transform, - const ui::Transform& rawTransform, float globalScaleFactor); + ftl::Flags<InputTargetFlags> targetFlags, const ui::Transform& transform, + const ui::Transform& rawTransform, float globalScaleFactor, gui::Uid targetUid, + int64_t vsyncId, std::optional<int32_t> windowId); DispatchEntry(const DispatchEntry&) = delete; DispatchEntry& operator=(const DispatchEntry&) = delete; inline bool hasForegroundTarget() const { - return targetFlags.test(InputTarget::Flags::FOREGROUND); + return targetFlags.test(InputTargetFlags::FOREGROUND); } - inline bool isSplit() const { return targetFlags.test(InputTarget::Flags::SPLIT); } + inline bool isSplit() const { return targetFlags.test(InputTargetFlags::SPLIT); } private: static volatile int32_t sNextSeqAtomic; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index c349a5857f..67190060e7 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -35,6 +35,7 @@ #include <input/PrintTools.h> #include <input/TraceTools.h> #include <openssl/mem.h> +#include <private/android_filesystem_config.h> #include <unistd.h> #include <utils/Trace.h> @@ -51,6 +52,8 @@ #include "Connection.h" #include "DebugConfig.h" #include "InputDispatcher.h" +#include "trace/InputTracer.h" +#include "trace/InputTracingPerfettoBackend.h" #define INDENT " " #define INDENT2 " " @@ -75,6 +78,14 @@ namespace android::inputdispatcher { namespace { +// Input tracing is only available on debuggable builds (userdebug and eng) when the feature +// flag is enabled. When the flag is changed, tracing will only be available after reboot. +bool isInputTracingEnabled() { + static const std::string buildType = base::GetProperty("ro.build.type", "user"); + static const bool isUserdebugOrEng = buildType == "userdebug" || buildType == "eng"; + return input_flags::enable_input_event_tracing() && isUserdebugOrEng; +} + template <class Entry> void ensureEventTraced(const Entry& entry) { if (!entry.traceTracker) { @@ -110,11 +121,6 @@ constexpr nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2 // Log a warning when an interception call takes longer than this to process. constexpr std::chrono::milliseconds SLOW_INTERCEPTION_THRESHOLD = 50ms; -// Additional key latency in case a connection is still processing some motion events. -// This will help with the case when a user touched a button that opens a new window, -// and gives us the chance to dispatch the key to this new window. -constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = 500ms; - // Number of recent events to keep for debugging purposes. constexpr size_t RECENT_QUEUE_MAX_SIZE = 10; @@ -359,14 +365,22 @@ size_t firstMarkedBit(T set) { return i; } -std::unique_ptr<DispatchEntry> createDispatchEntry( - const InputTarget& inputTarget, std::shared_ptr<const EventEntry> eventEntry, - ftl::Flags<InputTarget::Flags> inputTargetFlags) { +std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget, + std::shared_ptr<const EventEntry> eventEntry, + ftl::Flags<InputTarget::Flags> inputTargetFlags, + int64_t vsyncId) { + const sp<WindowInfoHandle> win = inputTarget.windowHandle; + const std::optional<int32_t> windowId = + win ? std::make_optional(win->getInfo()->id) : std::nullopt; + // Assume the only targets that are not associated with a window are global monitors, and use + // the system UID for global monitors for tracing purposes. + const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM); if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, uid, vsyncId, + windowId); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); @@ -413,19 +427,10 @@ std::unique_ptr<DispatchEntry> createDispatchEntry( std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, firstPointerTransform, inputTarget.displayTransform, - inputTarget.globalScaleFactor); + inputTarget.globalScaleFactor, uid, vsyncId, windowId); return dispatchEntry; } -status_t openInputChannelPair(const std::string& name, std::shared_ptr<InputChannel>& serverChannel, - std::unique_ptr<InputChannel>& clientChannel) { - std::unique_ptr<InputChannel> uniqueServerChannel; - status_t result = InputChannel::openInputChannelPair(name, uniqueServerChannel, clientChannel); - - serverChannel = std::move(uniqueServerChannel); - return result; -} - template <typename T> bool sharedPointersEqual(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) { if (lhs == nullptr && rhs == nullptr) { @@ -709,7 +714,7 @@ void filterUntrustedTargets(TouchState& touchState, std::vector<InputTarget>& ta // TODO(b/282025641): simplify this code once InputTargets are being identified // separately from TouchedWindows. std::erase_if(targets, [&](const InputTarget& target) { - return target.inputChannel->getConnectionToken() == window.windowHandle->getToken(); + return target.connection->getToken() == window.windowHandle->getToken(); }); return true; } @@ -799,12 +804,28 @@ int32_t getUserActivityEventType(const EventEntry& eventEntry) { } } +std::pair<bool /*cancelPointers*/, bool /*cancelNonPointers*/> expandCancellationMode( + CancelationOptions::Mode mode) { + switch (mode) { + case CancelationOptions::Mode::CANCEL_ALL_EVENTS: + return {true, true}; + case CancelationOptions::Mode::CANCEL_POINTER_EVENTS: + return {true, false}; + case CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS: + return {false, true}; + case CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS: + return {false, true}; + } +} + } // namespace // --- InputDispatcher --- InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) - : InputDispatcher(policy, nullptr) {} + : InputDispatcher(policy, + isInputTracingEnabled() ? std::make_unique<trace::impl::PerfettoBackend>() + : nullptr) {} InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, std::unique_ptr<trace::InputTracingBackendInterface> traceBackend) @@ -833,7 +854,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mKeyRepeatState.lastKeyEntry = nullptr; if (traceBackend) { - // TODO: Create input tracer instance. + mTracer = std::make_unique<trace::impl::InputTracer>(std::move(traceBackend)); } mLastUserActivityTimes.fill(0); @@ -849,7 +870,7 @@ InputDispatcher::~InputDispatcher() { while (!mConnectionsByToken.empty()) { std::shared_ptr<Connection> connection = mConnectionsByToken.begin()->second; - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), /*notify=*/false); + removeInputChannelLocked(connection->getToken(), /*notify=*/false); } } @@ -969,7 +990,7 @@ nsecs_t InputDispatcher::processAnrsLocked() { } connection->responsive = false; // Stop waking up for this unresponsive connection - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); onAnrLocked(connection); return LLONG_MIN; } @@ -979,8 +1000,7 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( if (connection->monitor) { return mMonitorDispatchingTimeout; } - const sp<WindowInfoHandle> window = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + const sp<WindowInfoHandle> window = getWindowHandleLocked(connection->getToken()); if (window != nullptr) { return window->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); } @@ -1181,7 +1201,7 @@ bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) * Return true if the events preceding this incoming motion event should be dropped * Return false otherwise (the default behaviour) */ -bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) { +bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const { const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); @@ -1223,16 +1243,6 @@ bool InputDispatcher::shouldPruneInboundQueueLocked(const MotionEntry& motionEnt } } - // Prevent getting stuck: if we have a pending key event, and some motion events that have not - // yet been processed by some connections, the dispatcher will wait for these motion - // events to be processed before dispatching the key event. This is because these motion events - // may cause a new window to be launched, which the user might expect to receive focus. - // To prevent waiting forever for such events, just send the key to the currently focused window - if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { - ALOGD("Received a new pointer down event, stop waiting for events to process and " - "just send the pending key event to the focused window."); - mKeyIsWaitingForEventsTimeout = now(); - } return false; } @@ -1280,6 +1290,20 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE mNextUnblockedEvent = mInboundQueue.back(); needWake = true; } + + const bool isPointerDownEvent = motionEntry.action == AMOTION_EVENT_ACTION_DOWN && + isFromSource(motionEntry.source, AINPUT_SOURCE_CLASS_POINTER); + if (isPointerDownEvent && mKeyIsWaitingForEventsTimeout) { + // Prevent waiting too long for unprocessed events: if we have a pending key event, + // and some other events have not yet been processed, the dispatcher will wait for + // these events to be processed before dispatching the key event. This is because + // the unprocessed events may cause the focus to change (for example, by launching a + // new window or tapping a different window). To prevent waiting too long, we force + // the key to be sent to the currently focused window when a new tap comes in. + ALOGD("Received a new pointer down event, stop waiting for events to process and " + "just send the pending key event to the currently focused window."); + mKeyIsWaitingForEventsTimeout = now(); + } break; } case EventEntry::Type::FOCUS: { @@ -1602,15 +1626,15 @@ void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bo void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") + - channel->getName(); + connection->getInputChannelName(); std::string reason = std::string("reason=").append(entry->reason); android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS; dispatchEventLocked(currentTime, entry, {target}); @@ -1670,8 +1694,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( } } - auto channel = getInputChannelLocked(token); - if (channel == nullptr) { + auto connection = getConnectionLocked(token); + if (connection == nullptr) { // Window has gone away, clean up Pointer Capture state. mWindowTokenWithPointerCapture = nullptr; if (mCurrentPointerCaptureRequest.enable) { @@ -1680,7 +1704,7 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked( return; } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); @@ -1711,12 +1735,12 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked if (token == nullptr) { continue; } - std::shared_ptr<InputChannel> channel = getInputChannelLocked(token); - if (channel == nullptr) { - continue; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(token); + if (connection == nullptr) { + continue; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; inputTargets.push_back(target); } return inputTargets; @@ -1961,6 +1985,7 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS); CancelationOptions options(mode, "input event injection failed"); + options.displayId = entry->displayId; synthesizeCancelationEventsForMonitorsLocked(options); return true; } @@ -1993,12 +2018,12 @@ void InputDispatcher::enqueueDragEventLocked(const sp<WindowInfoHandle>& windowH void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) { - std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken); - if (channel == nullptr) { - return; // Window has gone away + std::shared_ptr<Connection> connection = getConnectionLocked(entry->connectionToken); + if (connection == nullptr) { + return; // Connection has gone away } InputTarget target; - target.inputChannel = channel; + target.connection = connection; entry->dispatchInProgress = true; dispatchEventLocked(currentTime, entry, {target}); } @@ -2051,17 +2076,8 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, pokeUserActivityLocked(*eventEntry); for (const InputTarget& inputTarget : inputTargets) { - std::shared_ptr<Connection> connection = - getConnectionLocked(inputTarget.inputChannel->getConnectionToken()); - if (connection != nullptr) { - prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); - } else { - if (DEBUG_DROPPED_EVENTS_VERBOSE) { - LOG(INFO) << "Dropping event delivery to target with channel " - << inputTarget.inputChannel->getName() - << " because it is no longer registered with the input dispatcher."; - } - } + std::shared_ptr<Connection> connection = inputTarget.connection; + prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } } @@ -2072,12 +2088,24 @@ void InputDispatcher::cancelEventsForAnrLocked(const std::shared_ptr<Connection> // sending new pointers to the connection when it blocked, but focused events will continue to // pile up. ALOGW("Canceling events for %s because it is unresponsive", - connection->inputChannel->getName().c_str()); - if (connection->status == Connection::Status::NORMAL) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, - "application not responding"); - synthesizeCancelationEventsForConnectionLocked(connection, options); + connection->getInputChannelName().c_str()); + if (connection->status != Connection::Status::NORMAL) { + return; } + CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, + "application not responding"); + + sp<WindowInfoHandle> windowHandle; + if (!connection->monitor) { + windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle == nullptr) { + // The window that is receiving this ANR was removed, so there is no need to generate + // cancellations, because the cancellations would have already been generated when + // the window was removed. + return; + } + } + synthesizeCancelationEventsForConnectionLocked(connection, options, windowHandle); } void InputDispatcher::resetNoFocusedWindowTimeoutLocked() { @@ -2134,7 +2162,8 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, // Start the timer // Wait to send key because there are unprocessed events that may cause focus to change mKeyIsWaitingForEventsTimeout = currentTime + - std::chrono::duration_cast<std::chrono::nanoseconds>(KEY_WAITING_FOR_EVENTS_TIMEOUT) + std::chrono::duration_cast<std::chrono::nanoseconds>( + mPolicy.getKeyWaitingForEventsTimeout()) .count(); return true; } @@ -2255,17 +2284,11 @@ std::vector<Monitor> InputDispatcher::selectResponsiveMonitorsLocked( const std::vector<Monitor>& monitors) const { std::vector<Monitor> responsiveMonitors; std::copy_if(monitors.begin(), monitors.end(), std::back_inserter(responsiveMonitors), - [this](const Monitor& monitor) REQUIRES(mLock) { - std::shared_ptr<Connection> connection = - getConnectionLocked(monitor.inputChannel->getConnectionToken()); - if (connection == nullptr) { - ALOGE("Could not find connection for monitor %s", - monitor.inputChannel->getName().c_str()); - return false; - } + [](const Monitor& monitor) REQUIRES(mLock) { + std::shared_ptr<Connection> connection = monitor.connection; if (!connection->responsive) { ALOGW("Unresponsive monitor %s will not get the new gesture", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return false; } return true; @@ -2642,7 +2665,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( for (InputTarget& target : targets) { if (target.dispatchMode == InputTarget::DispatchMode::OUTSIDE) { sp<WindowInfoHandle> targetWindow = - getWindowHandleLocked(target.inputChannel->getConnectionToken()); + getWindowHandleLocked(target.connection->getToken()); if (targetWindow->getInfo()->ownerUid != foregroundWindowUid) { target.flags |= InputTarget::Flags::ZERO_COORDS; } @@ -2840,13 +2863,13 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked( const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, std::optional<nsecs_t> firstDownTimeInTarget) const { - std::shared_ptr<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken()); - if (inputChannel == nullptr) { + std::shared_ptr<Connection> connection = getConnectionLocked(windowHandle->getToken()); + if (connection == nullptr) { ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str()); return {}; } InputTarget inputTarget; - inputTarget.inputChannel = inputChannel; + inputTarget.connection = connection; inputTarget.windowHandle = windowHandle; inputTarget.dispatchMode = dispatchMode; inputTarget.flags = targetFlags; @@ -2870,8 +2893,7 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); const WindowInfo* windowInfo = windowHandle->getInfo(); @@ -2911,8 +2933,7 @@ void InputDispatcher::addPointerWindowTargetLocked( std::vector<InputTarget>::iterator it = std::find_if(inputTargets.begin(), inputTargets.end(), [&windowHandle](const InputTarget& inputTarget) { - return inputTarget.inputChannel->getConnectionToken() == - windowHandle->getToken(); + return inputTarget.connection->getToken() == windowHandle->getToken(); }); // This is a hack, because the actual entry could potentially be an ACTION_DOWN event that @@ -2962,7 +2983,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) { InputTarget target; - target.inputChannel = monitor.inputChannel; + target.connection = monitor.connection; // target.firstDownTimeInTarget is not set for global monitors. It is only required in split // touch and global monitoring works as intended even without setting firstDownTimeInTarget if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { @@ -3333,10 +3354,17 @@ void InputDispatcher::enqueueDispatchEntryAndStartDispatchCycleLocked( void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connection>& connection, std::shared_ptr<const EventEntry> eventEntry, const InputTarget& inputTarget) { + const bool isKeyOrMotion = eventEntry->type == EventEntry::Type::KEY || + eventEntry->type == EventEntry::Type::MOTION; + if (isKeyOrMotion && !inputTarget.windowHandle && !connection->monitor) { + LOG(FATAL) << "All InputTargets for non-monitors must be associated with a window; target: " + << inputTarget << " connection: " << connection->getInputChannelName() + << " entry: " << eventEntry->getDescription(); + } // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. std::unique_ptr<DispatchEntry> dispatchEntry = - createDispatchEntry(inputTarget, eventEntry, inputTarget.flags); + createDispatchEntry(inputTarget, eventEntry, inputTarget.flags, mWindowInfosVsyncId); // Use the eventEntry from dispatchEntry since the entry may have changed and can now be a // different EventEntry than what was passed in. @@ -3455,7 +3483,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio << cancelEvent->getDescription(); std::unique_ptr<DispatchEntry> cancelDispatchEntry = createDispatchEntry(inputTarget, std::move(cancelEvent), - ftl::Flags<InputTarget::Flags>()); + ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId); // Send these cancel events to the queue before sending the event from the new // device. @@ -3476,7 +3504,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio } dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action, - inputTarget.inputChannel->getConnectionToken()); + inputTarget.connection->getToken()); break; } @@ -3559,13 +3587,9 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, continue; // Skip windows that receive ACTION_OUTSIDE } - sp<IBinder> token = target.inputChannel->getConnectionToken(); - std::shared_ptr<Connection> connection = getConnectionLocked(token); - if (connection == nullptr) { - continue; - } + sp<IBinder> token = target.connection->getToken(); newConnectionTokens.insert(std::move(token)); - newConnections.emplace_back(connection); + newConnections.emplace_back(target.connection); if (target.windowHandle) { interactionUids.emplace(target.windowHandle->getInfo()->ownerUid); } @@ -3585,7 +3609,7 @@ void InputDispatcher::processInteractionsLocked(const EventEntry& entry, std::string targetList; for (const std::shared_ptr<Connection>& connection : newConnections) { - targetList += connection->getWindowName() + ", "; + targetList += connection->getInputChannelName() + ", "; } std::string message = "Interaction with: " + targetList; if (targetList.empty()) { @@ -3799,7 +3823,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->outboundQueue.erase(connection->outboundQueue.begin()); traceOutboundQueueLength(*connection); if (connection->responsive) { - mAnrTracker.insert(timeoutTime, connection->inputChannel->getConnectionToken()); + mAnrTracker.insert(timeoutTime, connection->getToken()); } traceWaitQueueLength(*connection); } @@ -3889,7 +3913,7 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, auto command = [this, connection]() REQUIRES(mLock) { scoped_unlock unlock(mLock); - mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken()); + mPolicy.notifyInputChannelBroken(connection->getToken()); }; postCommandLocked(std::move(command)); } @@ -3947,10 +3971,9 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok if (shouldReportMetricsForConnection(*connection)) { const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result); - mLatencyTracker - .trackGraphicsLatency(timeline.inputEventId, - connection->inputChannel->getConnectionToken(), - std::move(timeline.graphicsTimeline)); + mLatencyTracker.trackGraphicsLatency(timeline.inputEventId, + connection->getToken(), + std::move(timeline.graphicsTimeline)); } } gotOne = true; @@ -3971,8 +3994,7 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } else { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn about them. - const bool stillHaveWindowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()) != nullptr; + const bool stillHaveWindowHandle = getWindowHandleLocked(connection->getToken()) != nullptr; notify = !connection->monitor && stillHaveWindowHandle; if (notify) { ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred. events=0x%x", @@ -3981,38 +4003,84 @@ int InputDispatcher::handleReceiveCallback(int events, sp<IBinder> connectionTok } // Remove the channel. - removeInputChannelLocked(connection->inputChannel->getConnectionToken(), notify); + removeInputChannelLocked(connection->getToken(), notify); return 0; // remove the callback } void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { - for (const auto& [token, connection] : mConnectionsByToken) { - synthesizeCancelationEventsForConnectionLocked(connection, options); + // Cancel windows (i.e. non-monitors). + // A channel must have at least one window to receive any input. If a window was removed, the + // event streams directed to the window will already have been canceled during window removal. + // So there is no need to generate cancellations for connections without any windows. + const auto [cancelPointers, cancelNonPointers] = expandCancellationMode(options.mode); + // Generate cancellations for touched windows first. This is to avoid generating cancellations + // through a non-touched window if there are more than one window for an input channel. + if (cancelPointers) { + for (const auto& [displayId, touchState] : mTouchStatesByDisplay) { + if (options.displayId.has_value() && options.displayId != displayId) { + continue; + } + for (const auto& touchedWindow : touchState.windows) { + synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); + } + } } + // Follow up by generating cancellations for all windows, because we don't explicitly track + // the windows that have an ongoing focus event stream. + if (cancelNonPointers) { + for (const auto& [_, handles] : mWindowHandlesByDisplay) { + for (const auto& windowHandle : handles) { + synthesizeCancelationEventsForWindowLocked(windowHandle, options); + } + } + } + + // Cancel monitors. + synthesizeCancelationEventsForMonitorsLocked(options); } void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( const CancelationOptions& options) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - synthesizeCancelationEventsForInputChannelLocked(monitor.inputChannel, options); + synthesizeCancelationEventsForConnectionLocked(monitor.connection, options, + /*window=*/nullptr); } } } -void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) { - std::shared_ptr<Connection> connection = getConnectionLocked(channel->getConnectionToken()); - if (connection == nullptr) { - return; +void InputDispatcher::synthesizeCancelationEventsForWindowLocked( + const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options, + const std::shared_ptr<Connection>& connection) { + if (windowHandle == nullptr) { + LOG(FATAL) << __func__ << ": Window handle must not be null"; + } + if (connection) { + // The connection can be optionally provided to avoid multiple lookups. + if (windowHandle->getToken() != connection->getToken()) { + LOG(FATAL) << __func__ + << ": Wrong connection provided for window: " << windowHandle->getName(); + } } - synthesizeCancelationEventsForConnectionLocked(connection, options); + std::shared_ptr<Connection> resolvedConnection = + connection ? connection : getConnectionLocked(windowHandle->getToken()); + if (!resolvedConnection) { + LOG(DEBUG) << __func__ << "No connection found for window: " << windowHandle->getName(); + return; + } + synthesizeCancelationEventsForConnectionLocked(resolvedConnection, options, windowHandle); } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( - const std::shared_ptr<Connection>& connection, const CancelationOptions& options) { + const std::shared_ptr<Connection>& connection, const CancelationOptions& options, + const sp<WindowInfoHandle>& window) { + if (!connection->monitor && window == nullptr) { + LOG(FATAL) << __func__ + << ": Cannot send event to non-monitor channel without a window - channel: " + << connection->getInputChannelName(); + } if (connection->status != Connection::Status::NORMAL) { return; } @@ -4039,8 +4107,8 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const bool wasEmpty = connection->outboundQueue.empty(); // The target to use if we don't find a window associated with the channel. - const InputTarget fallbackTarget{.inputChannel = connection->inputChannel}; - const auto& token = connection->inputChannel->getConnectionToken(); + const InputTarget fallbackTarget{.connection = connection}; + const auto& token = connection->getToken(); for (size_t i = 0; i < cancelationEvents.size(); i++) { std::unique_ptr<EventEntry> cancelationEventEntry = std::move(cancelationEvents[i]); @@ -4049,10 +4117,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( switch (cancelationEventEntry->type) { case EventEntry::Type::KEY: { const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry); - const std::optional<int32_t> targetDisplay = keyEntry.displayId != ADISPLAY_ID_NONE - ? std::make_optional(keyEntry.displayId) - : std::nullopt; - if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { + if (window) { addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS, /*targetFlags=*/{}, keyEntry.downTime, targets); } else { @@ -4063,11 +4128,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } case EventEntry::Type::MOTION: { const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry); - const std::optional<int32_t> targetDisplay = - motionEntry.displayId != ADISPLAY_ID_NONE - ? std::make_optional(motionEntry.displayId) - : std::nullopt; - if (const auto& window = getWindowHandleLocked(token, targetDisplay); window) { + if (window) { std::bitset<MAX_POINTER_ID + 1> pointerIds; for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) { @@ -4141,8 +4202,12 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( connection->getInputChannelName().c_str(), downEvents.size()); } - sp<WindowInfoHandle> windowHandle = - getWindowHandleLocked(connection->inputChannel->getConnectionToken()); + const auto [_, touchedWindowState, displayId] = + findTouchStateWindowAndDisplayLocked(connection->getToken()); + if (touchedWindowState == nullptr) { + LOG(FATAL) << __func__ << ": Touch state is out of sync: No touched window for token"; + } + const auto& windowHandle = touchedWindowState->windowHandle; const bool wasEmpty = connection->outboundQueue.empty(); for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { @@ -4160,8 +4225,8 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( targetFlags, pointerIds, motionEntry.downTime, targets); } else { - targets.emplace_back(InputTarget{.inputChannel = connection->inputChannel, - .flags = targetFlags}); + targets.emplace_back( + InputTarget{.connection = connection, .flags = targetFlags}); const auto it = mDisplayInfos.find(motionEntry.displayId); if (it != mDisplayInfos.end()) { targets.back().displayTransform = it->second.transform; @@ -4196,17 +4261,6 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } -void InputDispatcher::synthesizeCancelationEventsForWindowLocked( - const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { - if (windowHandle != nullptr) { - std::shared_ptr<Connection> wallpaperConnection = - getConnectionLocked(windowHandle->getToken()); - if (wallpaperConnection != nullptr) { - synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); - } - } -} - std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds, nsecs_t splitDownTime) { @@ -4900,7 +4954,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu break; } default: { - ALOGE("Cannot verify events of type %" PRId32, event.getType()); + LOG(ERROR) << "Cannot verify events of type " << ftl::enum_string(event.getType()); return nullptr; } } @@ -5118,15 +5172,6 @@ bool InputDispatcher::canWindowReceiveMotionLocked(const sp<WindowInfoHandle>& w return true; } -std::shared_ptr<InputChannel> InputDispatcher::getInputChannelLocked( - const sp<IBinder>& token) const { - auto connectionIt = mConnectionsByToken.find(token); - if (connectionIt == mConnectionsByToken.end()) { - return nullptr; - } - return connectionIt->second->inputChannel; -} - void InputDispatcher::updateWindowHandlesForDisplayLocked( const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) { if (windowInfoHandles.empty()) { @@ -5146,7 +5191,7 @@ void InputDispatcher::updateWindowHandlesForDisplayLocked( std::vector<sp<WindowInfoHandle>> newHandles; for (const sp<WindowInfoHandle>& handle : windowInfoHandles) { const WindowInfo* info = handle->getInfo(); - if (getInputChannelLocked(handle->getToken()) == nullptr) { + if (getConnectionLocked(handle->getToken()) == nullptr) { const bool noInputChannel = info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL); const bool canReceiveInput = @@ -5225,6 +5270,7 @@ void InputDispatcher::setInputWindowsLocked( // Copy old handles for release if they are no longer present. const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId); + const sp<WindowInfoHandle> removedFocusedWindowHandle = getFocusedWindowHandleLocked(displayId); updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId); @@ -5233,7 +5279,7 @@ void InputDispatcher::setInputWindowsLocked( std::optional<FocusResolver::FocusChanges> changes = mFocusResolver.setInputWindows(displayId, windowHandles); if (changes) { - onFocusChangedLocked(*changes); + onFocusChangedLocked(*changes, removedFocusedWindowHandle); } std::unordered_map<int32_t, TouchState>::iterator stateIt = @@ -5245,19 +5291,16 @@ void InputDispatcher::setInputWindowsLocked( if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) { LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName() << " in display %" << displayId; - std::shared_ptr<InputChannel> touchedInputChannel = - getInputChannelLocked(touchedWindow.windowHandle->getToken()); - if (touchedInputChannel != nullptr) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, - "touched window was removed"); - synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); - // Since we are about to drop the touch, cancel the events for the wallpaper as - // well. - if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && - touchedWindow.windowHandle->getInfo()->inputConfig.test( - gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { - sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); - synthesizeCancelationEventsForWindowLocked(wallpaper, options); + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, + "touched window was removed"); + synthesizeCancelationEventsForWindowLocked(touchedWindow.windowHandle, options); + // Since we are about to drop the touch, cancel the events for the wallpaper as + // well. + if (touchedWindow.targetFlags.test(InputTarget::Flags::FOREGROUND) && + touchedWindow.windowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { + if (const auto& ww = state.getWallpaperWindow(); ww) { + synthesizeCancelationEventsForWindowLocked(ww, options); } } state.windows.erase(state.windows.begin() + i); @@ -5355,15 +5398,16 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { sp<IBinder> oldFocusedWindowToken = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId); if (oldFocusedWindowToken != nullptr) { - std::shared_ptr<InputChannel> inputChannel = - getInputChannelLocked(oldFocusedWindowToken); - if (inputChannel != nullptr) { - CancelationOptions - options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "The display which contains this window no longer has focus."); - options.displayId = ADISPLAY_ID_NONE; - synthesizeCancelationEventsForInputChannelLocked(inputChannel, options); + const auto windowHandle = + getWindowHandleLocked(oldFocusedWindowToken, mFocusedDisplayId); + if (windowHandle == nullptr) { + LOG(FATAL) << __func__ << ": Previously focused token did not have a window"; } + CancelationOptions + options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + "The display which contains this window no longer has focus."); + options.displayId = ADISPLAY_ID_NONE; + synthesizeCancelationEventsForWindowLocked(windowHandle, options); } mFocusedDisplayId = displayId; @@ -5543,9 +5587,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } const int32_t deviceId = *deviceIds.begin(); - sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); - if (toWindowHandle == nullptr) { - ALOGW("Cannot transfer touch because to window not found."); + const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; + const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId); + if (!toWindowHandle) { + ALOGW("Cannot transfer touch because the transfer target window was not found."); return false; } @@ -5558,7 +5603,6 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. ftl::Flags<InputTarget::Flags> oldTargetFlags = touchedWindow->targetFlags; std::vector<PointerProperties> pointers = touchedWindow->getTouchingPointers(deviceId); - sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); // Add new window. @@ -5590,7 +5634,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< fromConnection->inputState.mergePointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, "transferring touch from this window to another window"); - synthesizeCancelationEventsForConnectionLocked(fromConnection, options); + synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection); synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection, newTargetFlags); @@ -5825,11 +5869,10 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { if (!mConnectionsByToken.empty()) { dump += INDENT "Connections:\n"; for (const auto& [token, connection] : mConnectionsByToken) { - dump += StringPrintf(INDENT2 "%i: channelName='%s', windowName='%s', " + dump += StringPrintf(INDENT2 "%i: channelName='%s', " "status=%s, monitor=%s, responsive=%s\n", - connection->inputChannel->getFd().get(), + connection->inputPublisher.getChannel().getFd(), connection->getInputChannelName().c_str(), - connection->getWindowName().c_str(), ftl::enum_string(connection->status).c_str(), toString(connection->monitor), toString(connection->responsive)); @@ -5883,8 +5926,8 @@ void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor> const size_t numMonitors = monitors.size(); for (size_t i = 0; i < numMonitors; i++) { const Monitor& monitor = monitors[i]; - const std::shared_ptr<InputChannel>& channel = monitor.inputChannel; - dump += StringPrintf(INDENT2 "%zu: '%s', ", i, channel->getName().c_str()); + const std::shared_ptr<Connection>& connection = monitor.connection; + dump += StringPrintf(INDENT2 "%zu: '%s', ", i, connection->getInputChannelName().c_str()); dump += "\n"; } } @@ -5914,20 +5957,20 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const { // acquire lock std::scoped_lock _l(mLock); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); std::shared_ptr<Connection> connection = std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/false, mIdGenerator); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.try_emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } // release lock @@ -5939,9 +5982,9 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputChannel(const Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId, const std::string& name, gui::Pid pid) { - std::shared_ptr<InputChannel> serverChannel; + std::unique_ptr<InputChannel> serverChannel; std::unique_ptr<InputChannel> clientChannel; - status_t result = openInputChannelPair(name, serverChannel, clientChannel); + status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel); if (result) { return base::Error(result) << "Failed to open input channel pair with name " << name; } @@ -5954,21 +5997,23 @@ Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_ << " without a specified display."; } - std::shared_ptr<Connection> connection = - std::make_shared<Connection>(serverChannel, /*monitor=*/true, mIdGenerator); const sp<IBinder>& token = serverChannel->getConnectionToken(); - auto&& fd = serverChannel->getFd(); + const int fd = serverChannel->getFd(); + std::shared_ptr<Connection> connection = + std::make_shared<Connection>(std::move(serverChannel), /*monitor=*/true, + mIdGenerator); - if (mConnectionsByToken.find(token) != mConnectionsByToken.end()) { + auto [_, inserted] = mConnectionsByToken.emplace(token, connection); + if (!inserted) { ALOGE("Created a new connection, but the token %p is already known", token.get()); } - mConnectionsByToken.emplace(token, connection); + std::function<int(int events)> callback = std::bind(&InputDispatcher::handleReceiveCallback, this, std::placeholders::_1, token); - mGlobalMonitorsByDisplay[displayId].emplace_back(serverChannel, pid); + mGlobalMonitorsByDisplay[displayId].emplace_back(connection, pid); - mLooper->addFd(fd.get(), 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, sp<LooperEventCallback>::make(callback), nullptr); } @@ -6007,7 +6052,7 @@ status_t InputDispatcher::removeInputChannelLocked(const sp<IBinder>& connection removeMonitorChannelLocked(connectionToken); } - mLooper->removeFd(connection->inputChannel->getFd().get()); + mLooper->removeFd(connection->inputPublisher.getChannel().getFd()); nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); @@ -6020,7 +6065,7 @@ void InputDispatcher::removeMonitorChannelLocked(const sp<IBinder>& connectionTo for (auto it = mGlobalMonitorsByDisplay.begin(); it != mGlobalMonitorsByDisplay.end();) { auto& [displayId, monitors] = *it; std::erase_if(monitors, [connectionToken](const Monitor& monitor) { - return monitor.inputChannel->getConnectionToken() == connectionToken; + return monitor.connection->getToken() == connectionToken; }); if (monitors.empty()) { @@ -6037,8 +6082,8 @@ status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) { } status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { - const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token); - if (!requestingChannel) { + const std::shared_ptr<Connection> requestingConnection = getConnectionLocked(token); + if (!requestingConnection) { LOG(WARNING) << "Attempted to pilfer pointers from an un-registered channel or invalid token"; return BAD_VALUE; @@ -6071,16 +6116,14 @@ status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) { std::string canceledWindows; for (const TouchedWindow& w : state.windows) { - const std::shared_ptr<InputChannel> channel = - getInputChannelLocked(w.windowHandle->getToken()); - if (channel != nullptr && channel->getConnectionToken() != token) { - synthesizeCancelationEventsForInputChannelLocked(channel, options); + if (w.windowHandle->getToken() != token) { + synthesizeCancelationEventsForWindowLocked(w.windowHandle, options); canceledWindows += canceledWindows.empty() ? "[" : ", "; - canceledWindows += channel->getName(); + canceledWindows += w.windowHandle->getName(); } } canceledWindows += canceledWindows.empty() ? "[]" : "]"; - LOG(INFO) << "Channel " << requestingChannel->getName() + LOG(INFO) << "Channel " << requestingConnection->getInputChannelName() << " is stealing input gesture for device " << deviceId << " from " << canceledWindows; @@ -6146,7 +6189,7 @@ void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, std::optional<gui::Pid> InputDispatcher::findMonitorPidByTokenLocked(const sp<IBinder>& token) { for (const auto& [_, monitors] : mGlobalMonitorsByDisplay) { for (const Monitor& monitor : monitors) { - if (monitor.inputChannel->getConnectionToken() == token) { + if (monitor.connection->getToken() == token) { return monitor.pid; } } @@ -6178,8 +6221,8 @@ std::string InputDispatcher::getConnectionNameLocked(const sp<IBinder>& connecti } void InputDispatcher::removeConnectionLocked(const std::shared_ptr<Connection>& connection) { - mAnrTracker.eraseToken(connection->inputChannel->getConnectionToken()); - mConnectionsByToken.erase(connection->inputChannel->getConnectionToken()); + mAnrTracker.eraseToken(connection->getToken()); + mConnectionsByToken.erase(connection->getToken()); } void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, @@ -6201,12 +6244,11 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, const nsecs_t eventDuration = finishTime - dispatchEntry.deliveryTime; if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { - ALOGI("%s spent %" PRId64 "ms processing %s", connection->getWindowName().c_str(), + ALOGI("%s spent %" PRId64 "ms processing %s", connection->getInputChannelName().c_str(), ns2ms(eventDuration), dispatchEntry.eventEntry->getDescription().c_str()); } if (shouldReportFinishedEvent(dispatchEntry, *connection)) { - mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, - connection->inputChannel->getConnectionToken(), + mLatencyTracker.trackFinishedEvent(dispatchEntry.eventEntry->id, connection->getToken(), dispatchEntry.deliveryTime, consumeTime, finishTime); } @@ -6227,7 +6269,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, std::unique_ptr<DispatchEntry> dispatchEntry = std::move(*entryIt); connection->waitQueue.erase(entryIt); - const sp<IBinder>& connectionToken = connection->inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection->getToken(); mAnrTracker.erase(dispatchEntry->timeoutTime, connectionToken); if (!connection->responsive) { connection->responsive = isConnectionResponsive(*connection); @@ -6238,9 +6280,18 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } traceWaitQueueLength(*connection); if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) { - const InputTarget target{.inputChannel = connection->inputChannel, - .flags = dispatchEntry->targetFlags}; - enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + // Only dispatch fallbacks if there is a window for the connection. + if (windowHandle != nullptr) { + const auto inputTarget = + createInputTargetLocked(windowHandle, InputTarget::DispatchMode::AS_IS, + dispatchEntry->targetFlags, + fallbackKeyEntry->downTime); + if (inputTarget.has_value()) { + enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), + *inputTarget); + } + } } releaseDispatchEntry(std::move(dispatchEntry)); } @@ -6274,7 +6325,7 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) // is already healthy again. Don't raise ANR in this situation if (connection->waitQueue.empty()) { ALOGI("Not raising ANR because the connection %s has recovered", - connection->inputChannel->getName().c_str()); + connection->getInputChannelName().c_str()); return; } /** @@ -6289,10 +6340,10 @@ void InputDispatcher::onAnrLocked(const std::shared_ptr<Connection>& connection) 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->getInputChannelName().c_str(), ns2ms(currentWait), oldestEntry.eventEntry->getDescription().c_str()); - sp<IBinder> connectionToken = connection->inputChannel->getConnectionToken(); + sp<IBinder> connectionToken = connection->getToken(); updateLastAnrStateLocked(getWindowHandleLocked(connectionToken), reason); processConnectionUnresponsiveLocked(*connection, std::move(reason)); @@ -6391,15 +6442,15 @@ void InputDispatcher::sendWindowResponsiveCommandLocked(const sp<IBinder>& token */ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& connection, std::string reason) { - const sp<IBinder>& connectionToken = connection.inputChannel->getConnectionToken(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { - ALOGW("Monitor %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Monitor %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); pid = findMonitorPidByTokenLocked(connectionToken); } else { // The connection is a window - ALOGW("Window %s is unresponsive: %s", connection.inputChannel->getName().c_str(), + ALOGW("Window %s is unresponsive: %s", connection.getInputChannelName().c_str(), reason.c_str()); const sp<WindowInfoHandle> handle = getWindowHandleLocked(connectionToken); if (handle != nullptr) { @@ -6413,7 +6464,7 @@ void InputDispatcher::processConnectionUnresponsiveLocked(const Connection& conn * 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(); + const sp<IBinder>& connectionToken = connection.getToken(); std::optional<gui::Pid> pid; if (connection.monitor) { pid = findMonitorPidByTokenLocked(connectionToken); @@ -6464,22 +6515,26 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); if (const auto unhandledKeyFallback = - mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); unhandledKeyFallback) { event = *unhandledKeyFallback; } mLock.lock(); - // Cancel the fallback key. + // Cancel the fallback key, but only if we still have a window for the channel. + // It could have been removed during the policy call. if (*fallbackKeyCode != AKEYCODE_UNKNOWN) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, - "application handled the original non-fallback key " - "or is no longer a foreground target, " - "canceling previously dispatched fallback key"); - options.keyCode = *fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle != nullptr) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, + "application handled the original non-fallback key " + "or is no longer a foreground target, " + "canceling previously dispatched fallback key"); + options.keyCode = *fallbackKeyCode; + synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); + } } connection->inputState.removeFallbackKey(originalKeyCode); } @@ -6509,8 +6564,8 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl mLock.unlock(); bool fallback = false; - if (auto fb = mPolicy.dispatchUnhandledKey(connection->inputChannel->getConnectionToken(), - event, keyEntry.policyFlags); + if (auto fb = mPolicy.dispatchUnhandledKey(connection->getToken(), event, + keyEntry.policyFlags); fb) { fallback = true; event = *fb; @@ -6555,10 +6610,13 @@ std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptabl } } - CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, - "canceling fallback, policy no longer desires it"); - options.keyCode = *fallbackKeyCode; - synthesizeCancelationEventsForConnectionLocked(connection, options); + const auto windowHandle = getWindowHandleLocked(connection->getToken()); + if (windowHandle != nullptr) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS, + "canceling fallback, policy no longer desires it"); + options.keyCode = *fallbackKeyCode; + synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection); + } fallback = false; *fallbackKeyCode = AKEYCODE_UNKNOWN; @@ -6618,7 +6676,8 @@ void InputDispatcher::traceInboundQueueLengthLocked() { void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "oq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "oq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.outboundQueue.size()); } } @@ -6626,7 +6685,8 @@ void InputDispatcher::traceOutboundQueueLength(const Connection& connection) { void InputDispatcher::traceWaitQueueLength(const Connection& connection) { if (ATRACE_ENABLED()) { char counterName[40]; - snprintf(counterName, sizeof(counterName), "wq:%s", connection.getWindowName().c_str()); + snprintf(counterName, sizeof(counterName), "wq:%s", + connection.getInputChannelName().c_str()); ATRACE_INT(counterName, connection.waitQueue.size()); } } @@ -6695,15 +6755,19 @@ void InputDispatcher::setFocusedWindow(const FocusRequest& request) { mLooper->wake(); } -void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) { +void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes, + const sp<WindowInfoHandle> removedFocusedWindowHandle) { if (changes.oldFocus) { - std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus); - if (focusedInputChannel) { - CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, - "focus left window"); - synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options); - enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); - } + const auto resolvedWindow = removedFocusedWindowHandle != nullptr + ? removedFocusedWindowHandle + : getWindowHandleLocked(changes.oldFocus, changes.displayId); + if (resolvedWindow == nullptr) { + LOG(FATAL) << __func__ << ": Previously focused token did not have a window"; + } + CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, + "focus left window"); + synthesizeCancelationEventsForWindowLocked(resolvedWindow, options); + enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason); } if (changes.newFocus) { resetNoFocusedWindowTimeoutLocked(); diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index e635852662..f2fd0cab71 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -369,8 +369,6 @@ private: REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getWindowHandleLocked( const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); - std::shared_ptr<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const - REQUIRES(mLock); sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const REQUIRES(mLock); bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window, @@ -472,7 +470,7 @@ private: bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); - bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); + bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) const REQUIRES(mLock); /** * Time to stop waiting for the events to be processed while trying to dispatch a key. @@ -617,21 +615,21 @@ private: REQUIRES(mLock); void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options) REQUIRES(mLock); - void synthesizeCancelationEventsForInputChannelLocked( - const std::shared_ptr<InputChannel>& channel, const CancelationOptions& options) + void synthesizeCancelationEventsForWindowLocked(const sp<gui::WindowInfoHandle>&, + const CancelationOptions&, + const std::shared_ptr<Connection>& = nullptr) REQUIRES(mLock); + // This is a convenience function used to generate cancellation for a connection without having + // to check whether it's a monitor or a window. For non-monitors, the window handle must not be + // null. Always prefer the "-ForWindow" method above when explicitly dealing with windows. void synthesizeCancelationEventsForConnectionLocked( - const std::shared_ptr<Connection>& connection, const CancelationOptions& options) - REQUIRES(mLock); + const std::shared_ptr<Connection>& connection, const CancelationOptions& options, + const sp<gui::WindowInfoHandle>& window) REQUIRES(mLock); void synthesizePointerDownEventsForConnectionLocked( const nsecs_t downTime, const std::shared_ptr<Connection>& connection, ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock); - void synthesizeCancelationEventsForWindowLocked( - const sp<android::gui::WindowInfoHandle>& windowHandle, - const CancelationOptions& options) REQUIRES(mLock); - // Splitting motion events across windows. When splitting motion event for a target, // splitDownTime refers to the time of first 'down' event on that particular target std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, @@ -658,7 +656,9 @@ private: bool handled, nsecs_t consumeTime) REQUIRES(mLock); void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken, const KeyEntry& entry) REQUIRES(mLock); - void onFocusChangedLocked(const FocusResolver::FocusChanges& changes) REQUIRES(mLock); + void onFocusChangedLocked(const FocusResolver::FocusChanges& changes, + const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr) + REQUIRES(mLock); void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) REQUIRES(mLock); void sendDropWindowCommandLocked(const sp<IBinder>& token, float x, float y) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index c02c5d67a2..28e3c6d69a 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -83,9 +83,9 @@ std::string InputTarget::getPointerInfoString() const { } std::ostream& operator<<(std::ostream& out, const InputTarget& target) { - out << "{inputChannel="; - if (target.inputChannel != nullptr) { - out << target.inputChannel->getName(); + out << "{connection="; + if (target.connection != nullptr) { + out << target.connection->getInputChannelName(); } else { out << "<null>"; } diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index aef866baaf..5728bdf1e8 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -19,10 +19,11 @@ #include <ftl/flags.h> #include <gui/WindowInfo.h> #include <gui/constants.h> -#include <input/InputTransport.h> #include <ui/Transform.h> #include <utils/BitSet.h> #include <bitset> +#include "Connection.h" +#include "InputTargetFlags.h" namespace android::inputdispatcher { @@ -33,29 +34,7 @@ namespace android::inputdispatcher { * window area. */ struct InputTarget { - enum class Flags : uint32_t { - /* This flag indicates that the event is being delivered to a foreground application. */ - FOREGROUND = 1 << 0, - - /* This flag indicates that the MotionEvent falls within the area of the target - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ - WINDOW_IS_OBSCURED = 1 << 1, - - /* This flag indicates that a motion event is being split across multiple windows. */ - SPLIT = 1 << 2, - - /* This flag indicates that the pointer coordinates dispatched to the application - * will be zeroed out to avoid revealing information to an application. This is - * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing - * the same UID from watching all touches. */ - ZERO_COORDS = 1 << 3, - - /* This flag indicates that the target of a MotionEvent is partly or wholly - * obscured by another visible window above it. The motion event should be - * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ - WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, - }; + using Flags = InputTargetFlags; enum class DispatchMode { /* This flag indicates that the event should be sent as is. @@ -85,8 +64,8 @@ struct InputTarget { ftl_last = SLIPPERY_ENTER, }; - // The input channel to be targeted. - std::shared_ptr<InputChannel> inputChannel; + // The input connection to be targeted. + std::shared_ptr<Connection> connection; // Flags for the input target. ftl::Flags<Flags> flags; diff --git a/services/inputflinger/dispatcher/InputTargetFlags.h b/services/inputflinger/dispatcher/InputTargetFlags.h new file mode 100644 index 0000000000..7497543bd4 --- /dev/null +++ b/services/inputflinger/dispatcher/InputTargetFlags.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <ftl/flags.h> + +namespace android::inputdispatcher { + +enum class InputTargetFlags : uint32_t { + /* This flag indicates that the event is being delivered to a foreground application. */ + FOREGROUND = 1 << 0, + + /* This flag indicates that the MotionEvent falls within the area of the target + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. */ + WINDOW_IS_OBSCURED = 1 << 1, + + /* This flag indicates that a motion event is being split across multiple windows. */ + SPLIT = 1 << 2, + + /* This flag indicates that the pointer coordinates dispatched to the application + * will be zeroed out to avoid revealing information to an application. This is + * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing + * the same UID from watching all touches. */ + ZERO_COORDS = 1 << 3, + + /* This flag indicates that the target of a MotionEvent is partly or wholly + * obscured by another visible window above it. The motion event should be + * delivered with flag AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED. */ + WINDOW_IS_PARTIALLY_OBSCURED = 1 << 14, +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.cpp b/services/inputflinger/dispatcher/Monitor.cpp index 204791eb03..4e77d905aa 100644 --- a/services/inputflinger/dispatcher/Monitor.cpp +++ b/services/inputflinger/dispatcher/Monitor.cpp @@ -19,7 +19,7 @@ namespace android::inputdispatcher { // --- Monitor --- -Monitor::Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid) - : inputChannel(inputChannel), pid(pid) {} +Monitor::Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid) + : connection(connection), pid(pid) {} } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/Monitor.h b/services/inputflinger/dispatcher/Monitor.h index 1b1eb3a593..d15a222752 100644 --- a/services/inputflinger/dispatcher/Monitor.h +++ b/services/inputflinger/dispatcher/Monitor.h @@ -17,16 +17,16 @@ #pragma once #include <gui/PidUid.h> -#include <input/InputTransport.h> +#include "Connection.h" namespace android::inputdispatcher { struct Monitor { - std::shared_ptr<InputChannel> inputChannel; // never null + std::shared_ptr<Connection> connection; // never null gui::Pid pid; - explicit Monitor(const std::shared_ptr<InputChannel>& inputChannel, gui::Pid pid); + explicit Monitor(const std::shared_ptr<Connection>& connection, gui::Pid pid); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index 9e6209b3d9..62c2b02967 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -131,6 +131,18 @@ public: return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT; } + /** + * Get the additional latency to add while waiting for other input events to process before + * dispatching the pending key. + * If there are unprocessed events, the pending key will not be dispatched immediately. Instead, + * the dispatcher will wait for this timeout, to account for the possibility that the focus + * might change due to touch or other events (such as another app getting launched by keys). + * This would give the pending key the opportunity to go to a newly focused window instead. + */ + virtual std::chrono::nanoseconds getKeyWaitingForEventsTimeout() { + return KEY_WAITING_FOR_EVENTS_TIMEOUT; + } + /* Notifies the policy that a pointer down event has occurred outside the current focused * window. * @@ -150,6 +162,13 @@ public: /* Notifies the policy that there was an input device interaction with apps. */ virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp, const std::set<gui::Uid>& uids) = 0; + +private: + // Additional key latency in case a connection is still processing some motion events. + // This will help with the case when a user touched a button that opens a new window, + // and gives us the chance to dispatch the key to this new window. + static constexpr std::chrono::nanoseconds KEY_WAITING_FOR_EVENTS_TIMEOUT = + std::chrono::milliseconds(500); }; } // namespace android diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp new file mode 100644 index 0000000000..a61fa85d6e --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -0,0 +1,109 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace { + +void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_classification(static_cast<int32_t>(event.classification)); + outProto.set_cursor_position_x(event.xCursorPosition); + outProto.set_cursor_position_y(event.yCursorPosition); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); + + for (uint32_t i = 0; i < event.pointerProperties.size(); i++) { + auto* pointer = outProto.add_pointer(); + + const auto& props = event.pointerProperties[i]; + pointer->set_pointer_id(props.id); + pointer->set_tool_type(static_cast<int32_t>(props.toolType)); + + const auto& coords = event.pointerCoords[i]; + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const auto axis = bits.clearFirstMarkedBit(); + auto axisEntry = pointer->add_axis_value(); + axisEntry->set_axis(axis); + axisEntry->set_value(coords.values[axisIndex]); + } + } +} + +void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event, + proto::AndroidKeyEvent& outProto) { + outProto.set_event_id(event.id); + outProto.set_event_time_nanos(event.eventTime); + outProto.set_down_time_nanos(event.downTime); + outProto.set_source(event.source); + outProto.set_action(event.action); + outProto.set_device_id(event.deviceId); + outProto.set_display_id(event.displayId); + outProto.set_key_code(event.keyCode); + outProto.set_scan_code(event.scanCode); + outProto.set_meta_state(event.metaState); + outProto.set_repeat_count(event.repeatCount); + outProto.set_flags(event.flags); + outProto.set_policy_flags(event.policyFlags); +} + +void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( + const InputTracingBackendInterface::WindowDispatchArgs& args, + proto::AndroidWindowInputDispatchEvent& outProto) { + std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry); + outProto.set_vsync_id(args.vsyncId); + outProto.set_window_id(args.windowId); + outProto.set_resolved_flags(args.resolvedFlags); + + if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) { + for (size_t i = 0; i < motion->pointerProperties.size(); i++) { + auto* pointerProto = outProto.add_dispatched_pointer(); + pointerProto->set_pointer_id(motion->pointerProperties[i].id); + const auto rawXY = + MotionEvent::calculateTransformedXY(motion->source, args.rawTransform, + motion->pointerCoords[i].getXYValue()); + pointerProto->set_x_in_display(rawXY.x); + pointerProto->set_y_in_display(rawXY.y); + + const auto& coords = motion->pointerCoords[i]; + const auto coordsInWindow = + MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords); + auto bits = BitSet64(coords.bits); + for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { + const uint32_t axis = bits.clearFirstMarkedBit(); + const float axisValueInWindow = coordsInWindow.values[axisIndex]; + if (coords.values[axisIndex] != axisValueInWindow) { + auto* axisEntry = pointerProto->add_axis_value_in_window(); + axisEntry->set_axis(axis); + axisEntry->set_value(axisValueInWindow); + } + } + } + } +} + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h new file mode 100644 index 0000000000..8a46f1518b --- /dev/null +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h @@ -0,0 +1,39 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 <perfetto/trace/android/android_input_event.pbzero.h> + +#include "InputTracingBackendInterface.h" + +namespace proto = perfetto::protos::pbzero; + +namespace android::inputdispatcher::trace { + +/** + * Write traced events into Perfetto protos. + */ +class AndroidInputEventProtoConverter { +public: + static void toProtoMotionEvent(const TracedMotionEvent& event, + proto::AndroidMotionEvent& outProto); + static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto); + static void toProtoWindowDispatchEvent(const InputTracingBackendInterface::WindowDispatchArgs&, + proto::AndroidWindowInputDispatchEvent& outProto); +}; + +} // namespace android::inputdispatcher::trace diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp new file mode 100644 index 0000000000..8a855c2035 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp @@ -0,0 +1,224 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTracer" + +#include "InputTracer.h" + +#include <android-base/logging.h> +#include <utils/AndroidThreads.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +TracedEvent createTracedEvent(const MotionEntry& e) { + return TracedMotionEvent{e.id, + e.eventTime, + e.policyFlags, + e.deviceId, + e.source, + e.displayId, + e.action, + e.actionButton, + e.flags, + e.metaState, + e.buttonState, + e.classification, + e.edgeFlags, + e.xPrecision, + e.yPrecision, + e.xCursorPosition, + e.yCursorPosition, + e.downTime, + e.pointerProperties, + e.pointerCoords}; +} + +TracedEvent createTracedEvent(const KeyEntry& e) { + return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source, + e.displayId, e.action, e.keyCode, e.scanCode, e.metaState, + e.downTime, e.flags, e.repeatCount}; +} + +} // namespace + +// --- InputTracer --- + +InputTracer::InputTracer(std::unique_ptr<InputTracingBackendInterface> backend) + : mTracerThread(&InputTracer::threadLoop, this), mBackend(std::move(backend)) {} + +InputTracer::~InputTracer() { + { + std::scoped_lock lock(mLock); + mThreadExit = true; + } + mThreadWakeCondition.notify_all(); + mTracerThread.join(); +} + +std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) { + std::scoped_lock lock(mLock); + TracedEvent traced; + + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } + + return std::make_unique<EventTrackerImpl>(*this, std::move(traced)); +} + +void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie, + const InputTarget& target) { + std::scoped_lock lock(mLock); + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()"; + } + // TODO(b/210460522): Determine if the event is sensitive based on the target. +} + +void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) { + { + std::scoped_lock lock(mLock); + auto& cookieState = getState(cookie); + if (!cookieState) { + LOG(FATAL) << "Traced event was already logged. " + "eventProcessingComplete() was likely called more than once."; + } + mTraceQueue.emplace_back(std::move(*cookieState)); + cookieState.reset(); + } // release lock + + mThreadWakeCondition.notify_all(); +} + +void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry, + const EventTrackerInterface* cookie) { + { + std::scoped_lock lock(mLock); + const EventEntry& entry = *dispatchEntry.eventEntry; + + TracedEvent traced; + if (entry.type == EventEntry::Type::MOTION) { + const auto& motion = static_cast<const MotionEntry&>(entry); + traced = createTracedEvent(motion); + } else if (entry.type == EventEntry::Type::KEY) { + const auto& key = static_cast<const KeyEntry&>(entry); + traced = createTracedEvent(key); + } else { + LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type); + } + + if (!cookie) { + // This event was not tracked as an inbound event, so trace it now. + mTraceQueue.emplace_back(traced); + } + + // The vsyncId only has meaning if the event is targeting a window. + const int32_t windowId = dispatchEntry.windowId.value_or(0); + const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0; + + mDispatchTraceQueue.emplace_back(std::move(traced), dispatchEntry.deliveryTime, + dispatchEntry.resolvedFlags, dispatchEntry.targetUid, + vsyncId, windowId, dispatchEntry.transform, + dispatchEntry.rawTransform); + } // release lock + + mThreadWakeCondition.notify_all(); +} + +std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) { + return static_cast<const EventTrackerImpl&>(cookie).mLockedState; +} + +void InputTracer::threadLoop() { + androidSetThreadName("InputTracer"); + + std::vector<const EventState> eventsToTrace; + std::vector<const WindowDispatchArgs> dispatchEventsToTrace; + + while (true) { + { // acquire lock + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + // Wait until we need to process more events or exit. + mThreadWakeCondition.wait(lock, [&]() REQUIRES(mLock) { + return mThreadExit || !mTraceQueue.empty() || !mDispatchTraceQueue.empty(); + }); + if (mThreadExit) { + return; + } + + mTraceQueue.swap(eventsToTrace); + mDispatchTraceQueue.swap(dispatchEventsToTrace); + } // release lock + + // Trace the events into the backend without holding the lock to reduce the amount of + // work performed in the critical section. + writeEventsToBackend(eventsToTrace, dispatchEventsToTrace); + eventsToTrace.clear(); + dispatchEventsToTrace.clear(); + } +} + +void InputTracer::writeEventsToBackend( + const std::vector<const EventState>& events, + const std::vector<const WindowDispatchArgs>& dispatchEvents) { + for (const auto& event : events) { + if (auto* motion = std::get_if<TracedMotionEvent>(&event.event); motion != nullptr) { + mBackend->traceMotionEvent(*motion); + } else { + mBackend->traceKeyEvent(std::get<TracedKeyEvent>(event.event)); + } + } + + for (const auto& dispatchArgs : dispatchEvents) { + mBackend->traceWindowDispatch(dispatchArgs); + } +} + +// --- InputTracer::EventTrackerImpl --- + +InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event) + : mTracer(tracer), mLockedState(event) {} + +InputTracer::EventTrackerImpl::~EventTrackerImpl() { + { + std::scoped_lock lock(mTracer.mLock); + if (!mLockedState) { + // This event has already been written to the trace as expected. + return; + } + // We're still holding on to the state, which means it hasn't yet been written to the trace. + // Write it to the trace now. + // TODO(b/210460522): Determine why/where the event is being destroyed before + // eventProcessingComplete() is called. + mTracer.mTraceQueue.emplace_back(std::move(*mLockedState)); + mLockedState.reset(); + } // release lock + + mTracer.mThreadWakeCondition.notify_all(); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h new file mode 100644 index 0000000000..9fe395d397 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracer.h @@ -0,0 +1,99 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "InputTracerInterface.h" + +#include <android-base/thread_annotations.h> +#include <gui/WindowInfo.h> + +#include <memory> +#include <mutex> +#include <thread> +#include <unordered_set> +#include <vector> + +#include "../Entry.h" +#include "InputTracingBackendInterface.h" + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracer implementation for InputDispatcher. + * + * InputTracer is thread-safe, so it can be called from any thread. Upon construction, InputTracer + * will start its own thread that it uses for write events into the tracing backend. That is the + * one and only thread that will interact with the tracing backend, since the Perfetto backend + * uses thread-local storage. + * + * See the documentation in InputTracerInterface for the API surface. + */ +class InputTracer : public InputTracerInterface { +public: + explicit InputTracer(std::unique_ptr<InputTracingBackendInterface>); + ~InputTracer() override; + InputTracer(const InputTracer&) = delete; + InputTracer& operator=(const InputTracer&) = delete; + + std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override; + void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override; + void eventProcessingComplete(const EventTrackerInterface&) override; + void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override; + +private: + std::mutex mLock; + std::thread mTracerThread; + bool mThreadExit GUARDED_BY(mLock){false}; + std::condition_variable mThreadWakeCondition; + std::unique_ptr<InputTracingBackendInterface> mBackend; + + // The state of a tracked event. + struct EventState { + const TracedEvent event; + // TODO(b/210460522): Add additional args for tracking event sensitivity and + // dispatch target UIDs. + }; + std::vector<const EventState> mTraceQueue GUARDED_BY(mLock); + using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs; + std::vector<const WindowDispatchArgs> mDispatchTraceQueue GUARDED_BY(mLock); + + // Provides thread-safe access to the state from an event tracker cookie. + std::optional<EventState>& getState(const EventTrackerInterface&) REQUIRES(mLock); + + // Implementation of the event tracker cookie. + class EventTrackerImpl : public EventTrackerInterface { + public: + explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry); + virtual ~EventTrackerImpl() override; + + private: + InputTracer& mTracer; + // This event tracker cookie will only hold the state as long as it has not been written + // to the trace. The state is released when the event is written to the trace. + mutable std::optional<EventState> mLockedState; + + // Only allow InputTracer access to the locked state through getTrackerState() to ensure + // that the InputTracer lock is held when this is accessed. + friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&); + }; + + void threadLoop(); + void writeEventsToBackend(const std::vector<const EventState>& events, + const std::vector<const WindowDispatchArgs>& dispatchEvents); +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h index b430a5bb28..bc47817817 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h +++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h @@ -16,11 +16,65 @@ #pragma once -#include "../Entry.h" +#include <gui/PidUid.h> +#include <input/Input.h> +#include <ui/Transform.h> + +#include <array> +#include <variant> +#include <vector> namespace android::inputdispatcher::trace { /** + * A representation of an Android KeyEvent used by the tracing backend. + */ +struct TracedKeyEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t keyCode; + int32_t scanCode; + int32_t metaState; + nsecs_t downTime; + int32_t flags; + int32_t repeatCount; +}; + +/** + * A representation of an Android MotionEvent used by the tracing backend. + */ +struct TracedMotionEvent { + int32_t id; + nsecs_t eventTime; + uint32_t policyFlags; + int32_t deviceId; + uint32_t source; + int32_t displayId; + int32_t action; + int32_t actionButton; + int32_t flags; + int32_t metaState; + int32_t buttonState; + MotionClassification classification; + int32_t edgeFlags; + float xPrecision; + float yPrecision; + float xCursorPosition; + float yCursorPosition; + nsecs_t downTime; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; +}; + +/** A representation of a traced input event. */ +using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>; + +/** * An interface for the tracing backend, used for setting a custom backend for testing. */ class InputTracingBackendInterface { @@ -28,14 +82,14 @@ public: virtual ~InputTracingBackendInterface() = default; /** Trace a KeyEvent. */ - virtual void traceKeyEvent(const KeyEntry&) const = 0; + virtual void traceKeyEvent(const TracedKeyEvent&) const = 0; /** Trace a MotionEvent. */ - virtual void traceMotionEvent(const MotionEntry&) const = 0; + virtual void traceMotionEvent(const TracedMotionEvent&) const = 0; /** Trace an event being sent to a window. */ struct WindowDispatchArgs { - std::variant<MotionEntry, KeyEntry> eventEntry; + TracedEvent eventEntry; nsecs_t deliveryTime; int32_t resolvedFlags; gui::Uid targetUid; diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp new file mode 100644 index 0000000000..4442ad8586 --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -0,0 +1,94 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputTracer" + +#include "InputTracingPerfettoBackend.h" + +#include "AndroidInputEventProtoConverter.h" + +#include <android-base/logging.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> + +namespace android::inputdispatcher::trace::impl { + +namespace { + +constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent"; + +} // namespace + +// --- PerfettoBackend::InputEventDataSource --- + +void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) { + LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; +} + +void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) { + LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); }); +} + +// --- PerfettoBackend --- + +std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{}; + +PerfettoBackend::PerfettoBackend() { + // Use a once-flag to ensure that the data source is only registered once per boot, since + // we never unregister the InputEventDataSource. + std::call_once(sDataSourceRegistrationFlag, []() { + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + perfetto::Tracing::Initialize(args); + + // Register our custom data source for input event tracing. + perfetto::DataSourceDescriptor dsd; + dsd.set_name(INPUT_EVENT_TRACE_DATA_SOURCE_NAME); + InputEventDataSource::Register(dsd); + LOG(INFO) << "InputTracer initialized for data source: " + << INPUT_EVENT_TRACE_DATA_SOURCE_NAME; + }); +} + +void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchMotion = inputEvent->set_dispatcher_motion_event(); + AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion); + }); +} + +void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEvent = tracePacket->set_android_input_event(); + auto* dispatchKey = inputEvent->set_dispatcher_key_event(); + AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey); + }); +} + +void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) const { + InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { + auto tracePacket = ctx.NewTracePacket(); + auto* inputEventProto = tracePacket->set_android_input_event(); + auto* dispatchEventProto = inputEventProto->set_dispatcher_window_dispatch_event(); + AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, + *dispatchEventProto); + }); +} + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h new file mode 100644 index 0000000000..2777cfe9fe --- /dev/null +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h @@ -0,0 +1,66 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "InputTracingBackendInterface.h" + +#include <perfetto/tracing.h> +#include <mutex> + +namespace android::inputdispatcher::trace::impl { + +/** + * The tracing backend that writes events into ongoing Perfetto traces. + * + * Example shell command to take an input trace from Perfetto: + * + * adb shell perfetto \ + * -c - --txt \ + * -o /data/misc/perfetto-traces/trace.input-trace \ + * <<END + * buffers: { + * size_kb: 5000 + * fill_policy: RING_BUFFER + * } + * data_sources: { + * config { + * name: "android.input.inputevent" + * } + * } + * END + */ +class PerfettoBackend : public InputTracingBackendInterface { +public: + PerfettoBackend(); + ~PerfettoBackend() override = default; + + void traceKeyEvent(const TracedKeyEvent&) const override; + void traceMotionEvent(const TracedMotionEvent&) const override; + void traceWindowDispatch(const WindowDispatchArgs&) const override; + + class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> { + public: + void OnSetup(const SetupArgs&) override {} + void OnStart(const StartArgs&) override; + void OnStop(const StopArgs&) override; + }; + +private: + static std::once_flag sDataSourceRegistrationFlag; +}; + +} // namespace android::inputdispatcher::trace::impl diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 9abfef14dc..79c8a4ba76 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -226,6 +226,9 @@ struct InputReaderConfiguration { // True to enable tap-to-click on touchpads. bool touchpadTapToClickEnabled; + // True to enable tap dragging on touchpads. + bool touchpadTapDraggingEnabled; + // True to enable a zone on the right-hand side of touchpads where clicks will be turned into // context (a.k.a. "right") clicks. bool touchpadRightClickZoneEnabled; @@ -266,6 +269,7 @@ struct InputReaderConfiguration { touchpadPointerSpeed(0), touchpadNaturalScrollingEnabled(true), touchpadTapToClickEnabled(true), + touchpadTapDraggingEnabled(false), touchpadRightClickZoneEnabled(false), stylusButtonMotionEventsEnabled(true), stylusPointerIconEnabled(false) {} diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp index f954370928..ba586d7a18 100644 --- a/services/inputflinger/reader/Android.bp +++ b/services/inputflinger/reader/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index a41064b361..f3f15df151 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -251,6 +251,7 @@ std::list<NotifyArgs> InputDevice::configureInternal(nsecs_t when, mAssociatedDeviceType = getValueByKey(readerConfig.deviceTypeAssociations, mIdentifier.location); mIsWaking = mConfiguration.getBool("device.wake").value_or(false); + mShouldSmoothScroll = mConfiguration.getBool("device.viewBehavior_smoothScroll"); } if (!changes.any() || changes.test(Change::DEVICE_ALIAS)) { @@ -401,7 +402,8 @@ std::list<NotifyArgs> InputDevice::updateExternalStylusState(const StylusState& InputDeviceInfo InputDevice::getDeviceInfo() { InputDeviceInfo outDeviceInfo; outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal, - mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE)); + mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE), + {mShouldSmoothScroll}); for_each_mapper( [&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); }); diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 0582649d38..9608210ca0 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -164,19 +164,6 @@ void InputReader::loopOnce() { std::swap(notifyArgs, mPendingArgs); } // release lock - // Send out a message that the describes the changed input devices. - if (inputDevicesChanged) { - mPolicy->notifyInputDevicesChanged(inputDevices); - } - - // Notify the policy of the start of every new stylus gesture outside the lock. - for (const auto& args : notifyArgs) { - const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args); - if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) { - mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime); - } - } - // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -187,6 +174,21 @@ void InputReader::loopOnce() { for (const NotifyArgs& args : notifyArgs) { mNextListener.notify(args); } + + // Notify the policy that input devices have changed. + // This must be done after flushing events down the listener chain to ensure that the rest of + // the listeners are synchronized with the changes before the policy reacts to them. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + + // Notify the policy of the start of every new stylus gesture. + for (const auto& args : notifyArgs) { + const auto* motionArgs = std::get_if<NotifyMotionArgs>(&args); + if (motionArgs != nullptr && isStylusPointerGestureStart(*motionArgs)) { + mPolicy->notifyStylusGestureStarted(motionArgs->deviceId, motionArgs->eventTime); + } + } } std::list<NotifyArgs> InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index ba7234b3e1..0719b0ce3c 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -199,6 +199,7 @@ private: std::optional<DisplayViewport> mAssociatedViewport; bool mHasMic; bool mDropUntilNextSync; + std::optional<bool> mShouldSmoothScroll; typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code); int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index d207ed1655..45f09ae32a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -23,6 +23,7 @@ #include <optional> #include <com_android_input_flags.h> +#include <ftl/enum.h> #include <input/AccelerationCurve.h> #include "CursorButtonAccumulator.h" @@ -136,7 +137,7 @@ void CursorInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str()); - dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); + dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(mOrientation).c_str()); dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime); @@ -164,8 +165,9 @@ std::list<NotifyArgs> CursorInputMapper::reconfigure(nsecs_t when, configureOnChangeDisplayInfo(readerConfig); } + // Pointer speed settings depend on display settings. if (!changes.any() || changes.test(InputReaderConfiguration::Change::POINTER_SPEED) || - configurePointerCapture) { + changes.test(InputReaderConfiguration::Change::DISPLAY_INFO) || configurePointerCapture) { configureOnChangePointerSpeed(readerConfig); } return out; @@ -515,7 +517,11 @@ void CursorInputMapper::configureOnChangePointerSpeed(const InputReaderConfigura mNewPointerVelocityControl.setCurve( createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed)); } else { - mOldPointerVelocityControl.setParameters(config.pointerVelocityControlParameters); + mOldPointerVelocityControl.setParameters( + (config.displaysWithMousePointerAccelerationDisabled.count( + mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0) + ? config.pointerVelocityControlParameters + : FLAT_VELOCITY_CONTROL_PARAMS); } mWheelXVelocityControl.setParameters(config.wheelVelocityControlParameters); mWheelYVelocityControl.setParameters(config.wheelVelocityControlParameters); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 9e7e956866..738517b67e 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -20,6 +20,7 @@ #include "KeyboardInputMapper.h" +#include <ftl/enum.h> #include <ui/Rotation.h> namespace android { @@ -143,7 +144,7 @@ void KeyboardInputMapper::dump(std::string& dump) { dump += INDENT2 "Keyboard Input Mapper:\n"; dumpParameters(dump); dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType); - dump += StringPrintf(INDENT3 "Orientation: %d\n", getOrientation()); + dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(getOrientation()).c_str()); dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size()); dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState); dump += INDENT3 "KeyboardLayoutInfo: "; diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index bdc164029c..b990dd5e79 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -397,6 +397,8 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, .setBoolValues({config.touchpadNaturalScrollingEnabled}); mPropertyProvider.getProperty("Tap Enable") .setBoolValues({config.touchpadTapToClickEnabled}); + mPropertyProvider.getProperty("Tap Drag Enable") + .setBoolValues({config.touchpadTapDraggingEnabled}); mPropertyProvider.getProperty("Button Right Click Zone Enable") .setBoolValues({config.touchpadRightClickZoneEnabled}); } diff --git a/services/inputflinger/reporter/Android.bp b/services/inputflinger/reporter/Android.bp index b1e1aee02a..e85a10428d 100644 --- a/services/inputflinger/reporter/Android.bp +++ b/services/inputflinger/reporter/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp index 9e6dbe432d..255c7eb679 100644 --- a/services/inputflinger/rust/Android.bp +++ b/services/inputflinger/rust/Android.bp @@ -13,6 +13,10 @@ // limitations under the License. // Generate the C++ code that Rust calls into. +package { + default_team: "trendy_team_input_framework", +} + genrule { name: "inputflinger_rs_bootstrap_bridge_code", tools: ["cxxbridge"], diff --git a/services/inputflinger/rust/lib.rs b/services/inputflinger/rust/lib.rs index da72134065..25dfb03cb8 100644 --- a/services/inputflinger/rust/lib.rs +++ b/services/inputflinger/rust/lib.rs @@ -70,7 +70,9 @@ mod ffi { /// incremented using `AIBinder_incStrong`. See `binder::unstable_api::new_spibinder`. unsafe fn create_inputflinger_rust(callback: *mut ffi::IInputFlingerRustBootstrapCallbackAIBinder) { logger::init( - logger::Config::default().with_tag_on_device(LOG_TAG).with_min_level(log::Level::Trace), + logger::Config::default() + .with_tag_on_device(LOG_TAG) + .with_max_level(log::LevelFilter::Trace), ); let callback = callback as *mut AIBinder; diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 55aa22685e..a26153ec5b 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" @@ -44,6 +45,7 @@ cc_test { "EventHub_test.cpp", "FakeEventHub.cpp", "FakeInputReaderPolicy.cpp", + "FakeInputTracingBackend.cpp", "FakePointerController.cpp", "FocusResolver_test.cpp", "GestureConverter_test.cpp", diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index 7b793d8e86..8c17221ead 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -105,7 +105,7 @@ DisplayViewport createSecondaryViewport() { class ViewportFakingInputDeviceContext : public InputDeviceContext { public: ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, - DisplayViewport viewport) + std::optional<DisplayViewport> viewport) : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {} ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, @@ -117,10 +117,12 @@ public: return mAssociatedViewport; } - void setViewport(const DisplayViewport& viewport) { mAssociatedViewport = viewport; } + void setViewport(const std::optional<DisplayViewport>& viewport) { + mAssociatedViewport = viewport; + } private: - DisplayViewport mAssociatedViewport; + std::optional<DisplayViewport> mAssociatedViewport; }; } // namespace @@ -1355,6 +1357,45 @@ TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationWithAsso args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>(AllOf(WithMotionAction(HOVER_MOVE), + WithDisplayId(DISPLAY_ID), + WithRelativeMotion(10, 20))))); +} + +TEST_F(CursorInputMapperUnitTestWithNewBallistics, ConfigureAccelerationOnDisplayChange) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0); + mReaderConfiguration.setDisplayViewports({primaryViewport}); + // Disable acceleration for the display. + mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID); + createDevice(); + + // Don't associate the device with the display yet. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, + /*viewport=*/std::nullopt); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + + // Verify that acceleration is being applied by default by checking that the movement is scaled. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + const auto& coords = get<NotifyMotionArgs>(args.back()).pointerCoords[0]; + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), 10.f); + ASSERT_GT(coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y), 20.f); + + // Now associate the device with the display, and verify that acceleration is disabled. + deviceContext.setViewport(primaryViewport); + args += mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + args.clear(); + + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(HOVER_MOVE), WithDisplayId(DISPLAY_ID), WithRelativeMotion(10, 20))))); diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 88f514f1d4..9e9371248e 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -41,15 +41,21 @@ void FakeInputReaderPolicy::assertInputDevicesNotChanged() { } void FakeInputReaderPolicy::assertStylusGestureNotified(int32_t deviceId) { - std::scoped_lock lock(mLock); - ASSERT_TRUE(mStylusGestureNotified); - ASSERT_EQ(deviceId, *mStylusGestureNotified); - mStylusGestureNotified.reset(); + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + const bool success = + mStylusGestureNotifiedCondition.wait_for(lock, WAIT_TIMEOUT, [this]() REQUIRES(mLock) { + return mDeviceIdOfNotifiedStylusGesture.has_value(); + }); + ASSERT_TRUE(success) << "Timed out waiting for stylus gesture to be notified"; + ASSERT_EQ(deviceId, *mDeviceIdOfNotifiedStylusGesture); + mDeviceIdOfNotifiedStylusGesture.reset(); } void FakeInputReaderPolicy::assertStylusGestureNotNotified() { std::scoped_lock lock(mLock); - ASSERT_FALSE(mStylusGestureNotified); + ASSERT_FALSE(mDeviceIdOfNotifiedStylusGesture); } void FakeInputReaderPolicy::clearViewports() { @@ -258,7 +264,8 @@ void FakeInputReaderPolicy::waitForInputDevices(std::function<void(bool)> proces void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) { std::scoped_lock lock(mLock); - mStylusGestureNotified = deviceId; + mDeviceIdOfNotifiedStylusGesture = deviceId; + mStylusGestureNotifiedCondition.notify_all(); } std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay( diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 4ef9c3ee4a..da5085db7c 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -102,9 +102,11 @@ private: bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector<DisplayViewport> mViewports; TouchAffineTransformation transform; - std::optional<int32_t /*deviceId*/> mStylusGestureNotified GUARDED_BY(mLock){}; bool mIsInputMethodConnectionActive{false}; + std::condition_variable mStylusGestureNotifiedCondition; + std::optional<DeviceId> mDeviceIdOfNotifiedStylusGesture GUARDED_BY(mLock){}; + uint32_t mNextPointerCaptureSequenceNumber{0}; }; diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp new file mode 100644 index 0000000000..f4a06f7d28 --- /dev/null +++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp @@ -0,0 +1,138 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "FakeInputTracingBackend.h" + +#include <android-base/logging.h> +#include <utils/Errors.h> + +namespace android::inputdispatcher { + +namespace { + +// Use a larger timeout while waiting for events to be traced, compared to the timeout used while +// waiting to receive events through the input channel. Events are traced from a separate thread, +// which does not have the same high thread priority as the InputDispatcher's thread, so the tracer +// is expected to lag behind the Dispatcher at times. +constexpr auto TRACE_TIMEOUT = std::chrono::seconds(5); + +base::ResultError<> error(const std::ostringstream& ss) { + return base::ResultError(ss.str(), BAD_VALUE); +} + +inline auto getId(const trace::TracedEvent& v) { + return std::visit([](const auto& event) { return event.id; }, v); +} + +} // namespace + +// --- VerifyingTrace --- + +void VerifyingTrace::expectKeyDispatchTraced(const KeyEvent& event, int32_t windowId) { + std::scoped_lock lock(mLock); + mExpectedEvents.emplace_back(event, windowId); +} + +void VerifyingTrace::expectMotionDispatchTraced(const MotionEvent& event, int32_t windowId) { + std::scoped_lock lock(mLock); + mExpectedEvents.emplace_back(event, windowId); +} + +void VerifyingTrace::verifyExpectedEventsTraced() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + + base::Result<void> result; + mEventTracedCondition.wait_for(lock, TRACE_TIMEOUT, [&]() REQUIRES(mLock) { + for (const auto& [expectedEvent, windowId] : mExpectedEvents) { + std::visit([&](const auto& event) + REQUIRES(mLock) { result = verifyEventTraced(event, windowId); }, + expectedEvent); + if (!result.ok()) { + return false; + } + } + return true; + }); + + EXPECT_TRUE(result.ok()) + << "Timed out waiting for all expected events to be traced successfully: " + << result.error().message(); +} + +void VerifyingTrace::reset() { + std::scoped_lock lock(mLock); + mTracedEvents.clear(); + mTracedWindowDispatches.clear(); + mExpectedEvents.clear(); +} + +template <typename Event> +base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent, + int32_t expectedWindowId) const { + std::ostringstream msg; + + auto tracedEventsIt = mTracedEvents.find(expectedEvent.getId()); + if (tracedEventsIt == mTracedEvents.end()) { + msg << "Expected event with ID 0x" << std::hex << expectedEvent.getId() + << " to be traced, but it was not.\n" + << "Expected event: " << expectedEvent; + return error(msg); + } + + auto tracedDispatchesIt = + std::find_if(mTracedWindowDispatches.begin(), mTracedWindowDispatches.end(), + [&](const WindowDispatchArgs& args) { + return args.windowId == expectedWindowId && + getId(args.eventEntry) == expectedEvent.getId(); + }); + if (tracedDispatchesIt == mTracedWindowDispatches.end()) { + msg << "Expected dispatch of event with ID 0x" << std::hex << expectedEvent.getId() + << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not." + << "\nExpected event: " << expectedEvent; + return error(msg); + } + + return {}; +} + +// --- FakeInputTracingBackend --- + +void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedEvents.emplace(event.id); + } + mTrace->mEventTracedCondition.notify_all(); +} + +void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedEvents.emplace(event.id); + } + mTrace->mEventTracedCondition.notify_all(); +} + +void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) const { + { + std::scoped_lock lock(mTrace->mLock); + mTrace->mTracedWindowDispatches.push_back(args); + } + mTrace->mEventTracedCondition.notify_all(); +} + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h new file mode 100644 index 0000000000..40ca3a29f8 --- /dev/null +++ b/services/inputflinger/tests/FakeInputTracingBackend.h @@ -0,0 +1,91 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "../dispatcher/trace/InputTracingBackendInterface.h" + +#include <android-base/result.h> +#include <android-base/thread_annotations.h> +#include <gtest/gtest.h> +#include <input/Input.h> +#include <condition_variable> +#include <memory> +#include <mutex> +#include <unordered_set> +#include <vector> + +namespace android::inputdispatcher { + +/** + * A class representing an input trace, used to make assertions on what was traced by + * InputDispatcher in tests. This class is thread-safe. + */ +class VerifyingTrace { +public: + VerifyingTrace() = default; + + /** Add an expectation for a key event to be traced. */ + void expectKeyDispatchTraced(const KeyEvent& event, int32_t windowId); + + /** Add an expectation for a motion event to be traced. */ + void expectMotionDispatchTraced(const MotionEvent& event, int32_t windowId); + + /** + * Wait and verify that all expected events are traced. + * This is a lenient verifier that does not expect the events to be traced in the order + * that the events were expected, and does not fail if there are events that are traced that + * were not expected. Verifying does not clear the expectations. + */ + void verifyExpectedEventsTraced(); + + /** Reset the trace and clear all expectations. */ + void reset(); + +private: + std::mutex mLock; + std::condition_variable mEventTracedCondition; + std::unordered_set<uint32_t /*eventId*/> mTracedEvents GUARDED_BY(mLock); + using WindowDispatchArgs = trace::InputTracingBackendInterface::WindowDispatchArgs; + std::vector<WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock); + std::vector<std::pair<std::variant<KeyEvent, MotionEvent>, int32_t /*windowId*/>> + mExpectedEvents GUARDED_BY(mLock); + + friend class FakeInputTracingBackend; + + // Helper to verify that the given event appears as expected in the trace. If the verification + // fails, the error message describes why. + template <typename Event> + base::Result<void> verifyEventTraced(const Event&, int32_t windowId) const REQUIRES(mLock); +}; + +/** + * A backend implementation for input tracing that records events to the provided + * VerifyingTrace used for testing. + */ +class FakeInputTracingBackend : public trace::InputTracingBackendInterface { +public: + FakeInputTracingBackend(std::shared_ptr<VerifyingTrace> trace) : mTrace(trace) {} + +private: + std::shared_ptr<VerifyingTrace> mTrace; + + void traceKeyEvent(const trace::TracedKeyEvent& entry) const override; + void traceMotionEvent(const trace::TracedMotionEvent& entry) const override; + void traceWindowDispatch(const WindowDispatchArgs& entry) const override; +}; + +} // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f1f4a61e73..4c455f7804 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -17,6 +17,7 @@ #include "../dispatcher/InputDispatcher.h" #include "../BlockingQueue.h" #include "FakeApplicationHandle.h" +#include "FakeInputTracingBackend.h" #include "TestEventMatchers.h" #include <NotifyArgsBuilders.h> @@ -154,7 +155,7 @@ static KeyEvent getTestKeyEvent() { class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { struct AnrResult { sp<IBinder> token{}; - gui::Pid pid{gui::Pid::INVALID}; + std::optional<gui::Pid> pid{}; }; /* Stores data about a user-activity-poke event from the dispatcher. */ struct UserActivityPokeEvent { @@ -259,7 +260,7 @@ public: void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout, const sp<IBinder>& expectedToken, - gui::Pid expectedPid) { + std::optional<gui::Pid> expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); AnrResult result; @@ -279,7 +280,7 @@ public: } void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken, - gui::Pid expectedPid) { + std::optional<gui::Pid> expectedPid) { std::unique_lock lock(mLock); android::base::ScopedLockAssertion assumeLocked(mLock); AnrResult result; @@ -363,6 +364,8 @@ public: mInterceptKeyTimeout = timeout; } + std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; } + void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } void assertUserActivityNotPoked() { @@ -521,16 +524,14 @@ private: void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid, const std::string&) override { std::scoped_lock lock(mLock); - ASSERT_TRUE(pid.has_value()); - mAnrWindows.push({connectionToken, *pid}); + mAnrWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } void notifyWindowResponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid) override { std::scoped_lock lock(mLock); - ASSERT_TRUE(pid.has_value()); - mResponsiveWindows.push({connectionToken, *pid}); + mResponsiveWindows.push({connectionToken, pid}); mNotifyAnr.notify_all(); } @@ -658,14 +659,22 @@ private: // --- InputDispatcherTest --- +// The trace is a global variable for now, to avoid having to pass it into all of the +// FakeWindowHandles created throughout the tests. +// TODO(b/210460522): Update the tests to avoid the need to have the trace be a global variable. +static std::shared_ptr<VerifyingTrace> gVerifyingTrace = std::make_shared<VerifyingTrace>(); + class InputDispatcherTest : public testing::Test { protected: std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy; std::unique_ptr<InputDispatcher> mDispatcher; void SetUp() override { + gVerifyingTrace->reset(); mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); + mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, + std::make_unique<FakeInputTracingBackend>( + gVerifyingTrace)); mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread @@ -673,6 +682,7 @@ protected: } void TearDown() override { + ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced()); ASSERT_EQ(OK, mDispatcher->stop()); mFakePolicy.reset(); mDispatcher.reset(); @@ -1083,8 +1093,8 @@ public: EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode()); } - void assertNoEvents() { - std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); + void assertNoEvents(std::chrono::milliseconds timeout) { + std::unique_ptr<InputEvent> event = consume(timeout); if (event == nullptr) { return; } @@ -1113,7 +1123,7 @@ public: sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); } - int getChannelFd() { return mConsumer.getChannel()->getFd().get(); } + int getChannelFd() { return mConsumer.getChannel()->getFd(); } private: InputConsumer mConsumer; @@ -1395,11 +1405,7 @@ public: } std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() { - if (mInputReceiver == nullptr) { - ADD_FAILURE() << "Invalid receive event on window with no receiver"; - return std::make_pair(std::nullopt, nullptr); - } - return mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + return receive(); } void finishEvent(uint32_t sequenceNum) { @@ -1412,14 +1418,14 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - void assertNoEvents() { + void assertNoEvents(std::chrono::milliseconds timeout = CONSUME_TIMEOUT_NO_EVENT_EXPECTED) { if (mInputReceiver == nullptr && mInfo.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL)) { return; // Can't receive events if the window does not have input channel } ASSERT_NE(nullptr, mInputReceiver) << "Window without InputReceiver must specify feature NO_INPUT_CHANNEL"; - mInputReceiver->assertNoEvents(); + mInputReceiver->assertNoEvents(timeout); } sp<IBinder> getToken() { return mInfo.token; } @@ -1437,6 +1443,19 @@ public: int getChannelFd() { return mInputReceiver->getChannelFd(); } + // FakeWindowHandle uses this consume method to ensure received events are added to the trace. + std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) { + if (mInputReceiver == nullptr) { + LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; + } + std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled); + if (event == nullptr) { + ADD_FAILURE() << "Consume failed: no event"; + } + expectReceivedEventTraced(event); + return event; + } + private: FakeWindowHandle(std::string name) : mName(name){}; const std::string mName; @@ -1444,15 +1463,36 @@ private: static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger friend class sp<FakeWindowHandle>; - std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) { + // FakeWindowHandle uses this receive method to ensure received events are added to the trace. + std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>> receive() { if (mInputReceiver == nullptr) { - LOG(FATAL) << "Cannot consume event from a window with no input event receiver"; + ADD_FAILURE() << "Invalid receive event on window with no receiver"; + return std::make_pair(std::nullopt, nullptr); } - std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled); - if (event == nullptr) { - ADD_FAILURE() << "Consume failed: no event"; + auto out = mInputReceiver->receiveEvent(CONSUME_TIMEOUT_EVENT_EXPECTED); + const auto& [_, event] = out; + expectReceivedEventTraced(event); + return std::move(out); + } + + void expectReceivedEventTraced(const std::unique_ptr<InputEvent>& event) { + if (!event) { + return; + } + + switch (event->getType()) { + case InputEventType::KEY: { + gVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event), mInfo.id); + break; + } + case InputEventType::MOTION: { + gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event), + mInfo.id); + break; + } + default: + break; } - return event; } }; @@ -1512,7 +1552,7 @@ public: std::unique_ptr<MotionEvent> consumeMotion() { return mInputReceiver.consumeMotion(); } - void assertNoEvents() { mInputReceiver.assertNoEvents(); } + void assertNoEvents() { mInputReceiver.assertNoEvents(CONSUME_TIMEOUT_NO_EVENT_EXPECTED); } private: FakeInputReceiver mInputReceiver; @@ -5777,27 +5817,27 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Window should receive motion event. - firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + firstWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); // Transfer touch focus ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken(), SECOND_DISPLAY_ID)); // The first window gets cancel. - firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID); - secondWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID); + firstWindowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID); + secondWindowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionMove(SECOND_DISPLAY_ID); + firstWindowInSecondary->assertNoEvents(); + secondWindowInSecondary->consumeMotionMove(SECOND_DISPLAY_ID); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, {150, 50})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - firstWindowInPrimary->assertNoEvents(); - secondWindowInPrimary->consumeMotionUp(SECOND_DISPLAY_ID); + firstWindowInSecondary->assertNoEvents(); + secondWindowInSecondary->consumeMotionUp(SECOND_DISPLAY_ID); } TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) { @@ -6151,6 +6191,219 @@ TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) { monitor.assertNoEvents(); } +/** + * Two displays + * The first monitor has a foreground window, a monitor + * The second window has only one monitor. + * We first inject a Down event into the first display, this injection should succeed and both + * the foreground window and monitor should receive a down event, then inject a Down event into + * the second display as well, this injection should fail, at this point, the first display + * window and monitor should not receive a cancel or any other event. + * Continue to inject Move and UP events to the first display, the events should be received + * normally by the foreground window and monitor. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCanceledWhenAnotherEmptyDisplayReceiveEvents) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should fail since there's no " + "touchable window"; + + // Continue to inject event to first display. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * Two displays + * There is a monitor and foreground window on each display. + * First, we inject down events into each of the two displays, at this point, the foreground windows + * and monitors on both displays should receive down events. + * At this point, the foreground window of the second display goes away, the gone window should + * receive the cancel event, and the other windows and monitors should not receive any events. + * Inject a move event into the second display. At this point, the injection should fail because + * the second display no longer has a foreground window. At this point, the monitor on the second + * display should receive a cancel event, and any windows or monitors on the first display should + * not receive any events, and any subsequent injection of events into the second display should + * also fail. + * Continue to inject events into the first display, and the events should all be injected + * successfully and received normally. + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCancelWhenAnotherDisplayMonitorTouchCanceled) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + sp<FakeWindowHandle> secondWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "SecondForeground", + SECOND_DISPLAY_ID); + + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID); + + // There is a foreground window on both displays. + mDispatcher->onWindowInfosChanged({{*window->getInfo(), *secondWindow->getInfo()}, {}, 0, 0}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected into the first display should succeed"; + + window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {100, 200})) + << "The down event injected into the second display should succeed"; + + secondWindow->consumeMotionDown(SECOND_DISPLAY_ID); + secondMonitor.consumeMotionDown(SECOND_DISPLAY_ID); + + // Now second window is gone away. + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + // The gone window should receive a cancel, and the monitor on the second display should not + // receive any events. + secondWindow->consumeMotionCancel(SECOND_DISPLAY_ID); + secondMonitor.assertNoEvents(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + SECOND_DISPLAY_ID, {110, 220})) + << "The move event injected into the second display should fail because there's no " + "touchable window"; + // Now the monitor on the second display should receive a cancel event. + secondMonitor.consumeMotionCancel(SECOND_DISPLAY_ID); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "The move event injected into the first display should succeed"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID, + {110, 220})) + << "The up event injected into the second display should fail because there's no " + "touchable window"; + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {110, 220})) + << "The up event injected into the first display should succeed"; + + window->consumeMotionUp(ADISPLAY_ID_DEFAULT); + monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT); + + window->assertNoEvents(); + monitor.assertNoEvents(); + secondWindow->assertNoEvents(); + secondMonitor.assertNoEvents(); +} + +/** + * One display with transform + * There is a foreground window and a monitor on the display + * Inject down event and move event sequentially, the foreground window and monitor can receive down + * event and move event, then let the foreground window go away, the foreground window receives + * cancel event, inject move event again, the monitor receives cancel event, all the events received + * by the monitor should be with the same transform as the display + */ +TEST_F(InputDispatcherMonitorTest, MonitorTouchCancelEventWithDisplayTransform) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT); + + ui::Transform transform; + transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + + gui::DisplayInfo displayInfo; + displayInfo.displayId = ADISPLAY_ID_DEFAULT; + displayInfo.transform = transform; + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0}); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "The down event injected should succeed"; + + window->consumeMotionDown(); + std::unique_ptr<MotionEvent> downMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, downMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, downMotionEvent->getAction()); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should succeed"; + + window->consumeMotionMove(); + std::unique_ptr<MotionEvent> moveMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, moveMotionEvent->getTransform()); + EXPECT_EQ(AMOTION_EVENT_ACTION_MOVE, moveMotionEvent->getAction()); + + // Let foreground window gone + mDispatcher->onWindowInfosChanged({{}, {displayInfo}, 0, 0}); + + // Foreground window should receive a cancel event, but not the monitor. + window->consumeMotionCancel(); + + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The move event injected should failed"; + // Now foreground should not receive any events, but monitor should receive a cancel event + // with transform that same as display's display. + std::unique_ptr<MotionEvent> cancelMotionEvent = monitor.consumeMotion(); + EXPECT_EQ(transform, cancelMotionEvent->getTransform()); + EXPECT_EQ(ADISPLAY_ID_DEFAULT, cancelMotionEvent->getDisplayId()); + EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, cancelMotionEvent->getAction()); + + // Other event inject to this display should fail. + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 220})) + << "The up event injected should fail because the touched window was removed"; + window->assertNoEvents(); + monitor.assertNoEvents(); +} + TEST_F(InputDispatcherTest, TestMoveEvent) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, @@ -7048,7 +7301,7 @@ TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { mWindow->assertNoEvents(); } -TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) { +TEST_F(InputDispatcherFallbackKeyTest, InputChannelRemovedDuringPolicyCall) { setFallback(AKEYCODE_B); mDispatcher->notifyKey( KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); @@ -7081,6 +7334,71 @@ TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); } +TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) { + // When the unhandled key is reported to the policy next, remove the window. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + return KeyEventBuilder(event).keyCode(AKEYCODE_B).build(); + }); + // Release the original key, which the app will not handle. When this unhandled key is reported + // to the policy, the window will be removed. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + + // Since the window was removed, it loses focus, and the channel state will be reset. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + mWindow->consumeFocusEvent(false); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedWhileAwaitingFinishedSignal) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + const auto [seq, event] = mWindow->receiveEvent(); + ASSERT_TRUE(seq.has_value() && event != nullptr) << "Failed to receive fallback event"; + ASSERT_EQ(event->getType(), InputEventType::KEY); + ASSERT_THAT(static_cast<const KeyEvent&>(*event), + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Remove the window now, which should generate a cancellations and make the window lose focus. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + mWindow->consumeFocusEvent(false); + + // Finish the event by reporting it as handled. + mWindow->finishEvent(*seq); + mWindow->assertNoEvents(); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms; @@ -7090,12 +7408,9 @@ protected: sp<FakeWindowHandle> mWindow; virtual void SetUp() override { - mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); - mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); - mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); - ASSERT_EQ(OK, mDispatcher->start()); + InputDispatcherTest::SetUp(); + mDispatcher->setKeyRepeatConfiguration(KEY_REPEAT_TIMEOUT, KEY_REPEAT_DELAY); setUpWindow(); } @@ -8013,13 +8328,13 @@ protected: } } - void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints, + void touchAndAssertPositions(sp<FakeWindowHandle> touchedWindow, int32_t action, + const std::vector<PointF>& touchedPoints, std::vector<PointF> expectedPoints) { mDispatcher->notifyMotion(generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints)); - // Always consume from window1 since it's the window that has the InputReceiver - consumeMotionEvent(mWindow1, action, expectedPoints); + consumeMotionEvent(touchedWindow, action, expectedPoints); } }; @@ -8027,15 +8342,15 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) { // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) { @@ -8046,21 +8361,21 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentTransform) // Touch Window 1 PointF touchedPoint = {10, 10}; PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); // Release touch on Window 1 - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Touch Window 2 touchedPoint = {150, 150}; expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); - touchAndAssertPositions(AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_UP, {touchedPoint}, {expectedPoint}); // Update the transform so rotation is set mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint); - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); + touchAndAssertPositions(mWindow2, AMOTION_EVENT_ACTION_DOWN, {touchedPoint}, {expectedPoint}); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform) { @@ -8070,22 +8385,25 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentTransform // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 + // Since this is part of the same touch gesture that has already been dispatched to Window 1, + // the touch stream from Window 2 will be merged with the stream in Window 1. The merged stream + // will continue to be dispatched through Window 1. touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Release Window 2 - touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints); expectedPoints.pop_back(); // Update the transform so rotation is set for Window 2 mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTransform) { @@ -8095,37 +8413,37 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentTrans // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); // Release Window 2 - touchAndAssertPositions(POINTER_1_UP, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_UP, touchedPoints, expectedPoints); expectedPoints.pop_back(); // Touch Window 2 mWindow2->setWindowTransform(0, -1, 1, 0); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) { @@ -8135,20 +8453,20 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithSc // Touch Window 1 std::vector<PointF> touchedPoints = {PointF{10, 10}}; std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_DOWN, touchedPoints, expectedPoints); // Touch Window 2 touchedPoints.push_back(PointF{150, 150}); expectedPoints.push_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1])); - touchAndAssertPositions(POINTER_1_DOWN, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, POINTER_1_DOWN, touchedPoints, expectedPoints); // Move both windows touchedPoints = {{20, 20}, {175, 175}}; expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]), getPointInWindow(mWindow2->getInfo(), touchedPoints[1])}; - touchAndAssertPositions(AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); + touchAndAssertPositions(mWindow1, AMOTION_EVENT_ACTION_MOVE, touchedPoints, expectedPoints); } /** @@ -8192,7 +8510,7 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, HoverIntoClone) { .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(150)) .build()); consumeMotionEvent(mWindow1, ACTION_HOVER_EXIT, {{50, 50}}); - consumeMotionEvent(mWindow1, ACTION_HOVER_ENTER, + consumeMotionEvent(mWindow2, ACTION_HOVER_ENTER, {getPointInWindow(mWindow2->getInfo(), PointF{150, 150})}); } @@ -8614,16 +8932,11 @@ TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) { /** * If a window is processing a motion event, and then a key event comes in, the key event should * not get delivered to the focused window until the motion is processed. - * - * Warning!!! - * This test depends on the value of android::inputdispatcher::KEY_WAITING_FOR_MOTION_TIMEOUT - * and the injection timeout that we specify when injecting the key. - * We must have the injection timeout (100ms) be smaller than - * KEY_WAITING_FOR_MOTION_TIMEOUT (currently 500ms). - * - * If that value changes, this test should also change. */ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); @@ -8632,23 +8945,18 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { ASSERT_TRUE(downSequenceNum); const auto& [upSequenceNum, upEvent] = mWindow->receiveEvent(); ASSERT_TRUE(upSequenceNum); - // Don't finish the events yet, and send a key - // Injection will "succeed" because we will eventually give up and send the key to the focused - // window even if motions are still being processed. But because the injection timeout is short, - // we will receive INJECTION_TIMED_OUT as the result. - InputEventInjectionResult result = - injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT, - InputEventInjectionSync::WAIT_FOR_RESULT, 100ms); - ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result); + // Don't finish the events yet, and send a key + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); // Key will not be sent to the window, yet, because the window is still processing events // and the key remains pending, waiting for the touch events to be processed // Make sure that `assertNoEvents` doesn't wait too long, because it could cause an ANR. - // Rely here on the fact that it uses CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); - std::this_thread::sleep_for(500ms); + std::this_thread::sleep_for(400ms); // if we wait long enough though, dispatcher will give up, and still send the key // to the focused window, even though we have not yet finished the motion event mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT); @@ -8663,7 +8971,10 @@ TEST_F(InputDispatcherSingleWindowAnr, Key_StaysPendingWhileMotionIsProcessed) { * focused window right away. */ TEST_F(InputDispatcherSingleWindowAnr, - PendingKey_IsDroppedWhileMotionIsProcessedAndNewTouchComesIn) { + PendingKey_IsDeliveredWhileMotionIsProcessingAndNewTouchComesIn) { + // The timeouts in this test are established by relying on the fact that the "key waiting for + // events timeout" is equal to 500ms. + ASSERT_EQ(mFakePolicy->getKeyWaitingForEventsTimeout(), 500ms); mWindow->setDispatchingTimeout(2s); // Set a long ANR timeout to prevent it from triggering mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); @@ -8678,15 +8989,19 @@ TEST_F(InputDispatcherSingleWindowAnr, .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) .build()); // At this point, key is still pending, and should not be sent to the application yet. - // Make sure the `assertNoEvents` check doesn't take too long. It uses - // CONSUME_TIMEOUT_NO_EVENT_EXPECTED under the hood. - static_assert(CONSUME_TIMEOUT_NO_EVENT_EXPECTED < 100ms); - mWindow->assertNoEvents(); + mWindow->assertNoEvents(100ms); // Now tap down again. It should cause the pending key to go to the focused window right away. tapOnWindow(); - mWindow->consumeKeyEvent(WithKeyAction(AKEY_EVENT_ACTION_DOWN)); // it doesn't matter that we - // haven't ack'd the other events yet. We can finish events in any order. + // Now that we tapped, we should receive the key immediately. + // Since there's still room for slowness, we use 200ms, which is much less than + // the "key waiting for events' timeout of 500ms minus the already waited 100ms duration. + std::unique_ptr<InputEvent> keyEvent = mWindow->consume(200ms); + ASSERT_NE(nullptr, keyEvent); + ASSERT_EQ(InputEventType::KEY, keyEvent->getType()); + ASSERT_THAT(static_cast<KeyEvent&>(*keyEvent), WithKeyAction(AKEY_EVENT_ACTION_DOWN)); + // it doesn't matter that we haven't ack'd the other events yet. We can finish events in any + // order. mWindow->finishEvent(*downSequenceNum); // first tap's ACTION_DOWN mWindow->finishEvent(*upSequenceNum); // first tap's ACTION_UP mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); @@ -8743,6 +9058,61 @@ TEST_F(InputDispatcherSingleWindowAnr, TwoGesturesWithAnr) { mWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); } +// Send an event to the app and have the app not respond right away. Then remove the app window. +// When the window is removed, the dispatcher will cancel the events for that window. +// So InputDispatcher will enqueue ACTION_CANCEL event as well. +TEST_F(InputDispatcherSingleWindowAnr, AnrAfterWindowRemoval) { + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {WINDOW_LOCATION})); + + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + + // Remove the window, but the input channel should remain alive. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + // Since the window was removed, Dispatcher does not know the PID associated with the window + // anymore, so the policy is notified without the PID. + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow->getToken(), + /*pid=*/std::nullopt); + + mWindow->finishEvent(*sequenceNum); + // The cancellation was generated when the window was removed, along with the focus event. + mWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT))); + mWindow->consumeFocusEvent(false); + ASSERT_TRUE(mDispatcher->waitForIdle()); + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt); +} + +// Send an event to the app and have the app not respond right away. Wait for the policy to be +// notified of the unresponsive window, then remove the app window. +TEST_F(InputDispatcherSingleWindowAnr, AnrFollowedByWindowRemoval) { + mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {WINDOW_LOCATION})); + + const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN + ASSERT_TRUE(sequenceNum); + const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT); + mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow); + + // Remove the window, but the input channel should remain alive. + mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); + + mWindow->finishEvent(*sequenceNum); + // The cancellation was generated during the ANR, and the window lost focus when it was removed. + mWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT))); + mWindow->consumeFocusEvent(false); + ASSERT_TRUE(mDispatcher->waitForIdle()); + // Since the window was removed, Dispatcher does not know the PID associated with the window + // becoming responsive, so the policy is notified without the PID. + mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt); +} + class InputDispatcherMultiWindowAnr : public InputDispatcherTest { virtual void SetUp() override { InputDispatcherTest::SetUp(); @@ -9334,7 +9704,7 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, CanGetFocus) { TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedIfAllWindowsFocusable) { setFocusedWindow(mMirror); - // window gets focused + // window gets focused because it is above the mirror mWindow->consumeFocusEvent(true); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; @@ -9407,10 +9777,10 @@ TEST_F(InputDispatcherMirrorWindowFocusTests, FocusedWhileWindowsAlive) { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - mWindow->consumeKeyDown(ADISPLAY_ID_NONE); + mMirror->consumeKeyDown(ADISPLAY_ID_NONE); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher)) << "Inject key event should return InputEventInjectionResult::SUCCEEDED"; - mWindow->consumeKeyUp(ADISPLAY_ID_NONE); + mMirror->consumeKeyUp(ADISPLAY_ID_NONE); // Both windows are removed mDispatcher->onWindowInfosChanged({{}, {}, 0, 0}); diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 460a7b185d..c1dc7ffcfb 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -2706,6 +2706,31 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe ASSERT_NO_FATAL_FAILURE(mapper2.assertProcessWasCalled()); } +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorNotSet) { + // Set some behavior to force the configuration to be update. + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_FALSE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.has_value()); +} + +TEST_F(InputDeviceTest, Configure_SmoothScrollViewBehaviorEnabled) { + mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.viewBehavior_smoothScroll", "1"); + mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(), + AINPUT_SOURCE_KEYBOARD); + + std::list<NotifyArgs> unused = + mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), + /*changes=*/{}); + + ASSERT_TRUE(mDevice->getDeviceInfo().getViewBehavior().shouldSmoothScroll.value_or(false)); +} + TEST_F(InputDeviceTest, WakeDevice_AddsWakeFlagToProcessNotifyArgs) { mFakeEventHub->addConfigurationProperty(EVENTHUB_ID, "device.wake", "1"); FakeInputMapper& mapper = diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp index 8a4f6f0acc..81c335326a 100644 --- a/services/inputflinger/tests/fuzzers/Android.bp +++ b/services/inputflinger/tests/fuzzers/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_input_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp index be765cc364..c2bf275611 100644 --- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp @@ -123,6 +123,7 @@ void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfigura config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7); config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool(); config.touchpadTapToClickEnabled = fdp.ConsumeBool(); + config.touchpadTapDraggingEnabled = fdp.ConsumeBool(); config.touchpadRightClickZoneEnabled = fdp.ConsumeBool(); } diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 019fefaca4..afaf0ae84f 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -10,6 +10,7 @@ package { aconfig_declarations { name: "sensorservice_flags", package: "com.android.frameworks.sensorservice.flags", + container: "system", srcs: ["senserservice_flags.aconfig"], } diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index ad10e7a7bd..118d9281fc 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -340,6 +340,12 @@ private: binder::Status onSensorPrivacyChanged(int toggleType, int sensor, bool enabled); + // This callback is used for additional automotive-specific states for sensor privacy + // such as AUTO_DRIVER_ASSISTANCE_APPS. The newly defined states will only be valid + // for camera privacy on automotive devices. onSensorPrivacyChanged() will still be + // invoked whenever the enabled status of a toggle changes. + binder::Status onSensorPrivacyStateChanged(int, int, int) {return binder::Status::ok();} + protected: std::atomic_bool mSensorPrivacyEnabled; wp<SensorService> mService; diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig index a3bd0ee483..8d43f79950 100644 --- a/services/sensorservice/senserservice_flags.aconfig +++ b/services/sensorservice/senserservice_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.frameworks.sensorservice.flags" +container: "system" flag { name: "dynamic_sensor_hal_reconnect_handling" diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 0989863b7d..dcef9a3775 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -10,6 +10,7 @@ package { aconfig_declarations { name: "surfaceflinger_flags", package: "com.android.graphics.surfaceflinger.flags", + container: "system", srcs: ["surfaceflinger_flags.aconfig"], } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index d26ca9dd00..e2d17ee502 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -147,8 +147,6 @@ public: bool hasProtectedLayers() const; - bool hasSolidColorLayers() const; - // True if any layer in this cached set has CachingHint::Disabled bool cachingHintExcludesLayers() const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h index 1f241b091c..dc3821ca43 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h @@ -258,11 +258,6 @@ public: gui::CachingHint getCachingHint() const { return mCachingHint.get(); } - bool hasSolidColorCompositionType() const { - return getOutputLayer()->getLayerFE().getCompositionState()->compositionType == - aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR; - } - float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; } void dump(std::string& result) const; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 7fe3369f88..091c207464 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -394,7 +394,6 @@ void OutputLayer::writeStateToHWC(bool includeGeometry, bool skipLayer, uint32_t auto requestedCompositionType = outputIndependentState->compositionType; if (requestedCompositionType == Composition::SOLID_COLOR && state.overrideInfo.buffer) { - // this should never happen, as SOLID_COLOR is skipped from caching, b/230073351 requestedCompositionType = Composition::DEVICE; } @@ -665,6 +664,9 @@ void OutputLayer::uncacheBuffers(const std::vector<uint64_t>& bufferIdsToUncache void OutputLayer::writeBufferStateToHWC(HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState, bool skipLayer) { + if (skipLayer && outputIndependentState.buffer == nullptr) { + return; + } auto supportedPerFrameMetadata = getOutput().getDisplayColorProfile()->getSupportedPerFrameMetadata(); if (auto error = hwcLayer->setPerFrameMetadata(supportedPerFrameMetadata, diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 869dda61bb..1f53588412 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -393,12 +393,6 @@ bool CachedSet::hasProtectedLayers() const { [](const Layer& layer) { return layer.getState()->isProtected(); }); } -bool CachedSet::hasSolidColorLayers() const { - return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { - return layer.getState()->hasSolidColorCompositionType(); - }); -} - bool CachedSet::cachingHintExcludesLayers() const { const bool shouldExcludeLayers = std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 91cfe5dd41..0a5c43a99b 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -513,13 +513,6 @@ void Flattener::buildCachedSets(time_point now) { } } - for (const CachedSet& layer : mLayers) { - if (layer.hasSolidColorLayers()) { - ATRACE_NAME("layer->hasSolidColorLayers()"); - return; - } - } - std::vector<Run> runs = findCandidateRuns(now); std::optional<Run> bestRun = findBestRun(runs); diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9e35717c95..575a30e522 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -97,13 +97,13 @@ public: MOCK_CONST_METHOD1(isConnected, bool(PhysicalDisplayId)); MOCK_CONST_METHOD2(getModes, std::vector<HWComposer::HWCDisplayMode>(PhysicalDisplayId, int32_t)); - MOCK_CONST_METHOD1(getActiveMode, std::optional<hal::HWConfigId>(PhysicalDisplayId)); + MOCK_CONST_METHOD1(getActiveMode, ftl::Expected<hal::HWConfigId, status_t>(PhysicalDisplayId)); MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(PhysicalDisplayId)); MOCK_METHOD3(setActiveColorMode, status_t(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent)); MOCK_CONST_METHOD0(isUsingVrComposer, bool()); MOCK_CONST_METHOD1(getDisplayConnectionType, ui::DisplayConnectionType(PhysicalDisplayId)); MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(PhysicalDisplayId)); - MOCK_CONST_METHOD2(getDisplayVsyncPeriod, status_t(PhysicalDisplayId, nsecs_t*)); + MOCK_CONST_METHOD1(getDisplayVsyncPeriod, ftl::Expected<nsecs_t, status_t>(PhysicalDisplayId)); MOCK_METHOD4(setActiveModeWithConstraints, status_t(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index a7296f3d7a..62cfaf412c 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -4018,7 +4018,6 @@ TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, usesExpectedDisplaySettingsWithFp16Buffer) { SET_FLAG_FOR_TEST(flags::fp16_client_target, true); - ALOGE("alecmouri: %d", flags::fp16_client_target()); verify().ifMixedCompositionIs(false) .andIfUsesHdr(true) .withDisplayBrightnessNits(kDisplayLuminance) diff --git a/services/surfaceflinger/Display/DisplayModeRequest.h b/services/surfaceflinger/Display/DisplayModeRequest.h index d07cdf55d2..c0e77bb5fc 100644 --- a/services/surfaceflinger/Display/DisplayModeRequest.h +++ b/services/surfaceflinger/Display/DisplayModeRequest.h @@ -27,6 +27,9 @@ struct DisplayModeRequest { // Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE. bool emitEvent = false; + + // Whether to force the request to be applied, even if the mode is unchanged. + bool force = false; }; inline bool operator==(const DisplayModeRequest& lhs, const DisplayModeRequest& rhs) { diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 4f81482814..45f08a4521 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -24,6 +24,7 @@ #define ATRACE_TAG ATRACE_TAG_GRAPHICS +#include <common/FlagManager.h> #include <compositionengine/CompositionEngine.h> #include <compositionengine/Display.h> #include <compositionengine/DisplayColorProfile.h> @@ -214,6 +215,17 @@ void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps rende bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode, const hal::VsyncPeriodChangeConstraints& constraints, hal::VsyncPeriodChangeTimeline& outTimeline) { + // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For + // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared + // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed + // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`. + if (FlagManager::getInstance().connected_display()) { + std::scoped_lock lock(mDesiredModeLock); + if (mDesiredModeOpt) { + mDesiredModeOpt->force = false; + } + } + mPendingModeOpt = std::move(desiredMode); mIsModeSetPending = true; @@ -239,10 +251,8 @@ nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const { return 0; } - nsecs_t vsyncPeriod; - const auto status = mHwComposer.getDisplayVsyncPeriod(physicalId, &vsyncPeriod); - if (status == NO_ERROR) { - return vsyncPeriod; + if (const auto vsyncPeriodOpt = mHwComposer.getDisplayVsyncPeriod(physicalId).value_opt()) { + return *vsyncPeriodOpt; } return refreshRateSelector().getActiveMode().modePtr->getVsyncRate().getPeriodNsecs(); @@ -519,8 +529,7 @@ void DisplayDevice::animateOverlay() { } } -auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bool force) - -> DesiredModeAction { +auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction { ATRACE_CALL(); const auto& desiredModePtr = desiredMode.mode.modePtr; @@ -528,20 +537,26 @@ auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode, bo LOG_ALWAYS_FATAL_IF(getPhysicalId() != desiredModePtr->getPhysicalDisplayId(), "DisplayId mismatch"); - ALOGV("%s(%s)", __func__, to_string(*desiredModePtr).c_str()); + // TODO (b/318533819): Stringize DisplayModeRequest. + ALOGD("%s(%s, force=%s)", __func__, to_string(*desiredModePtr).c_str(), + desiredMode.force ? "true" : "false"); std::scoped_lock lock(mDesiredModeLock); if (mDesiredModeOpt) { // A mode transition was already scheduled, so just override the desired mode. const bool emitEvent = mDesiredModeOpt->emitEvent; + const bool force = mDesiredModeOpt->force; mDesiredModeOpt = std::move(desiredMode); mDesiredModeOpt->emitEvent |= emitEvent; + if (FlagManager::getInstance().connected_display()) { + mDesiredModeOpt->force |= force; + } return DesiredModeAction::None; } // If the desired mode is already active... const auto activeMode = refreshRateSelector().getActiveMode(); - if (!force && activeMode.modePtr->getId() == desiredModePtr->getId()) { + if (!desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) { if (activeMode == desiredMode.mode) { return DesiredModeAction::None; } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index 4ab6321064..edd57cce91 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -189,8 +189,7 @@ public: enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch }; - DesiredModeAction setDesiredMode(display::DisplayModeRequest&&, bool force = false) - EXCLUDES(mDesiredModeLock); + DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock); using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>; diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index 64a8ae7fcd..362ab9c39e 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -330,7 +330,11 @@ std::string AidlComposer::dumpDebugInfo() { t.join(); close(pipefds[0]); - return str; + + std::string hash; + mAidlComposer->getInterfaceHash(&hash); + return std::string(mAidlComposer->descriptor) + + " version:" + std::to_string(mComposerInterfaceVersion) + " hash:" + hash + str; } void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { @@ -339,7 +343,9 @@ void AidlComposer::registerCallback(HWC2::ComposerCallback& callback) { } mAidlComposerCallback = ndk::SharedRefBase::make<AidlIComposerCallbackWrapper>(callback); - AIBinder_setMinSchedulerPolicy(mAidlComposerCallback->asBinder().get(), SCHED_FIFO, 2); + + ndk::SpAIBinder binder = mAidlComposerCallback->asBinder(); + AIBinder_setMinSchedulerPolicy(binder.get(), SCHED_FIFO, 2); const auto status = mAidlComposerClient->registerCallback(mAidlComposerCallback); if (!status.isOk()) { diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h index ba0825c5af..224f50e78e 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayMode.h +++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h @@ -21,17 +21,17 @@ #include <android-base/stringprintf.h> #include <android/configuration.h> +#include <ftl/mixins.h> #include <ftl/small_map.h> #include <ui/DisplayId.h> #include <ui/DisplayMode.h> #include <ui/Size.h> #include <utils/Timers.h> +#include <common/FlagManager.h> #include <scheduler/Fps.h> -#include <common/FlagManager.h> #include "DisplayHardware/Hal.h" -#include "Scheduler/StrongTyping.h" namespace android { @@ -46,7 +46,12 @@ bool operator>(const DisplayModePtr&, const DisplayModePtr&) = delete; bool operator<=(const DisplayModePtr&, const DisplayModePtr&) = delete; bool operator>=(const DisplayModePtr&, const DisplayModePtr&) = delete; -using DisplayModeId = StrongTyping<ui::DisplayModeId, struct DisplayModeIdTag, Compare>; +struct DisplayModeId : ftl::DefaultConstructible<DisplayModeId, ui::DisplayModeId>, + ftl::Incrementable<DisplayModeId>, + ftl::Equatable<DisplayModeId>, + ftl::Orderable<DisplayModeId> { + using DefaultConstructible::DefaultConstructible; +}; using DisplayModes = ftl::SmallMap<DisplayModeId, DisplayModePtr, 3>; using DisplayModeIterator = DisplayModes::const_iterator; @@ -185,7 +190,7 @@ inline bool equalsExceptDisplayModeId(const DisplayMode& lhs, const DisplayMode& inline std::string to_string(const DisplayMode& mode) { return base::StringPrintf("{id=%d, hwcId=%d, resolution=%dx%d, vsyncRate=%s, " "dpi=%.2fx%.2f, group=%d, vrrConfig=%s}", - mode.getId().value(), mode.getHwcId(), mode.getWidth(), + ftl::to_underlying(mode.getId()), mode.getHwcId(), mode.getWidth(), mode.getHeight(), to_string(mode.getVsyncRate()).c_str(), mode.getDpi().x, mode.getDpi().y, mode.getGroup(), to_string(mode.getVrrConfig()).c_str()); diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 704ece516d..bd5efc379c 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -27,6 +27,7 @@ #include "HWC2.h" #include <android/configuration.h> +#include <common/FlagManager.h> #include <ui/Fence.h> #include <ui/FloatRect.h> #include <ui/GraphicBuffer.h> @@ -416,7 +417,19 @@ Error Display::setActiveConfigWithConstraints(hal::HWConfigId configId, VsyncPeriodChangeTimeline* outTimeline) { ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId); - if (isVsyncPeriodSwitchSupported()) { + // FIXME (b/319505580): At least the first config set on an external display must be + // `setActiveConfig`, so skip over the block that calls `setActiveConfigWithConstraints` + // for simplicity. + ui::DisplayConnectionType type = ui::DisplayConnectionType::Internal; + const bool connected_display = FlagManager::getInstance().connected_display(); + if (connected_display) { + // Do not bail out on error, since the underlying API may return UNSUPPORTED on older HWCs. + // TODO: b/323905961 - Remove this AIDL call. + getConnectionType(&type); + } + + if (isVsyncPeriodSwitchSupported() && + (!connected_display || type != ui::DisplayConnectionType::External)) { Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints; hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos; hwc2Constraints.seamlessRequired = constraints.seamlessRequired; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index cf1c3c10b7..bac24c701e 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -343,14 +343,18 @@ std::vector<HWComposer::HWCDisplayMode> HWComposer::getModesFromLegacyDisplayCon return modes; } -std::optional<hal::HWConfigId> HWComposer::getActiveMode(PhysicalDisplayId displayId) const { - RETURN_IF_INVALID_DISPLAY(displayId, std::nullopt); +ftl::Expected<hal::HWConfigId, status_t> HWComposer::getActiveMode( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); const auto hwcId = *fromPhysicalDisplayId(displayId); hal::HWConfigId configId; const auto error = static_cast<hal::Error>(mComposer->getActiveConfig(hwcId, &configId)); + if (error == hal::Error::BAD_CONFIG) { + return ftl::Unexpected(NO_INIT); + } - RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, std::nullopt); + RETURN_IF_HWC_ERROR_FOR("getActiveConfig", error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); return configId; } @@ -376,20 +380,20 @@ bool HWComposer::isVsyncPeriodSwitchSupported(PhysicalDisplayId displayId) const return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported(); } -status_t HWComposer::getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const { - RETURN_IF_INVALID_DISPLAY(displayId, 0); +ftl::Expected<nsecs_t, status_t> HWComposer::getDisplayVsyncPeriod( + PhysicalDisplayId displayId) const { + RETURN_IF_INVALID_DISPLAY(displayId, ftl::Unexpected(BAD_INDEX)); if (!isVsyncPeriodSwitchSupported(displayId)) { - return INVALID_OPERATION; + return ftl::Unexpected(INVALID_OPERATION); } + const auto hwcId = *fromPhysicalDisplayId(displayId); Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0; - auto error = + const auto error = static_cast<hal::Error>(mComposer->getDisplayVsyncPeriod(hwcId, &vsyncPeriodNanos)); - RETURN_IF_HWC_ERROR(error, displayId, 0); - *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos); - return NO_ERROR; + RETURN_IF_HWC_ERROR(error, displayId, ftl::Unexpected(UNKNOWN_ERROR)); + return static_cast<nsecs_t>(vsyncPeriodNanos); } std::vector<ui::ColorMode> HWComposer::getColorModes(PhysicalDisplayId displayId) const { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index fb32ff45e1..7fbbb1a672 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -25,6 +25,7 @@ #include <vector> #include <android-base/thread_annotations.h> +#include <ftl/expected.h> #include <ftl/future.h> #include <ui/DisplayIdentification.h> #include <ui/FenceTime.h> @@ -237,7 +238,7 @@ public: virtual std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const = 0; - virtual std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const = 0; + virtual ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const = 0; virtual std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const = 0; @@ -247,8 +248,7 @@ public: // Composer 2.4 virtual ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const = 0; virtual bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const = 0; - virtual status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const = 0; + virtual ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const = 0; virtual status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) = 0; @@ -424,7 +424,7 @@ public: std::vector<HWCDisplayMode> getModes(PhysicalDisplayId, int32_t maxFrameIntervalNs) const override; - std::optional<hal::HWConfigId> getActiveMode(PhysicalDisplayId) const override; + ftl::Expected<hal::HWConfigId, status_t> getActiveMode(PhysicalDisplayId) const override; std::vector<ui::ColorMode> getColorModes(PhysicalDisplayId) const override; @@ -435,8 +435,7 @@ public: // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; - status_t getDisplayVsyncPeriod(PhysicalDisplayId displayId, - nsecs_t* outVsyncPeriod) const override; + ftl::Expected<nsecs_t, status_t> getDisplayVsyncPeriod(PhysicalDisplayId) const override; status_t setActiveModeWithConstraints(PhysicalDisplayId, hal::HWConfigId, const hal::VsyncPeriodChangeConstraints&, hal::VsyncPeriodChangeTimeline* outTimeline) override; diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index c4ff9cc6be..12ab2c284a 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -291,6 +291,7 @@ std::vector<Capability> HidlComposer::getCapabilities() { std::string HidlComposer::dumpDebugInfo() { std::string info; + info += std::string(mComposer->descriptor) + "\n"; mComposer->dumpDebugInfo([&](const auto& tmpInfo) { info = tmpInfo.c_str(); }); return info; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 8b200a2cf8..ea06cf6de6 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -315,6 +315,7 @@ std::ostream& operator<<(std::ostream& out, const LayerSnapshot& obj) { if (obj.hasInputInfo()) { out << "\n input{" << "(" << obj.inputInfo.inputConfig.string() << ")"; + if (obj.inputInfo.canOccludePresentation) out << " canOccludePresentation"; if (obj.touchCropId != UNASSIGNED_LAYER_ID) out << " touchCropId=" << obj.touchCropId; if (obj.inputInfo.replaceTouchableRegionWithCrop) out << " replaceTouchableRegionWithCrop"; auto touchableRegion = obj.inputInfo.touchableRegion.getBounds(); diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp index 8df5d8c679..0966fe0496 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp @@ -1044,6 +1044,8 @@ void LayerSnapshotBuilder::updateInput(LayerSnapshot& snapshot, snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo() ? requested.windowInfoHandle->getInfo()->touchOcclusionMode : parentSnapshot.inputInfo.touchOcclusionMode; + snapshot.inputInfo.canOccludePresentation = parentSnapshot.inputInfo.canOccludePresentation || + (requested.flags & layer_state_t::eCanOccludePresentation); if (requested.dropInputMode == gui::DropInputMode::ALL || parentSnapshot.dropInputMode == gui::DropInputMode::ALL) { snapshot.dropInputMode = gui::DropInputMode::ALL; diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp index b72588ab47..cb0e2a1938 100644 --- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp +++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp @@ -170,6 +170,9 @@ void RequestedLayerState::merge(const ResolvedComposerState& resolvedComposerSta if ((oldFlags ^ flags) & layer_state_t::eIgnoreDestinationFrame) { changes |= RequestedLayerState::Changes::Geometry; } + if ((oldFlags ^ flags) & layer_state_t::eCanOccludePresentation) { + changes |= RequestedLayerState::Changes::Input; + } } if (clientState.what & layer_state_t::eBufferChanged) { diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index c80c8fd237..96eccf290f 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -148,7 +148,7 @@ DisplayEventReceiver::Event makeModeChanged(const scheduler::FrameRateMode& mode DisplayEventReceiver::Event event; event.header = {DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE, mode.modePtr->getPhysicalDisplayId(), systemTime()}; - event.modeChange.modeId = mode.modePtr->getId().value(); + event.modeChange.modeId = ftl::to_underlying(mode.modePtr->getId()); event.modeChange.vsyncPeriod = mode.fps.getPeriodNsecs(); return event; } @@ -404,6 +404,9 @@ VsyncEventData EventThread::getLatestVsyncEventData( }(); generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC), presentTime, deadline); + if (FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted(TimePoint::fromNs(presentTime)); + } return vsyncEventData; } @@ -721,6 +724,11 @@ void EventThread::dispatchEvent(const DisplayEventReceiver::Event& event, removeDisplayEventConnectionLocked(consumer); } } + if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC && + FlagManager::getInstance().vrr_config()) { + mCallback.onExpectedPresentTimePosted( + TimePoint::fromNs(event.vsync.vsyncData.preferredExpectedPresentationTime())); + } } void EventThread::dump(std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index 8970103a7c..90e61a984b 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -142,6 +142,7 @@ struct IEventThreadCallback { virtual bool throttleVsync(TimePoint, uid_t) = 0; virtual Period getVsyncPeriod(uid_t) = 0; virtual void resync() = 0; + virtual void onExpectedPresentTimePosted(TimePoint) = 0; }; namespace impl { diff --git a/services/surfaceflinger/Scheduler/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h index 3b61de7e83..9f4f5b6d39 100644 --- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h +++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h @@ -30,6 +30,8 @@ struct ISchedulerCallback { virtual void kernelTimerChanged(bool expired) = 0; virtual void triggerOnFrameRateOverridesChanged() = 0; virtual void onChoreographerAttached() = 0; + virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, + Fps renderRate) = 0; protected: ~ISchedulerCallback() = default; diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 5ce883ce39..dcb62546a6 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -221,9 +221,8 @@ auto LayerHistory::summarize(const RefreshRateSelector& selector, nsecs_t now) - const std::string categoryString = vote.category == FrameRateCategory::Default ? "" : base::StringPrintf("category=%s", ftl::enum_string(vote.category).c_str()); - ATRACE_FORMAT_INSTANT("%s %s %s (%d%)", ftl::enum_string(vote.type).c_str(), - to_string(vote.fps).c_str(), categoryString.c_str(), - weight * 100); + ATRACE_FORMAT_INSTANT("%s %s %s (%.2f)", ftl::enum_string(vote.type).c_str(), + to_string(vote.fps).c_str(), categoryString.c_str(), weight); summary.push_back({info->getName(), info->getOwnerUid(), vote.type, vote.fps, vote.seamlessness, vote.category, vote.categorySmoothSwitchOnly, weight, layerFocused}); diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 9c4f7a510b..9745452e89 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -327,7 +327,8 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec mLayerVote.type != LayerHistory::LayerVoteType::ExplicitDefault) { ATRACE_FORMAT_INSTANT("Vote %s", ftl::enum_string(mLayerVote.type).c_str()); ALOGV("%s voted %d", mName.c_str(), static_cast<int>(mLayerVote.type)); - votes.push_back(mLayerVote); + votes.push_back({mLayerVote.type, mLayerVote.fps, mLayerVote.seamlessness, + FrameRateCategory::Default, mLayerVote.categorySmoothSwitchOnly}); } return votes; diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index c3709e5cc2..e696e8c358 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -286,7 +286,8 @@ struct RefreshRateSelector::RefreshRateScoreComparator { std::string RefreshRateSelector::Policy::toString() const { return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s" ", primaryRanges=%s, appRequestRanges=%s}", - defaultMode.value(), allowGroupSwitching ? "true" : "false", + ftl::to_underlying(defaultMode), + allowGroupSwitching ? "true" : "false", to_string(primaryRanges).c_str(), to_string(appRequestRanges).c_str()); } @@ -843,14 +844,18 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; + // A method for UI Toolkit to send the touch signal via "HighHint" category vote, + // which will touch boost when there are no ExplicitDefault layer votes. This is an + // incomplete solution but accounts for cases such as games that use `setFrameRate` with default + // compatibility to limit the frame rate, which should not have touch boost. const bool hasInteraction = signals.touch || interactiveLayers > 0; - if (hasInteraction && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 && - touchBoostForExplicitExact && + + if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact && scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); - return {touchRefreshRates, GlobalSignals{.touch = signals.touch}}; + return {touchRefreshRates, GlobalSignals{.touch = true}}; } // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h index a1a7c289cf..6051e8935d 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h @@ -31,7 +31,6 @@ #include "DisplayHardware/DisplayMode.h" #include "Scheduler/OneShotTimer.h" -#include "Scheduler/StrongTyping.h" #include "ThreadContext.h" #include "Utils/Dumper.h" diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index c76d4bd982..6979f0399b 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -71,15 +71,13 @@ namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats, - IVsyncTrackerCallback& vsyncTrackerCallback) + surfaceflinger::Factory& factory, Fps activeRefreshRate, TimeStats& timeStats) : android::impl::MessageQueue(compositor), mFeatures(features), mVsyncConfiguration(factory.createVsyncConfiguration(activeRefreshRate)), mVsyncModulator(sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs())), mRefreshRateStats(std::make_unique<RefreshRateStats>(timeStats, activeRefreshRate)), - mSchedulerCallback(callback), - mVsyncTrackerCallback(vsyncTrackerCallback) {} + mSchedulerCallback(callback) {} Scheduler::~Scheduler() { // MessageQueue depends on VsyncSchedule, so first destroy it. @@ -134,10 +132,11 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - auto schedulePtr = std::make_shared<VsyncSchedule>( - selectorPtr->getActiveMode().modePtr, mFeatures, - [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, - mVsyncTrackerCallback); + auto schedulePtr = + std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures, + [this](PhysicalDisplayId id, bool enable) { + onHardwareVsyncRequest(id, enable); + }); registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); } @@ -222,7 +221,12 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, targets.try_emplace(id, &targeter.target()); } - if (!compositor.commit(pacesetterPtr->displayId, targets)) return; + if (!compositor.commit(pacesetterPtr->displayId, targets)) { + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } + return; + } } // The pacesetter may have changed or been registered anew during commit. @@ -263,6 +267,9 @@ void Scheduler::onFrameSignal(ICompositor& compositor, VsyncId vsyncId, } const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters); + if (FlagManager::getInstance().vrr_config()) { + compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId); + } compositor.sample(); for (const auto& [id, targeter] : targeters) { @@ -323,6 +330,19 @@ Period Scheduler::getVsyncPeriod(uid_t uid) { // behaviour. return Period::fromNs(currentPeriod.ns() * divisor); } +void Scheduler::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + const auto frameRateMode = [this] { + std::scoped_lock lock(mDisplayLock); + const auto pacesetterOpt = pacesetterDisplayLocked(); + const Display& pacesetter = *pacesetterOpt; + return pacesetter.selectorPtr->getActiveMode(); + }(); + + if (frameRateMode.modePtr->getVrrConfig()) { + mSchedulerCallback.onExpectedPresentTimePosted(expectedPresentTime, frameRateMode.modePtr, + frameRateMode.fps); + } +} ConnectionHandle Scheduler::createEventThread(Cycle cycle, frametimeline::TokenManager* tokenManager, diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 99126220b0..9f29e9f696 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -111,7 +111,7 @@ class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { public: Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&, - Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&); + Fps activeRefreshRate, TimeStats&); virtual ~Scheduler(); void startTimers(); @@ -458,6 +458,7 @@ private: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); + void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock); // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { @@ -497,8 +498,6 @@ private: ISchedulerCallback& mSchedulerCallback; - IVsyncTrackerCallback& mVsyncTrackerCallback; - // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h deleted file mode 100644 index a05c123956..0000000000 --- a/services/surfaceflinger/Scheduler/StrongTyping.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -namespace android { - -template <typename T, template <typename> class AbilityType> -struct Ability { - T& base() { return static_cast<T&>(*this); } - T const& base() const { return static_cast<T const&>(*this); } -}; - -template <typename T> -struct Add : Ability<T, Add> { - inline T operator+(T const& other) const { return T(this->base().value() + other.value()); } - inline T& operator++() { - ++this->base().value(); - return this->base(); - }; - inline T operator++(int) { - T tmp(this->base()); - operator++(); - return tmp; - }; - inline T& operator+=(T const& other) { - this->base().value() += other.value(); - return this->base(); - }; -}; - -template <typename T> -struct Compare : Ability<T, Compare> { - inline bool operator==(T const& other) const { return this->base().value() == other.value(); }; - inline bool operator<(T const& other) const { return this->base().value() < other.value(); } - inline bool operator<=(T const& other) const { return (*this < other) || (*this == other); } - inline bool operator!=(T const& other) const { return !(*this == other); } - inline bool operator>=(T const& other) const { return !(*this < other); } - inline bool operator>(T const& other) const { return !(*this < other || *this == other); } -}; - -template <typename T> -struct Hash : Ability<T, Hash> { - [[nodiscard]] std::size_t hash() const { - return std::hash<typename std::remove_const< - typename std::remove_reference<decltype(this->base().value())>::type>::type>{}( - this->base().value()); - } -}; - -template <typename T, typename W, template <typename> class... Ability> -struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... { - constexpr StrongTyping() = default; - constexpr explicit StrongTyping(T const& value) : mValue(value) {} - StrongTyping(StrongTyping const&) = default; - StrongTyping& operator=(StrongTyping const&) = default; - explicit inline operator T() const { return mValue; } - T const& value() const { return mValue; } - T& value() { return mValue; } - - friend std::ostream& operator<<(std::ostream& os, const StrongTyping<T, W, Ability...>& value) { - return os << value.value(); - } - -private: - T mValue{0}; -}; -} // namespace android - -namespace std { -template <typename T, typename W, template <typename> class... Ability> -struct hash<android::StrongTyping<T, W, Ability...>> { - std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const { - return k.hash(); - } -}; -} // namespace std diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h index f978016c9e..ed8f8fef93 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatch.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h @@ -20,10 +20,9 @@ #include <optional> #include <string> +#include <ftl/mixins.h> #include <utils/Timers.h> -#include "StrongTyping.h" - namespace android::scheduler { using ScheduleResult = std::optional<nsecs_t>; @@ -35,7 +34,11 @@ enum class CancelResult { Cancelled, TooLate, Error }; */ class VSyncDispatch { public: - using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>; + struct CallbackToken : ftl::DefaultConstructible<CallbackToken, size_t>, + ftl::Equatable<CallbackToken>, + ftl::Incrementable<CallbackToken> { + using DefaultConstructible::DefaultConstructible; + }; virtual ~VSyncDispatch(); diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 5cb0ffbfb7..f5aaf06655 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -107,7 +107,7 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance)); bool const wouldSkipAWakeup = mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); - if (FlagManager::getInstance().dont_skip_on_early()) { + if (FlagManager::getInstance().dont_skip_on_early_ro()) { if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { nextVsyncTime = mArmedInfo->mActualVsyncTime; } else { @@ -267,15 +267,15 @@ void VSyncDispatchTimerQueue::setTimer(nsecs_t targetTime, nsecs_t /*now*/) { } void VSyncDispatchTimerQueue::rearmTimer(nsecs_t now) { - rearmTimerSkippingUpdateFor(now, mCallbacks.end()); + rearmTimerSkippingUpdateFor(now, mCallbacks.cend()); } void VSyncDispatchTimerQueue::rearmTimerSkippingUpdateFor( - nsecs_t now, CallbackMap::iterator const& skipUpdateIt) { + nsecs_t now, CallbackMap::const_iterator skipUpdateIt) { std::optional<nsecs_t> min; std::optional<nsecs_t> targetVsync; std::optional<std::string_view> nextWakeupName; - for (auto it = mCallbacks.begin(); it != mCallbacks.end(); it++) { + for (auto it = mCallbacks.cbegin(); it != mCallbacks.cend(); ++it) { auto& callback = it->second; if (!callback->wakeupTime() && !callback->hasPendingWorkloadUpdate()) { continue; @@ -351,13 +351,12 @@ void VSyncDispatchTimerQueue::timerCallback() { VSyncDispatchTimerQueue::CallbackToken VSyncDispatchTimerQueue::registerCallback( Callback callback, std::string callbackName) { std::lock_guard lock(mMutex); - return CallbackToken{ - mCallbacks - .emplace(++mCallbackToken, - std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName), - std::move(callback), - mMinVsyncDistance)) - .first->first}; + return mCallbacks + .try_emplace(++mCallbackToken, + std::make_shared<VSyncDispatchTimerQueueEntry>(std::move(callbackName), + std::move(callback), + mMinVsyncDistance)) + .first->first; } void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { @@ -367,7 +366,7 @@ void VSyncDispatchTimerQueue::unregisterCallback(CallbackToken token) { auto it = mCallbacks.find(token); if (it != mCallbacks.end()) { entry = it->second; - mCallbacks.erase(it); + mCallbacks.erase(it->first); } } diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h index 3d08410df7..81c746e866 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h @@ -16,14 +16,13 @@ #pragma once -#include <functional> #include <memory> #include <mutex> #include <string> #include <string_view> -#include <unordered_map> #include <android-base/thread_annotations.h> +#include <ftl/small_map.h> #include "VSyncDispatch.h" #include "VsyncSchedule.h" @@ -135,13 +134,14 @@ private: VSyncDispatchTimerQueue(const VSyncDispatchTimerQueue&) = delete; VSyncDispatchTimerQueue& operator=(const VSyncDispatchTimerQueue&) = delete; + // The static capacity was chosen to exceed the expected number of callbacks. using CallbackMap = - std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>; + ftl::SmallMap<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>, 5>; void timerCallback(); void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex); void rearmTimer(nsecs_t now) REQUIRES(mMutex); - void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::iterator const& skipUpdate) + void rearmTimerSkippingUpdateFor(nsecs_t now, CallbackMap::const_iterator skipUpdate) REQUIRES(mMutex); void cancelTimer() REQUIRES(mMutex); ScheduleResult scheduleLocked(CallbackToken, ScheduleTiming) REQUIRES(mMutex); @@ -158,7 +158,7 @@ private: nsecs_t const mTimerSlack; nsecs_t const mMinVsyncDistance; - size_t mCallbackToken GUARDED_BY(mMutex) = 0; + CallbackToken mCallbackToken GUARDED_BY(mMutex); CallbackMap mCallbacks GUARDED_BY(mMutex); nsecs_t mIntendedWakeupTime GUARDED_BY(mMutex) = kInvalidTime; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index 6e12b337ee..8697696915 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -48,14 +48,12 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; VSyncPredictor::VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback& callback) + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) : mId(modePtr->getPhysicalDisplayId()), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), - mVsyncTrackerCallback(callback), mDisplayModePtr(modePtr) { resetModel(); } @@ -312,13 +310,7 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint, FlagManager::getInstance().vrr_config() && !lastFrameMissed && lastVsyncOpt ? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold) : timePoint; - const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime); - if (FlagManager::getInstance().vrr_config() && mDisplayModePtr->getVrrConfig()) { - const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); - const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps(); - mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate); - } - return vsyncTime; + return snapToVsyncAlignedWithRenderRate(baseTime); } nsecs_t VSyncPredictor::snapToVsyncAlignedWithRenderRate(nsecs_t timePoint) const { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index 919100363f..8fd7e6046d 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -37,11 +37,9 @@ public: * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. - * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ VSyncPredictor(ftl::NonNull<DisplayModePtr> modePtr, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, - IVsyncTrackerCallback&); + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); @@ -106,7 +104,6 @@ private: size_t const kHistorySize; size_t const kMinimumSamplesForPrediction; size_t const kOutlierTolerancePercent; - IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; std::optional<nsecs_t> mKnownTimestamp GUARDED_BY(mMutex); diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index 417163f4e1..37bd4b4977 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -26,12 +26,6 @@ namespace android::scheduler { -struct IVsyncTrackerCallback { - virtual ~IVsyncTrackerCallback() = default; - virtual void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) = 0; -}; - /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index 3491600d8e..001938c756 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -57,11 +57,10 @@ private: }; VsyncSchedule::VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags features, - RequestHardwareVsync requestHardwareVsync, - IVsyncTrackerCallback& callback) + RequestHardwareVsync requestHardwareVsync) : mId(modePtr->getPhysicalDisplayId()), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(modePtr, callback)), + mTracker(createTracker(modePtr)), mDispatch(createDispatch(mTracker)), mController(createController(modePtr->getPhysicalDisplayId(), *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) @@ -115,15 +114,14 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr, - IVsyncTrackerCallback& callback) { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(ftl::NonNull<DisplayModePtr> modePtr) { // TODO(b/144707443): Tune constants. constexpr size_t kHistorySize = 20; constexpr size_t kMinSamplesForPrediction = 6; constexpr uint32_t kDiscardOutlierPercent = 20; return std::make_unique<VSyncPredictor>(modePtr, kHistorySize, kMinSamplesForPrediction, - kDiscardOutlierPercent, callback); + kDiscardOutlierPercent); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 6f656d2029..85cd3e7c31 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -57,8 +57,7 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync, - IVsyncTrackerCallback&); + VsyncSchedule(ftl::NonNull<DisplayModePtr> modePtr, FeatureFlags, RequestHardwareVsync); ~VsyncSchedule(); // IVsyncSource overrides: @@ -128,7 +127,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr, IVsyncTrackerCallback&); + static TrackerPtr createTracker(ftl::NonNull<DisplayModePtr> modePtr); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h index 12ee36e084..8673a2251c 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h @@ -47,6 +47,9 @@ struct ICompositor { virtual CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&) = 0; + // Sends a hint about the expected present time + virtual void sendNotifyExpectedPresentHint(PhysicalDisplayId) = 0; + // Samples the composited frame via RegionSamplingThread. virtual void sample() = 0; diff --git a/services/surfaceflinger/Scheduler/src/Timer.cpp b/services/surfaceflinger/Scheduler/src/Timer.cpp index 09e8a1ebe0..eeb9c60d15 100644 --- a/services/surfaceflinger/Scheduler/src/Timer.cpp +++ b/services/surfaceflinger/Scheduler/src/Timer.cpp @@ -159,7 +159,7 @@ bool Timer::dispatch() { ALOGW("Failed to set SCHED_FIFO on dispatch thread"); } - if (pthread_setname_np(pthread_self(), "TimerDispatch")) { + if (pthread_setname_np(pthread_self(), "TimerDispatch") != 0) { ALOGW("Failed to set thread name on dispatch thread"); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1a40bc241f..5bb550834e 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -116,6 +116,7 @@ #include <common/FlagManager.h> #include <gui/LayerStatePermissions.h> #include <gui/SchedulingPolicy.h> +#include <gui/SyncScreenCaptureListener.h> #include <ui/DisplayIdentification.h> #include "BackgroundExecutor.h" #include "Client.h" @@ -800,7 +801,7 @@ void SurfaceFlinger::bootFinished() { void chooseRenderEngineType(renderengine::RenderEngineCreationArgs::Builder& builder) { char prop[PROPERTY_VALUE_MAX]; - property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "skiaglthreaded"); + property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, ""); if (strcmp(prop, "skiagl") == 0) { builder.setThreaded(renderengine::RenderEngine::Threaded::NO) @@ -1082,7 +1083,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info for (const auto& [id, mode] : displayModes) { ui::DisplayMode outMode; - outMode.id = static_cast<int32_t>(id.value()); + outMode.id = ftl::to_underlying(id); auto [width, height] = mode->getResolution(); auto [xDpi, yDpi] = mode->getDpi(); @@ -1131,7 +1132,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info const PhysicalDisplayId displayId = snapshot.displayId(); const auto mode = display->refreshRateSelector().getActiveMode(); - info->activeDisplayModeId = mode.modePtr->getId().value(); + info->activeDisplayModeId = ftl::to_underlying(mode.modePtr->getId()); info->renderFrameRate = mode.fps.getValue(); info->activeColorMode = display->getCompositionDisplay()->getState().colorMode; info->hdrCapabilities = filterOut4k30(display->getHdrCapabilities()); @@ -1147,7 +1148,7 @@ void SurfaceFlinger::getDynamicDisplayInfoInternal(ui::DynamicDisplayInfo*& info if (getHwComposer().hasCapability(Capability::BOOT_DISPLAY_CONFIG)) { if (const auto hwcId = getHwComposer().getPreferredBootDisplayMode(displayId)) { if (const auto modeId = snapshot.translateModeId(*hwcId)) { - info->preferredBootDisplayMode = modeId->value(); + info->preferredBootDisplayMode = ftl::to_underlying(*modeId); } } } @@ -1229,8 +1230,10 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>& displayToken, return NO_ERROR; } -void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool force) { - const auto displayId = request.mode.modePtr->getPhysicalDisplayId(); +void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) { + const auto mode = desiredMode.mode; + const auto displayId = mode.modePtr->getPhysicalDisplayId(); + ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str()); const auto display = getDisplayDeviceLocked(displayId); @@ -1239,10 +1242,9 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& request, bool return; } - const auto mode = request.mode; - const bool emitEvent = request.emitEvent; + const bool emitEvent = desiredMode.emitEvent; - switch (display->setDesiredMode(std::move(request), force)) { + switch (display->setDesiredMode(std::move(desiredMode))) { case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch: // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler. mScheduler->setRenderRate(displayId, @@ -1310,7 +1312,7 @@ status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<display::DisplayToke [](const DisplayModePtr& mode) { return mode->getPeakFps(); }); if (!fpsOpt) { - ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } @@ -1419,16 +1421,17 @@ void SurfaceFlinger::initiateDisplayModeChanges() { if (!displayModePtrOpt) { ALOGW("Desired display mode is no longer supported. Mode ID = %d", - desiredModeId.value()); - dropModeRequest(display); + ftl::to_underlying(desiredModeId)); continue; } - ALOGV("%s changing active mode to %d(%s) for display %s", __func__, desiredModeId.value(), + ALOGV("%s changing active mode to %d(%s) for display %s", __func__, + ftl::to_underlying(desiredModeId), to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(), to_string(display->getId()).c_str()); - if (display->getActiveMode() == desiredModeOpt->mode) { + if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) && + display->getActiveMode() == desiredModeOpt->mode) { applyActiveMode(display); continue; } @@ -1616,7 +1619,7 @@ status_t SurfaceFlinger::setBootDisplayMode(const sp<display::DisplayToken>& dis [](const DisplayModePtr& mode) { return mode->getHwcId(); }); if (!hwcIdOpt) { - ALOGE("%s: Invalid mode %d for display %s", whence, modeId.value(), + ALOGE("%s: Invalid mode %d for display %s", whence, ftl::to_underlying(modeId), to_string(snapshot.displayId()).c_str()); return BAD_VALUE; } @@ -2752,23 +2755,11 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay); } - const TimePoint expectedPresentTime = pacesetterTarget.expectedPresentTime(); // TODO(b/255601557) Update frameInterval per display - refreshArgs.frameInterval = mScheduler->getNextFrameInterval(pacesetterId, expectedPresentTime); + refreshArgs.frameInterval = + mScheduler->getNextFrameInterval(pacesetterId, pacesetterTarget.expectedPresentTime()); refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime(); refreshArgs.hasTrustedPresentationListener = mNumTrustedPresentationListeners > 0; - { - auto& notifyExpectedPresentData = mNotifyExpectedPresentMap[pacesetterId]; - auto lastExpectedPresentTimestamp = TimePoint::fromNs( - notifyExpectedPresentData.lastExpectedPresentTimestamp.load().ns()); - if (expectedPresentTime > lastExpectedPresentTimestamp) { - // If the values are not same, then hint is sent with newer value. - // And because composition always follows the notifyExpectedPresentIfRequired, we can - // skip updating the lastExpectedPresentTimestamp in this case. - notifyExpectedPresentData.lastExpectedPresentTimestamp - .compare_exchange_weak(lastExpectedPresentTimestamp, expectedPresentTime); - } - } // Store the present time just before calling to the composition engine so we could notify // the scheduler. const auto presentTime = systemTime(); @@ -2832,6 +2823,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( scheduleComposite(FrameHint::kNone); } + mNotifyExpectedPresentMap[pacesetterId].hintStatus = NotifyExpectedPresentHintStatus::Start; onCompositionPresented(pacesetterId, frameTargeters, presentTime); const bool hadGpuComposited = @@ -3293,7 +3285,11 @@ void SurfaceFlinger::commitTransactions() { std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( PhysicalDisplayId displayId) const { std::vector<HWComposer::HWCDisplayMode> hwcModes; - std::optional<hal::HWDisplayId> activeModeHwcId; + std::optional<hal::HWConfigId> activeModeHwcIdOpt; + + const bool isExternalDisplay = FlagManager::getInstance().connected_display() && + getHwComposer().getDisplayConnectionType(displayId) == + ui::DisplayConnectionType::External; int attempt = 0; constexpr int kMaxAttempts = 3; @@ -3301,10 +3297,81 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( hwcModes = getHwComposer().getModes(displayId, scheduler::RefreshRateSelector::kMinSupportedFrameRate .getPeriodNsecs()); - activeModeHwcId = getHwComposer().getActiveMode(displayId); + const auto activeModeHwcIdExp = getHwComposer().getActiveMode(displayId); + activeModeHwcIdOpt = activeModeHwcIdExp.value_opt(); + + if (isExternalDisplay && + activeModeHwcIdExp.has_error([](status_t error) { return error == NO_INIT; })) { + constexpr nsecs_t k59HzVsyncPeriod = 16949153; + constexpr nsecs_t k60HzVsyncPeriod = 16666667; + + // DM sets the initial mode for an external display to 1080p@60, but + // this comes after SF creates its own state (including the + // DisplayDevice). For now, pick the same mode in order to avoid + // inconsistent state and unnecessary mode switching. + // TODO (b/318534874): Let DM decide the initial mode. + // + // Try to find 1920x1080 @ 60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod == k60HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // Try to find 1920x1080 @ 59-60 Hz + if (const auto iter = std::find_if(hwcModes.begin(), hwcModes.end(), + [](const auto& mode) { + return mode.width == 1920 && + mode.height == 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod; + }); + iter != hwcModes.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // The display does not support 1080p@60, and this is the last attempt to pick a display + // mode. Prefer 60 Hz if available, with the closest resolution to 1080p. + if (attempt + 1 == kMaxAttempts) { + std::vector<HWComposer::HWCDisplayMode> hwcModeOpts; + + for (const auto& mode : hwcModes) { + if (mode.width <= 1920 && mode.height <= 1080 && + mode.vsyncPeriod >= k60HzVsyncPeriod && + mode.vsyncPeriod <= k59HzVsyncPeriod) { + hwcModeOpts.push_back(mode); + } + } - const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) { - return mode.hwcId == activeModeHwcId; + if (const auto iter = std::max_element(hwcModeOpts.begin(), hwcModeOpts.end(), + [](const auto& a, const auto& b) { + const auto aSize = a.width * a.height; + const auto bSize = b.width * b.height; + if (aSize < bSize) + return true; + else if (aSize == bSize) + return a.vsyncPeriod > b.vsyncPeriod; + else + return false; + }); + iter != hwcModeOpts.end()) { + activeModeHwcIdOpt = iter->hwcId; + break; + } + + // hwcModeOpts was empty, use hwcModes[0] as the last resort + activeModeHwcIdOpt = hwcModes[0].hwcId; + } + } + + const auto isActiveMode = [activeModeHwcIdOpt](const HWComposer::HWCDisplayMode& mode) { + return mode.hwcId == activeModeHwcIdOpt; }; if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) { @@ -3314,7 +3381,7 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( if (attempt == kMaxAttempts) { const std::string activeMode = - activeModeHwcId ? std::to_string(*activeModeHwcId) : "unknown"s; + activeModeHwcIdOpt ? std::to_string(*activeModeHwcIdOpt) : "unknown"s; ALOGE("HWC failed to report an active mode that is supported: activeModeHwcId=%s, " "hwcModes={%s}", activeMode.c_str(), base::Join(hwcModes, ", ").c_str()); @@ -3327,15 +3394,15 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( }) .value_or(DisplayModes{}); - ui::DisplayModeId nextModeId = 1 + - std::accumulate(oldModes.begin(), oldModes.end(), static_cast<ui::DisplayModeId>(-1), - [](ui::DisplayModeId max, const auto& pair) { - return std::max(max, pair.first.value()); - }); + DisplayModeId nextModeId = std::accumulate(oldModes.begin(), oldModes.end(), DisplayModeId(-1), + [](DisplayModeId max, const auto& pair) { + return std::max(max, pair.first); + }); + ++nextModeId; DisplayModes newModes; for (const auto& hwcMode : hwcModes) { - const DisplayModeId id{nextModeId++}; + const auto id = nextModeId++; newModes.try_emplace(id, DisplayMode::Builder(hwcMode.hwcId) .setId(id) @@ -3358,10 +3425,14 @@ std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes( // Keep IDs if modes have not changed. const auto& modes = sameModes ? oldModes : newModes; const DisplayModePtr activeMode = - std::find_if(modes.begin(), modes.end(), [activeModeHwcId](const auto& pair) { - return pair.second->getHwcId() == activeModeHwcId; + std::find_if(modes.begin(), modes.end(), [activeModeHwcIdOpt](const auto& pair) { + return pair.second->getHwcId() == activeModeHwcIdOpt; })->second; + if (isExternalDisplay) { + ALOGI("External display %s initial mode: {%s}", to_string(displayId).c_str(), + to_string(*activeMode).c_str()); + } return {modes, activeMode}; } @@ -3406,8 +3477,12 @@ const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId, auto [displayModes, activeMode] = loadDisplayModes(displayId); if (!activeMode) { - // TODO(b/241286153): Report hotplug failure to the framework. ALOGE("Failed to hotplug display %s", to_string(displayId).c_str()); + if (FlagManager::getInstance().hotplug2()) { + mScheduler->onHotplugConnectionError(mAppConnectionHandle, + static_cast<int32_t>( + DisplayHotplugEvent::ERROR_UNKNOWN)); + } getHwComposer().disconnectDisplay(displayId); return nullptr; } @@ -3666,6 +3741,27 @@ void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken, } mDisplays.try_emplace(displayToken, std::move(display)); + + // For an external display, loadDisplayModes already attempted to select the same mode + // as DM, but SF still needs to be updated to match. + // TODO (b/318534874): Let DM decide the initial mode. + if (const auto& physical = state.physical; + mScheduler && physical && FlagManager::getInstance().connected_display()) { + const bool isInternalDisplay = mPhysicalDisplays.get(physical->id) + .transform(&PhysicalDisplay::isInternal) + .value_or(false); + + if (!isInternalDisplay) { + auto activeModePtr = physical->activeMode; + const auto fps = activeModePtr->getPeakFps(); + + setDesiredMode( + {.mode = scheduler::FrameRateMode{fps, + ftl::as_non_null(std::move(activeModePtr))}, + .emitEvent = false, + .force = true}); + } + } } void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) { @@ -3775,6 +3871,9 @@ void SurfaceFlinger::processDisplayChangesLocked() { mVisibleRegionsDirty = true; mUpdateInputInfo = true; + // Apply the current color matrix to any added or changed display. + mCurrentState.colorMatrixChanged = true; + // find the displays that were removed // (ie: in drawing state but not in current state) // also handle displays that changed @@ -4084,8 +4183,8 @@ void SurfaceFlinger::requestDisplayModes(std::vector<display::DisplayModeRequest if (display->refreshRateSelector().isModeAllowed(request.mode)) { setDesiredMode(std::move(request)); } else { - ALOGV("%s: Mode %d is disallowed for display %s", __func__, modePtr->getId().value(), - to_string(displayId).c_str()); + ALOGV("%s: Mode %d is disallowed for display %s", __func__, + ftl::to_underlying(modePtr->getId()), to_string(displayId).c_str()); } } } @@ -4111,8 +4210,9 @@ void SurfaceFlinger::onChoreographerAttached() { } } -void SurfaceFlinger::onVsyncGenerated(TimePoint expectedPresentTime, - ftl::NonNull<DisplayModePtr> modePtr, Fps renderRate) { +void SurfaceFlinger::onExpectedPresentTimePosted(TimePoint expectedPresentTime, + ftl::NonNull<DisplayModePtr> modePtr, + Fps renderRate) { const auto vsyncPeriod = modePtr->getVsyncRate().getPeriod(); const auto timeoutOpt = [&]() -> std::optional<Period> { const auto vrrConfig = modePtr->getVrrConfig(); @@ -4133,45 +4233,91 @@ void SurfaceFlinger::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt) { - { - auto& data = mNotifyExpectedPresentMap[displayId]; - const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp.load(); - const auto lastFrameInterval = data.lastFrameInterval; - data.lastFrameInterval = frameInterval; - const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); - - const constexpr nsecs_t kOneSecondNs = - std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); - const auto timeout = Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() - : kOneSecondNs); - const bool frameIntervalIsOnCadence = - isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, - lastFrameInterval, timeout, threshold); - - const bool expectedPresentWithinTimeout = - isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, - timeoutOpt, threshold); - - using fps_approx_ops::operator!=; - if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) { - data.lastExpectedPresentTimestamp = expectedPresentTime; - } - - if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + auto& data = mNotifyExpectedPresentMap[displayId]; + const auto lastExpectedPresentTimestamp = data.lastExpectedPresentTimestamp; + const auto lastFrameInterval = data.lastFrameInterval; + data.lastFrameInterval = frameInterval; + data.lastExpectedPresentTimestamp = expectedPresentTime; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const auto timeout = + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 ? timeoutOpt->ns() : kOneSecondNs); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, timeout, threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return; + } + + auto hintStatus = data.hintStatus.load(); + if (!expectedPresentWithinTimeout) { + if (hintStatus != NotifyExpectedPresentHintStatus::Sent || + (timeoutOpt && timeoutOpt->ns() == 0)) { + // Send the hint immediately if timeout, as the hint gets + // delayed otherwise, as the frame is scheduled close + // to the actual present. + if (data.hintStatus + .compare_exchange_strong(hintStatus, + NotifyExpectedPresentHintStatus::ScheduleOnTx)) { + scheduleNotifyExpectedPresentHint(displayId); + } return; } - data.lastExpectedPresentTimestamp = expectedPresentTime; + } + + if (hintStatus != NotifyExpectedPresentHintStatus::Start) { + return; + } + data.hintStatus.store(NotifyExpectedPresentHintStatus::ScheduleOnPresent); + mScheduler->scheduleFrame(); +} + +void SurfaceFlinger::scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + auto itr = mNotifyExpectedPresentMap.find(displayId); + if (itr == mNotifyExpectedPresentMap.end()) { + return; } const char* const whence = __func__; - static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) { - const auto status = getHwComposer().notifyExpectedPresent(displayId, expectedPresentTime, - frameInterval); + const auto sendHint = [=, this]() { + auto& data = mNotifyExpectedPresentMap.at(displayId); + data.hintStatus.store(NotifyExpectedPresentHintStatus::Sent); + const auto status = + getHwComposer().notifyExpectedPresent(displayId, data.lastExpectedPresentTimestamp, + data.lastFrameInterval); if (status != NO_ERROR) { ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, whence, displayId.value); } - })); + }; + + if (itr->second.hintStatus == NotifyExpectedPresentHintStatus::ScheduleOnTx) { + return static_cast<void>(mScheduler->schedule([=, + this]() FTL_FAKE_GUARD(kMainThreadContext) { + auto& data = mNotifyExpectedPresentMap.at(displayId); + auto scheduleHintOnTx = NotifyExpectedPresentHintStatus::ScheduleOnTx; + if (data.hintStatus.compare_exchange_strong(scheduleHintOnTx, + NotifyExpectedPresentHintStatus::Sent)) { + sendHint(); + } + })); + } + sendHint(); +} + +void SurfaceFlinger::sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + if (auto itr = mNotifyExpectedPresentMap.find(displayId); + itr == mNotifyExpectedPresentMap.end() || + itr->second.hintStatus != NotifyExpectedPresentHintStatus::ScheduleOnPresent) { + return; + } + scheduleNotifyExpectedPresentHint(displayId); } void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { @@ -4213,8 +4359,7 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - getFactory(), activeRefreshRate, *mTimeStats, - static_cast<IVsyncTrackerCallback&>(*this)); + getFactory(), activeRefreshRate, *mTimeStats); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); if (FlagManager::getInstance().vrr_config()) { mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); @@ -4273,7 +4418,6 @@ void SurfaceFlinger::doCommitTransactions() { } mDrawingState = mCurrentState; - // clear the "changed" flags in current state mCurrentState.colorMatrixChanged = false; if (mVisibleRegionsDirty) { @@ -5743,7 +5887,7 @@ status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, gui::CreateSurface case ISurfaceComposerClient::eFXSurfaceContainer: case ISurfaceComposerClient::eFXSurfaceBufferState: args.flags |= ISurfaceComposerClient::eNoColorFill; - FMT_FALLTHROUGH; + [[fallthrough]]; case ISurfaceComposerClient::eFXSurfaceEffect: { result = createBufferStateLayer(args, &outResult.handle, &layer); std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter(); @@ -7750,6 +7894,12 @@ void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args kAllowProtected, kGrayscale, captureListener); } +ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) { + sp<SyncScreenCaptureListener> captureListener = sp<SyncScreenCaptureListener>::make(); + captureLayers(args, captureListener); + return captureListener->waitForResults(); +} + void SurfaceFlinger::captureLayers(const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { ATRACE_CALL(); @@ -8338,15 +8488,15 @@ status_t SurfaceFlinger::applyRefreshRateSelectorPolicy( const auto preferredModeId = preferredMode.modePtr->getId(); const Fps preferredFps = preferredMode.fps; - ALOGV("Switching to Scheduler preferred mode %d (%s)", preferredModeId.value(), + ALOGV("Switching to Scheduler preferred mode %d (%s)", ftl::to_underlying(preferredModeId), to_string(preferredFps).c_str()); if (!selector.isModeAllowed(preferredMode)) { - ALOGE("%s: Preferred mode %d is disallowed", __func__, preferredModeId.value()); + ALOGE("%s: Preferred mode %d is disallowed", __func__, ftl::to_underlying(preferredModeId)); return INVALID_OPERATION; } - setDesiredMode({std::move(preferredMode), .emitEvent = true}, force); + setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force}); // Update the frameRateOverride list as the display render rate might have changed if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) { @@ -8430,7 +8580,7 @@ status_t SurfaceFlinger::getDesiredDisplayModeSpecs(const sp<IBinder>& displayTo scheduler::RefreshRateSelector::Policy policy = display->refreshRateSelector().getDisplayManagerPolicy(); - outSpecs->defaultMode = policy.defaultMode.value(); + outSpecs->defaultMode = ftl::to_underlying(policy.defaultMode); outSpecs->allowGroupSwitching = policy.allowGroupSwitching; outSpecs->primaryRanges = translate(policy.primaryRanges); outSpecs->appRequestRanges = translate(policy.appRequestRanges); @@ -9598,6 +9748,12 @@ binder::Status SurfaceComposerAIDL::captureDisplayById( return binderStatusFromStatusT(NO_ERROR); } +binder::Status SurfaceComposerAIDL::captureLayersSync(const LayerCaptureArgs& args, + ScreenCaptureResults* outResults) { + *outResults = mFlinger->captureLayersSync(args); + return binderStatusFromStatusT(NO_ERROR); +} + binder::Status SurfaceComposerAIDL::captureLayers( const LayerCaptureArgs& args, const sp<IScreenCaptureListener>& captureListener) { mFlinger->captureLayers(args, captureListener); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 7cfe46cbea..be057979f9 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -199,8 +199,7 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ICompositor, private scheduler::ISchedulerCallback, - private compositionengine::ICEPowerCallback, - private scheduler::IVsyncTrackerCallback { + private compositionengine::ICEPowerCallback { public: struct SkipInitializationTag {}; @@ -560,6 +559,7 @@ private: void captureDisplay(const DisplayCaptureArgs&, const sp<IScreenCaptureListener>&); void captureDisplay(DisplayId, const CaptureArgs&, const sp<IScreenCaptureListener>&); + ScreenCaptureResults captureLayersSync(const LayerCaptureArgs&); void captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&); status_t getDisplayStats(const sp<IBinder>& displayToken, DisplayStatInfo* stats); @@ -685,14 +685,12 @@ private: void kernelTimerChanged(bool expired) override; void triggerOnFrameRateOverridesChanged() override; void onChoreographerAttached() override; + void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, + Fps renderRate) override; // ICEPowerCallback overrides: void notifyCpuLoadUp() override; - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>, - Fps renderRate) override; - // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); @@ -718,7 +716,7 @@ private: // Show hdr sdr ratio overlay bool mHdrSdrRatioOverlay = false; - void setDesiredMode(display::DisplayModeRequest&&, bool force = false) REQUIRES(mStateLock); + void setDesiredMode(display::DisplayModeRequest&&) REQUIRES(mStateLock); status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps, Fps maxFps); @@ -1481,14 +1479,28 @@ private: ftl::SmallMap<int64_t, sp<SurfaceControl>, 3> mMirrorMapForDebug; // NotifyExpectedPresentHint + enum class NotifyExpectedPresentHintStatus { + // Represents that framework can start sending hint if required. + Start, + // Represents that the hint is already sent. + Sent, + // Represents that the hint will be scheduled with a new frame. + ScheduleOnPresent, + // Represents that a hint will be sent instantly by scheduling on the main thread. + ScheduleOnTx + }; struct NotifyExpectedPresentData { - // lastExpectedPresentTimestamp is read and write from multiple threads such as - // main thread, EventThread, MessageQueue. And is atomic for that reason. - std::atomic<TimePoint> lastExpectedPresentTimestamp{}; + TimePoint lastExpectedPresentTimestamp{}; Fps lastFrameInterval{}; + // hintStatus is read and write from multiple threads such as + // main thread, EventThread. And is atomic for that reason. + std::atomic<NotifyExpectedPresentHintStatus> hintStatus = + NotifyExpectedPresentHintStatus::Start; }; std::unordered_map<PhysicalDisplayId, NotifyExpectedPresentData> mNotifyExpectedPresentMap; - + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) override + REQUIRES(kMainThreadContext); + void scheduleNotifyExpectedPresentHint(PhysicalDisplayId displayId); void notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, TimePoint expectedPresentTime, Fps frameInterval, std::optional<Period> timeoutOpt); @@ -1547,6 +1559,7 @@ public: const sp<IScreenCaptureListener>&) override; binder::Status captureLayers(const LayerCaptureArgs&, const sp<IScreenCaptureListener>&) override; + binder::Status captureLayersSync(const LayerCaptureArgs&, ScreenCaptureResults* results); // TODO(b/239076119): Remove deprecated AIDL. [[deprecated]] binder::Status clearAnimationFrameStats() override { diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp index 5ef22b590b..e125bbefe5 100644 --- a/services/surfaceflinger/common/Android.bp +++ b/services/surfaceflinger/common/Android.bp @@ -18,7 +18,7 @@ cc_defaults { "server_configurable_flags", ], static_libs: [ - "librenderengine", + "librenderengine_includes", ], srcs: [ "FlagManager.cpp", diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp index b07e7ace06..f7adc0e5d6 100644 --- a/services/surfaceflinger/common/FlagManager.cpp +++ b/services/surfaceflinger/common/FlagManager.cpp @@ -108,8 +108,6 @@ void FlagManager::dump(std::string& result) const { DUMP_SERVER_FLAG(use_skia_tracing); /// Trunk stable server flags /// - DUMP_SERVER_FLAG(late_boot_misc2); - DUMP_SERVER_FLAG(dont_skip_on_early); DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display); /// Trunk stable readonly flags /// @@ -130,6 +128,9 @@ void FlagManager::dump(std::string& result) const { DUMP_READ_ONLY_FLAG(enable_layer_command_batching); DUMP_READ_ONLY_FLAG(screenshot_fence_preservation); DUMP_READ_ONLY_FLAG(vulkan_renderengine); + DUMP_READ_ONLY_FLAG(renderable_buffer_usage); + DUMP_READ_ONLY_FLAG(restore_blur_step); + DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro); #undef DUMP_READ_ONLY_FLAG #undef DUMP_SERVER_FLAG #undef DUMP_FLAG_INTERVAL @@ -206,16 +207,11 @@ FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "") FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation") FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan") +FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "") +FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step") +FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "") /// Trunk stable server flags /// -FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "") -/// Exceptions /// -bool FlagManager::dont_skip_on_early() const { - // Even though this is a server writable flag, we do call it before boot completed, but that's - // fine since the decision is done per frame. We can't do caching though. - return flags::dont_skip_on_early(); -} - } // namespace android diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h index 2a30a40730..18f623faff 100644 --- a/services/surfaceflinger/common/include/common/FlagManager.h +++ b/services/surfaceflinger/common/include/common/FlagManager.h @@ -48,8 +48,6 @@ public: bool use_skia_tracing() const; /// Trunk stable server flags /// - bool late_boot_misc2() const; - bool dont_skip_on_early() const; bool refresh_rate_overlay_on_external_display() const; /// Trunk stable readonly flags /// @@ -70,6 +68,9 @@ public: bool enable_layer_command_batching() const; bool screenshot_fence_preservation() const; bool vulkan_renderengine() const; + bool renderable_buffer_usage() const; + bool restore_blur_step() const; + bool dont_skip_on_early_ro() const; protected: // overridden for unit tests diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp index 68237c8dd6..682079f979 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp @@ -143,7 +143,6 @@ private: void invokeComposerHal2_2(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); void invokeComposerHal2_3(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); void invokeComposerHal2_4(Hwc2::AidlComposer*, Display, Hwc2::V2_4::hal::Layer); - void getDisplayVsyncPeriod(); void setActiveModeWithConstraints(); void getDisplayIdentificationData(); void dumpHwc(); @@ -202,11 +201,6 @@ Display DisplayHardwareFuzzer::createVirtualDisplay(Hwc2::AidlComposer* composer return display; } -void DisplayHardwareFuzzer::getDisplayVsyncPeriod() { - nsecs_t outVsyncPeriod; - mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId, &outVsyncPeriod); -} - void DisplayHardwareFuzzer::setActiveModeWithConstraints() { hal::VsyncPeriodChangeTimeline outTimeline; mHwc.setActiveModeWithConstraints(mPhysicalDisplayId, kActiveConfig, {} /*constraints*/, @@ -617,8 +611,7 @@ void DisplayHardwareFuzzer::invokeComposer() { mHwc.getDisplayConnectionType(mPhysicalDisplayId); mHwc.isVsyncPeriodSwitchSupported(mPhysicalDisplayId); - - getDisplayVsyncPeriod(); + mHwc.getDisplayVsyncPeriod(mPhysicalDisplayId); setActiveModeWithConstraints(); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index fa79956131..0d15f6264f 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -228,9 +228,9 @@ public: VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, surfaceflinger::Factory& factory, TimeStats& timeStats, - ISchedulerCallback& callback, IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& callback) : Scheduler(*this, callback, Feature::kContentDetection, factory, - selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { + selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -289,6 +289,8 @@ private: } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} + // MessageQueue overrides: void scheduleFrame() override {} void postMessage(sp<MessageHandler>&& handler) override { handler->handleMessage(Message()); } @@ -396,8 +398,7 @@ public: } // namespace surfaceflinger::test // TODO(b/189053744) : Create a common test/mock library for surfaceflinger -class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback, - private scheduler::IVsyncTrackerCallback { +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; @@ -660,7 +661,6 @@ public: std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, scheduler::ISchedulerCallback* callback = nullptr, - scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr, bool hasMultipleModes = false) { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -675,8 +675,7 @@ public: mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), mRefreshRateSelector, mFactory, *mFlinger->mTimeStats, - *(callback ?: this), - *(vsyncTrackerCallback ?: this)); + *(callback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); @@ -796,9 +795,7 @@ private: void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} - - // IVsyncTrackerCallback overrides - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} + void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index a6b12d0a12..8cd6e1bb3a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -103,6 +103,7 @@ struct EventThreadCallback : public IEventThreadCallback { bool throttleVsync(TimePoint, uid_t) override { return false; } Period getVsyncPeriod(uid_t) override { return kSyncPeriod; } void resync() override {} + void onExpectedPresentTimePosted(TimePoint) override {} }; void SchedulerFuzzer::fuzzEventThread() { @@ -180,21 +181,15 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); } -struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} -}; - void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); - VsyncTrackerCallback callback; const auto mode = ftl::as_non_null( mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(idealPeriod))); scheduler::VSyncPredictor tracker{mode, historySize, minimumSamplesForPrediction, - mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, - callback}; + mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); tracker.setDisplayModePtr(ftl::as_non_null( mock::createDisplayMode(DisplayModeId(0), Fps::fromPeriodNsecs(period)))); diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index fcbef01bfa..f5ec1eeaf2 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -1,4 +1,5 @@ package: "com.android.graphics.surfaceflinger.flags" +container: "system" flag { name: "misc1" @@ -16,13 +17,6 @@ flag { is_fixed_read_only: true } -flag{ - name: "late_boot_misc2" - namespace: "core_graphics" - description: "This flag controls minor miscellaneous SurfaceFlinger changes. Cannot be read before boot finished!" - bug: "297389311" -} - flag { name: "vrr_config" namespace: "core_graphics" @@ -40,13 +34,6 @@ flag { } flag { - name: "dont_skip_on_early" - namespace: "core_graphics" - description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" - bug: "273702768" -} - -flag { name: "multithreaded_present" namespace: "core_graphics" description: "Controls whether to offload present calls to another thread" @@ -174,3 +161,36 @@ flag { bug: "302703346" is_fixed_read_only: true } + +flag { + name: "renderable_buffer_usage" + namespace: "core_graphics" + description: "Decide whether an ExternalTexture isRenderable based on its buffer's usage." + bug: "305445199" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "restore_blur_step" + namespace: "core_graphics" + description: "Restore drawing the blur input prior to drawing blurred content." + bug: "255921628" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "dont_skip_on_early_ro" + namespace: "core_graphics" + description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early" + bug: "273702768" + is_fixed_read_only: true + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 5809ea02f8..da4e47fdec 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -95,6 +95,7 @@ cc_test { "MessageQueueTest.cpp", "PowerAdvisorTest.cpp", "SmallAreaDetectionAllowMappingsTest.cpp", + "SurfaceFlinger_ColorMatrixTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", "SurfaceFlinger_DisplayModeSwitching.cpp", @@ -128,7 +129,6 @@ cc_test { "TransactionTraceWriterTest.cpp", "TransactionTracingTest.cpp", "TunnelModeEnabledReporterTest.cpp", - "StrongTypingTest.cpp", "VSyncCallbackRegistrationTest.cpp", "VSyncDispatchTimerQueueTest.cpp", "VSyncDispatchRealtimeTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h new file mode 100644 index 0000000000..34e4ba5e78 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h @@ -0,0 +1,86 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <compositionengine/Display.h> +#include <compositionengine/mock/DisplaySurface.h> +#include <renderengine/mock/RenderEngine.h> + +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockComposer.h" +#include "mock/DisplayHardware/MockPowerAdvisor.h" +#include "mock/MockTimeStats.h" +#include "mock/system/window/MockNativeWindow.h" + +namespace android { + +// Minimal setup to use TestableSurfaceFlinger::commitAndComposite. +struct CommitAndCompositeTest : testing::Test { + void SetUp() override { + mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); + mComposer = new Hwc2::mock::Composer(); + mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); + mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); + mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); + mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); + + constexpr bool kIsPrimary = true; + FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) + .setPowerMode(hal::PowerMode::ON) + .inject(&mFlinger, mComposer); + auto compostionEngineDisplayArgs = + compositionengine::DisplayCreationArgsBuilder() + .setId(DEFAULT_DISPLAY_ID) + .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) + .setPowerAdvisor(mPowerAdvisor) + .setName("Internal display") + .build(); + auto compositionDisplay = + compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), + std::move(compostionEngineDisplayArgs)); + mDisplay = FakeDisplayDeviceInjector(mFlinger, compositionDisplay, + ui::DisplayConnectionType::Internal, HWC_DISPLAY, + kIsPrimary) + .setDisplaySurface(mDisplaySurface) + .setNativeWindow(mNativeWindow) + .setPowerMode(hal::PowerMode::ON) + .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) + .skipRegisterDisplay() + .inject(); + } + + using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; + using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; + + static constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; + static constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); + static constexpr int DEFAULT_DISPLAY_WIDTH = 1920; + static constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; + + TestableSurfaceFlinger mFlinger; + renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); + sp<DisplayDevice> mDisplay; + sp<compositionengine::mock::DisplaySurface> mDisplaySurface = + sp<compositionengine::mock::DisplaySurface>::make(); + sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); + mock::TimeStats* mTimeStats = new mock::TimeStats(); + Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; + Hwc2::mock::Composer* mComposer = nullptr; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index beb2147c98..7d8a30a727 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -25,6 +25,7 @@ #include <compositionengine/Display.h> #include <compositionengine/mock/DisplaySurface.h> +#include <ftl/future.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> @@ -227,14 +228,6 @@ void CompositionTest::captureScreenComposition() { LayerCase::cleanup(this); } -template <class T> -std::future<T> futureOf(T obj) { - std::promise<T> resultPromise; - std::future<T> resultFuture = resultPromise.get_future(); - resultPromise.set_value(std::move(obj)); - return resultFuture; -} - /* ------------------------------------------------------------------------ * Variants for each display configuration which can be tested */ @@ -327,13 +320,13 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -378,14 +371,14 @@ struct BaseDisplayVariant { .WillRepeatedly([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>&, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.clip); EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace); - return futureOf<FenceResult>(Fence::NO_FENCE); + return ftl::yield<FenceResult>(Fence::NO_FENCE); }); } @@ -578,7 +571,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -586,7 +579,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so gtet the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupREBufferCompositionCommonCallExpectations " @@ -627,7 +621,7 @@ struct BaseLayerProperties { .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -635,7 +629,8 @@ struct BaseLayerProperties { displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " @@ -709,7 +704,7 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> .WillOnce([&](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, const std::shared_ptr<renderengine::ExternalTexture>&, - base::unique_fd&&) -> std::future<FenceResult> { + base::unique_fd&&) -> ftl::Future<FenceResult> { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -717,7 +712,8 @@ struct CommonSecureLayerProperties : public BaseLayerProperties<LayerProperties> displaySettings.clip); // screen capture adds an additional color layer as an alpha // prefill, so get the back layer. - std::future<FenceResult> resultFuture = futureOf<FenceResult>(Fence::NO_FENCE); + ftl::Future<FenceResult> resultFuture = + ftl::yield<FenceResult>(Fence::NO_FENCE); if (layerSettings.empty()) { ADD_FAILURE() << "layerSettings was not expected to be empty in " "setupInsecureREBufferCompositionCommonCallExpectations " diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 13796650c8..fa31643df1 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -80,8 +80,7 @@ void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), TestableSurfaceFlinger::DefaultDisplayMode{displayId}, - TestableSurfaceFlinger::SchedulerCallbackImpl::kMock, - TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock); + TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h index 387d2f26e6..f26336a655 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h @@ -461,9 +461,11 @@ struct HwcDisplayVariant { ? IComposerClient::DisplayConnectionType::INTERNAL : IComposerClient::DisplayConnectionType::EXTERNAL; + using ::testing::AtLeast; EXPECT_CALL(*test->mComposer, getDisplayConnectionType(HWC_DISPLAY_ID, _)) - .WillOnce(DoAll(SetArgPointee<1>(CONNECTION_TYPE), - Return(hal::V2_4::Error::NONE))); + .Times(AtLeast(1)) + .WillRepeatedly(DoAll(SetArgPointee<1>(CONNECTION_TYPE), + Return(hal::V2_4::Error::NONE))); } EXPECT_CALL(*test->mComposer, setClientTargetSlotCount(_)) diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 45db0c56a9..d5ec654c59 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -85,6 +85,7 @@ protected: bool throttleVsync(TimePoint, uid_t) override; Period getVsyncPeriod(uid_t) override; void resync() override; + void onExpectedPresentTimePosted(TimePoint) override; void setupEventThread(); sp<MockEventThreadConnection> createConnection(ConnectionEventRecorder& recorder, @@ -107,6 +108,7 @@ protected: int32_t expectedConfigId, nsecs_t expectedVsyncPeriod); void expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid_t); + void expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime); void expectUidFrameRateMappingEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, std::vector<FrameRateOverride>); @@ -131,6 +133,7 @@ protected: mVSyncCallbackUnregisterRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)(nsecs_t, uid_t)> mThrottleVsyncCallRecorder; + AsyncCallRecorder<void (*)(nsecs_t)> mOnExpectedPresentTimePostedRecorder; ConnectionEventRecorder mConnectionEventCallRecorder{0}; ConnectionEventRecorder mThrottledConnectionEventCallRecorder{0}; @@ -190,6 +193,10 @@ void EventThreadTest::resync() { mResyncCallRecorder.recordCall(); } +void EventThreadTest::onExpectedPresentTimePosted(TimePoint expectedPresentTime) { + mOnExpectedPresentTimePostedRecorder.recordCall(expectedPresentTime.ns()); +} + void EventThreadTest::setupEventThread() { mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); mThread = std::make_unique<impl::EventThread>("EventThreadTest", mVsyncSchedule, @@ -244,6 +251,12 @@ void EventThreadTest::expectThrottleVsyncReceived(nsecs_t expectedTimestamp, uid EXPECT_EQ(uid, std::get<1>(args.value())); } +void EventThreadTest::expectOnExpectedPresentTimePosted(nsecs_t expectedPresentTime) { + auto args = mOnExpectedPresentTimePostedRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPresentTime, std::get<0>(args.value())); +} + void EventThreadTest::expectVsyncEventReceivedByConnection( const char* name, ConnectionEventRecorder& connectionEventRecorder, nsecs_t expectedTimestamp, unsigned expectedCount) { @@ -410,6 +423,7 @@ TEST_F(EventThreadTest, requestNextVsyncPostsASingleVSyncEventToTheConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // EventThread is requesting one more callback due to VsyncRequest::SingleSuppressCallback expectVSyncCallbackScheduleReceived(true); @@ -562,16 +576,19 @@ TEST_F(EventThreadTest, setVsyncRateOnePostsAllEventsToThatConnection) { onVSyncEvent(123, 456, 789); expectThrottleVsyncReceived(456, mConnectionUid); expectVsyncEventReceivedByConnection(123, 1u); + expectOnExpectedPresentTimePosted(456); // A second event should go to the same places. onVSyncEvent(456, 123, 0); expectThrottleVsyncReceived(123, mConnectionUid); expectVsyncEventReceivedByConnection(456, 2u); + expectOnExpectedPresentTimePosted(123); // A third event should go to the same places. onVSyncEvent(789, 777, 111); expectThrottleVsyncReceived(777, mConnectionUid); expectVsyncEventReceivedByConnection(789, 3u); + expectOnExpectedPresentTimePosted(777); } TEST_F(EventThreadTest, setVsyncRateTwoPostsEveryOtherEventToThatConnection) { diff --git a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp index 0c820fb291..299e8aaeca 100644 --- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp @@ -28,6 +28,7 @@ namespace android { +using namespace com::android::graphics::surfaceflinger; using testing::Return; class TestableFlagManager : public FlagManager { @@ -86,26 +87,26 @@ TEST_F(FlagManagerTest, legacyReturnsValue) { TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) { mFlagManager.markBootIncomplete(); - EXPECT_DEATH(FlagManager::getInstance().late_boot_misc2(), ""); + EXPECT_DEATH(FlagManager::getInstance() + .refresh_rate_overlay_on_external_display(), ""); } TEST_F(FlagManagerTest, returnsOverrideTrue) { mFlagManager.markBootCompleted(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, false); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. EXPECT_CALL(mFlagManager, getBoolProperty).WillOnce(Return(true)); - EXPECT_TRUE(mFlagManager.late_boot_misc2()); + EXPECT_TRUE(mFlagManager.refresh_rate_overlay_on_external_display()); // Further calls will not result in further calls to getBoolProperty. - EXPECT_TRUE(mFlagManager.late_boot_misc2()); + EXPECT_TRUE(mFlagManager.refresh_rate_overlay_on_external_display()); } TEST_F(FlagManagerTest, returnsOverrideReadonly) { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace, - false); + SET_FLAG_FOR_TEST(flags::add_sf_skipped_frames_to_trace, false); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. @@ -116,9 +117,7 @@ TEST_F(FlagManagerTest, returnsOverrideReadonly) { TEST_F(FlagManagerTest, returnsOverrideFalse) { mFlagManager.markBootCompleted(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags:: - refresh_rate_overlay_on_external_display, - true); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, true); // This is stored in a static variable, so this test depends on the fact // that this flag has not been read in this process. @@ -129,7 +128,7 @@ TEST_F(FlagManagerTest, returnsOverrideFalse) { TEST_F(FlagManagerTest, ignoresOverrideInUnitTestMode) { mFlagManager.setUnitTestMode(); - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::multithreaded_present, true); + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); // If this has not been called in this process, it will be called. // Regardless, the result is ignored. @@ -144,13 +143,13 @@ TEST_F(FlagManagerTest, returnsValue) { EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, true); - EXPECT_EQ(true, mFlagManager.late_boot_misc2()); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, true); + EXPECT_EQ(true, mFlagManager.refresh_rate_overlay_on_external_display()); } { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::late_boot_misc2, false); - EXPECT_EQ(false, mFlagManager.late_boot_misc2()); + SET_FLAG_FOR_TEST(flags::refresh_rate_overlay_on_external_display, false); + EXPECT_EQ(false, mFlagManager.refresh_rate_overlay_on_external_display()); } } @@ -160,28 +159,14 @@ TEST_F(FlagManagerTest, readonlyReturnsValue) { EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, true); + SET_FLAG_FOR_TEST(flags::misc1, true); EXPECT_EQ(true, mFlagManager.misc1()); } { - SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::misc1, false); + SET_FLAG_FOR_TEST(flags::misc1, false); EXPECT_EQ(false, mFlagManager.misc1()); } } -TEST_F(FlagManagerTest, dontSkipOnEarlyIsNotCached) { - EXPECT_CALL(mFlagManager, getBoolProperty).WillRepeatedly(Return(std::nullopt)); - - const auto initialValue = com::android::graphics::surfaceflinger::flags::dont_skip_on_early(); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(true); - EXPECT_EQ(true, mFlagManager.dont_skip_on_early()); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(false); - EXPECT_EQ(false, mFlagManager.dont_skip_on_early()); - - com::android::graphics::surfaceflinger::flags::dont_skip_on_early(initialValue); -} - } // namespace android diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index a5c0657868..a25804c909 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -104,7 +104,7 @@ TEST_F(HWComposerTest, isHeadless) { TEST_F(HWComposerTest, getActiveMode) { // Unknown display. - EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(PhysicalDisplayId::fromPort(0)), ftl::Unexpected(BAD_INDEX)); constexpr hal::HWDisplayId kHwcDisplayId = 2; expectHotplugConnect(kHwcDisplayId); @@ -117,14 +117,20 @@ TEST_F(HWComposerTest, getActiveMode) { EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(Return(HalError::BAD_DISPLAY)); - EXPECT_EQ(mHwc.getActiveMode(info->id), std::nullopt); + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(UNKNOWN_ERROR)); + } + { + EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) + .WillOnce(Return(HalError::BAD_CONFIG)); + + EXPECT_EQ(mHwc.getActiveMode(info->id), ftl::Unexpected(NO_INIT)); } { constexpr hal::HWConfigId kConfigId = 42; EXPECT_CALL(*mHal, getActiveConfig(kHwcDisplayId, _)) .WillOnce(DoAll(SetArgPointee<1>(kConfigId), Return(HalError::NONE))); - EXPECT_EQ(mHwc.getActiveMode(info->id), kConfigId); + EXPECT_EQ(mHwc.getActiveMode(info->id).value_opt(), kConfigId); } } diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 734fddb2a3..110f324c8b 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -165,12 +165,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableSurfaceFlinger mFlinger; - - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { @@ -1030,6 +1026,7 @@ protected: }; TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) { + SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true); auto layer = createLegacyAndFrontedEndLayer(1); nsecs_t time = systemTime(); @@ -1047,6 +1044,7 @@ TEST_F(SmallAreaDetectionTest, SmallDirtyLayer) { } TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) { + SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true); auto layer = createLegacyAndFrontedEndLayer(1); nsecs_t time = systemTime(); @@ -1064,6 +1062,7 @@ TEST_F(SmallAreaDetectionTest, NotSmallDirtyLayer) { } TEST_F(SmallAreaDetectionTest, smallDirtyLayerWithMatrix) { + SET_FLAG_FOR_TEST(flags::enable_small_area_detection, true); auto layer = createLegacyAndFrontedEndLayer(1); nsecs_t time = systemTime(); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index 9456e37f5c..9b8ff42782 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -146,11 +146,8 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { @@ -649,7 +646,7 @@ TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) { // Second LayerRequirement is the frame rate specification EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitDefault, summarizeLayerHistory(time)[1].vote); EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate); - EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[1].frameRateCategory); + EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory); // layer became inactive, but the vote stays setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic); diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 22cfbd8d8a..c1fa6ac6f2 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -64,10 +64,8 @@ protected: HI_FPS)), DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler(mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback); + TestableScheduler* mScheduler = new TestableScheduler(mSelector, mFlinger, mSchedulerCallback); }; namespace { @@ -215,7 +213,8 @@ TEST_F(LayerInfoTest, getRefreshRateVote_explicitVote) { TEST_F(LayerInfoTest, getRefreshRateVote_explicitVoteWithCategory) { LayerInfo::LayerVote vote = {.type = LayerHistory::LayerVoteType::ExplicitDefault, .fps = 20_Hz, - .category = FrameRateCategory::High}; + .category = FrameRateCategory::High, + .categorySmoothSwitchOnly = true}; layerInfo.setLayerVote(vote); auto actualVotes = @@ -223,10 +222,12 @@ TEST_F(LayerInfoTest, getRefreshRateVote_explicitVoteWithCategory) { ASSERT_EQ(actualVotes.size(), 2u); ASSERT_EQ(actualVotes[0].type, LayerHistory::LayerVoteType::ExplicitCategory); ASSERT_EQ(actualVotes[0].category, vote.category); + ASSERT_TRUE(actualVotes[0].categorySmoothSwitchOnly); ASSERT_EQ(actualVotes[1].type, vote.type); ASSERT_EQ(actualVotes[1].fps, vote.fps); ASSERT_EQ(actualVotes[1].seamlessness, vote.seamlessness); - ASSERT_EQ(actualVotes[1].category, vote.category); + ASSERT_EQ(actualVotes[1].category, FrameRateCategory::Default); + ASSERT_TRUE(actualVotes[1].categorySmoothSwitchOnly); } TEST_F(LayerInfoTest, getRefreshRateVote_explicitCategory) { diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index 13b47ffa4c..3baa48d002 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -1237,4 +1237,25 @@ TEST_F(LayerSnapshotTest, NonVisibleLayerWithInput) { EXPECT_TRUE(foundInputLayer); } +TEST_F(LayerSnapshotTest, canOccludePresentation) { + setFlags(12, layer_state_t::eCanOccludePresentation, layer_state_t::eCanOccludePresentation); + LayerSnapshotBuilder::Args args{.root = mHierarchyBuilder.getHierarchy(), + .layerLifecycleManager = mLifecycleManager, + .includeMetadata = false, + .displays = mFrontEndDisplayInfos, + .displayChanges = false, + .globalShadowSettings = globalShadowSettings, + .supportsBlur = true, + .supportedLayerGenericMetadata = {}, + .genericLayerMetadataKeyMap = {}}; + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + + EXPECT_EQ(getSnapshot(1)->inputInfo.canOccludePresentation, false); + + // ensure we can set the property on the window info for layer and all its children + EXPECT_EQ(getSnapshot(12)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(121)->inputInfo.canOccludePresentation, true); + EXPECT_EQ(getSnapshot(1221)->inputInfo.canOccludePresentation, true); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp index 249ed4017d..f5661fccf5 100644 --- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp @@ -41,6 +41,7 @@ struct NoOpCompositor final : ICompositor { return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) {} } gNoOpCompositor; class TestableMessageQueue : public impl::MessageQueue { diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 1e526ba348..39a8aacaf8 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -103,7 +103,7 @@ struct TestableRefreshRateSelector : RefreshRateSelector { auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; } auto getRankedFrameRates(const std::vector<LayerRequirement>& layers, - GlobalSignals signals) const { + GlobalSignals signals = {}) const { const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals); EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(), @@ -1619,20 +1619,23 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr1.name = "ExplicitCategory HighHint"; lr2.vote = LayerVoteType::NoVote; lr2.name = "NoVote"; - auto actualFrameRateMode = selector.getBestFrameRateMode(layers); + auto actualRankedFrameRates = selector.getRankedFrameRates(layers); // Gets touch boost - EXPECT_EQ(120_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); + // No touch boost, for example a game that uses setFrameRate(30, default compatibility). lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; lr1.name = "ExplicitCategory HighHint"; lr2.vote = LayerVoteType::ExplicitDefault; lr2.desiredRefreshRate = 30_Hz; lr2.name = "30Hz ExplicitDefault"; - actualFrameRateMode = selector.getBestFrameRateMode(layers); - EXPECT_EQ(30_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + actualRankedFrameRates = selector.getRankedFrameRates(layers); + EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; @@ -1640,10 +1643,11 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr2.vote = LayerVoteType::ExplicitCategory; lr2.frameRateCategory = FrameRateCategory::HighHint; lr2.name = "ExplicitCategory HighHint#2"; - actualFrameRateMode = selector.getBestFrameRateMode(layers); + actualRankedFrameRates = selector.getRankedFrameRates(layers); // Gets touch boost - EXPECT_EQ(120_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; @@ -1651,9 +1655,11 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr2.vote = LayerVoteType::ExplicitCategory; lr2.frameRateCategory = FrameRateCategory::Low; lr2.name = "ExplicitCategory Low"; - actualFrameRateMode = selector.getBestFrameRateMode(layers); - EXPECT_EQ(30_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + actualRankedFrameRates = selector.getRankedFrameRates(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; @@ -1661,10 +1667,11 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.desiredRefreshRate = 30_Hz; lr2.name = "30Hz ExplicitExactOrMultiple"; - actualFrameRateMode = selector.getBestFrameRateMode(layers); + actualRankedFrameRates = selector.getRankedFrameRates(layers); // Gets touch boost - EXPECT_EQ(120_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); lr1.vote = LayerVoteType::ExplicitCategory; lr1.frameRateCategory = FrameRateCategory::HighHint; @@ -1672,14 +1679,17 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighH lr2.vote = LayerVoteType::ExplicitExact; lr2.desiredRefreshRate = 30_Hz; lr2.name = "30Hz ExplicitExact"; - actualFrameRateMode = selector.getBestFrameRateMode(layers); + actualRankedFrameRates = selector.getRankedFrameRates(layers); if (selector.supportsAppFrameRateOverrideByContent()) { // Gets touch boost - EXPECT_EQ(120_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId120, + actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch); } else { - EXPECT_EQ(30_Hz, actualFrameRateMode.fps); - EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + EXPECT_EQ(30_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps); + EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId()); + EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch); } } diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 6986689463..b0595257a9 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -95,10 +95,8 @@ protected: kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - mock::VsyncTrackerCallback mVsyncTrackerCallback; TestableSurfaceFlinger mFlinger; - TestableScheduler* mScheduler = - new TestableScheduler{mSelector, mFlinger, mSchedulerCallback, mVsyncTrackerCallback}; + TestableScheduler* mScheduler = new TestableScheduler{mSelector, mFlinger, mSchedulerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder; ConnectionHandle mConnectionHandle; @@ -341,6 +339,61 @@ TEST_F(SchedulerTest, chooseDisplayModesSingleDisplay) { EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, globalSignals)); } +TEST_F(SchedulerTest, chooseDisplayModesSingleDisplayHighHintTouchSignal) { + mScheduler->registerDisplay(kDisplayId1, + std::make_shared<RefreshRateSelector>(kDisplay1Modes, + kDisplay1Mode60->getId())); + + using DisplayModeChoice = TestableScheduler::DisplayModeChoice; + + std::vector<RefreshRateSelector::LayerRequirement> layers = + std::vector<RefreshRateSelector::LayerRequirement>({{.weight = 1.f}, {.weight = 1.f}}); + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + // Scenario that is similar to game. Expects no touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + mScheduler->setContentRequirements(layers); + auto modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + auto choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({60_Hz, kDisplay1Mode60}, {.touch = false})); + + // Scenario that is similar to video playback and interaction. Expects touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExactOrMultiple"; + mScheduler->setContentRequirements(layers); + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, {.touch = true})); + + // Scenario with explicit category and HighHint. Expects touch boost. + lr1.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = RefreshRateSelector::LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::Low; + lr2.name = "ExplicitCategory Low"; + mScheduler->setContentRequirements(layers); + modeChoices = mScheduler->chooseDisplayModes(); + ASSERT_EQ(1u, modeChoices.size()); + choice = modeChoices.get(kDisplayId1); + ASSERT_TRUE(choice); + EXPECT_EQ(choice->get(), DisplayModeChoice({120_Hz, kDisplay1Mode120}, {.touch = true})); +} + TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) { mScheduler->registerDisplay(kDisplayId1, std::make_shared<RefreshRateSelector>(kDisplay1Modes, @@ -516,6 +569,7 @@ TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) { } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} } compositor(*mScheduler); mScheduler->doFrameSignal(compositor, VsyncId(42)); @@ -568,7 +622,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { frameRate.getPeriodNsecs())})); std::shared_ptr<VSyncPredictor> vrrTracker = std::make_shared<VSyncPredictor>(kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback); + kOutlierTolerancePercent); std::shared_ptr<RefreshRateSelector> vrrSelectorPtr = std::make_shared<RefreshRateSelector>(makeModes(kMode), kMode->getId()); TestableScheduler scheduler{std::make_unique<android::mock::VsyncController>(), @@ -576,8 +630,7 @@ TEST_F(SchedulerTest, nextFrameIntervalTest) { vrrSelectorPtr, mFlinger.getFactory(), mFlinger.getTimeStats(), - mSchedulerCallback, - mVsyncTrackerCallback}; + mSchedulerCallback}; scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker); vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate); diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp deleted file mode 100644 index 45b761036a..0000000000 --- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include "Scheduler/StrongTyping.h" - -using namespace testing; - -namespace android { - -TEST(StrongTypeTest, comparison) { - using SpunkyType = StrongTyping<int, struct SpunkyTypeTag, Compare>; - SpunkyType f1(10); - - EXPECT_TRUE(f1 == f1); - EXPECT_TRUE(SpunkyType(10) != SpunkyType(11)); - EXPECT_FALSE(SpunkyType(31) != SpunkyType(31)); - - EXPECT_TRUE(SpunkyType(10) < SpunkyType(11)); - EXPECT_TRUE(SpunkyType(-1) < SpunkyType(0)); - EXPECT_FALSE(SpunkyType(-10) < SpunkyType(-20)); - - EXPECT_TRUE(SpunkyType(10) <= SpunkyType(11)); - EXPECT_TRUE(SpunkyType(10) <= SpunkyType(10)); - EXPECT_TRUE(SpunkyType(-10) <= SpunkyType(1)); - EXPECT_FALSE(SpunkyType(10) <= SpunkyType(9)); - - EXPECT_TRUE(SpunkyType(11) >= SpunkyType(11)); - EXPECT_TRUE(SpunkyType(12) >= SpunkyType(11)); - EXPECT_FALSE(SpunkyType(11) >= SpunkyType(12)); - - EXPECT_FALSE(SpunkyType(11) > SpunkyType(12)); - EXPECT_TRUE(SpunkyType(-11) < SpunkyType(7)); -} - -TEST(StrongTypeTest, addition) { - using FunkyType = StrongTyping<int, struct FunkyTypeTag, Compare, Add>; - FunkyType f2(22); - FunkyType f1(10); - - EXPECT_THAT(f1 + f2, Eq(FunkyType(32))); - EXPECT_THAT(f2 + f1, Eq(FunkyType(32))); - - EXPECT_THAT(++f1.value(), Eq(11)); - EXPECT_THAT(f1.value(), Eq(11)); - EXPECT_THAT(f1++.value(), Eq(11)); - EXPECT_THAT(f1++.value(), Eq(12)); - EXPECT_THAT(f1.value(), Eq(13)); - - auto f3 = f1; - EXPECT_THAT(f1, Eq(f3)); - EXPECT_THAT(f1, Lt(f2)); - - f3 += f1; - EXPECT_THAT(f1.value(), Eq(13)); - EXPECT_THAT(f3.value(), Eq(26)); -} -} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp new file mode 100644 index 0000000000..f127213f0d --- /dev/null +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" + +#include "CommitAndCompositeTest.h" + +#define EXPECT_COLOR_MATRIX_CHANGED(current, drawing) \ + EXPECT_EQ(current, mFlinger.currentState().colorMatrixChanged); \ + EXPECT_EQ(drawing, mFlinger.drawingState().colorMatrixChanged); + +namespace android { + +class ColorMatrixTest : public CommitAndCompositeTest {}; + +TEST_F(ColorMatrixTest, colorMatrixChanged) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.setDaltonizerType(ColorBlindnessType::Deuteranomaly); + EXPECT_COLOR_MATRIX_CHANGED(true, false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) { + EXPECT_COLOR_MATRIX_CHANGED(true, true); + mFlinger.mutableTransactionFlags() |= eTransactionNeeded; + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); + + mFlinger.createDisplay(String8("Test Display"), false); + + mFlinger.commit(); + EXPECT_COLOR_MATRIX_CHANGED(false, true); + + mFlinger.commitAndComposite(); + EXPECT_COLOR_MATRIX_CHANGED(false, false); +} + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp index 8b16a8a480..15a6db626a 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp @@ -21,9 +21,21 @@ #include "mock/DisplayHardware/MockDisplayMode.h" #include "mock/MockDisplayModeSpecs.h" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include <ftl/fake_guard.h> #include <scheduler/Fps.h> +using namespace com::android::graphics::surfaceflinger; + +#define EXPECT_SET_ACTIVE_CONFIG(displayId, modeId) \ + EXPECT_CALL(*mComposer, \ + setActiveConfigWithConstraints(displayId, \ + static_cast<hal::HWConfigId>( \ + ftl::to_underlying(modeId)), \ + _, _)) \ + .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))) + namespace android { namespace { @@ -161,8 +173,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); @@ -170,10 +181,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequ // Verify that next commit will call setActiveConfigWithConstraints in HWC const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); mFlinger.commit(); @@ -202,8 +210,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshR mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), true, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, true, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90); @@ -212,10 +219,7 @@ TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshR // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); EXPECT_CALL(*mAppEventThread, onModeChanged(scheduler::FrameRateMode{90_Hz, ftl::as_non_null(kMode90)})); @@ -238,28 +242,20 @@ TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) { mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90, false, 0, 120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90); mFlinger.commit(); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId120.value(), false, 0, - 180)); + mock::createDisplayModeSpecs(kModeId120, false, 0, 180)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId120.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120); mFlinger.commit(); @@ -281,8 +277,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe mFlinger.onActiveDisplayChanged(nullptr, *mDisplay); mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90_4K.value(), false, 0, - 120)); + mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120)); ASSERT_TRUE(mDisplay->getDesiredMode()); EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K); @@ -291,10 +286,7 @@ TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRe // Verify that next commit will call setActiveConfigWithConstraints in HWC // and complete the mode change. const VsyncPeriodChangeTimeline timeline{.refreshRequired = false}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID, - hal::HWConfigId(kModeId90_4K.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K); EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true)); @@ -331,7 +323,7 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { } if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) { - *result_listener << "Unexpected desired mode " << modeId; + *result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId); return false; } @@ -345,14 +337,15 @@ MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") { MATCHER_P(ModeSettledTo, modeId, "") { if (const auto desiredOpt = arg->getDesiredMode()) { - *result_listener << "Unsettled desired mode " << desiredOpt->mode.modePtr->getId(); + *result_listener << "Unsettled desired mode " + << ftl::to_underlying(desiredOpt->mode.modePtr->getId()); return false; } ftl::FakeGuard guard(kMainThreadContext); if (arg->getActiveMode().modePtr->getId() != modeId) { - *result_listener << "Settled to unexpected active mode " << modeId; + *result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId); return false; } @@ -360,6 +353,13 @@ MATCHER_P(ModeSettledTo, modeId, "") { } TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -376,22 +376,19 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -412,10 +409,7 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); @@ -429,6 +423,12 @@ TEST_F(DisplayModeSwitchingTest, innerXorOuterDisplay) { } TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -447,27 +447,20 @@ TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); - - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); @@ -486,8 +479,8 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); @@ -495,10 +488,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { mDisplay->setPowerMode(hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -512,6 +502,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) { } TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + + // For the inner display, this is handled by setupHwcHotplugCallExpectations. + EXPECT_CALL(*mComposer, getDisplayConnectionType(kOuterDisplayHwcId, _)) + .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayConnectionType::INTERNAL), + Return(hal::V2_4::Error::NONE))); + const auto [innerDisplay, outerDisplay] = injectOuterDisplay(); EXPECT_TRUE(innerDisplay->isPoweredOn()); @@ -530,13 +527,13 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId90.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId90, false, + 0.f, 120.f))); EXPECT_EQ(NO_ERROR, mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(), - mock::createDisplayModeSpecs(kModeId60.value(), - false, 0.f, 120.f))); + mock::createDisplayModeSpecs(kModeId60, false, + 0.f, 120.f))); EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90)); EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60)); @@ -545,10 +542,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { outerDisplay->setPowerMode(hal::PowerMode::OFF); const VsyncPeriodChangeTimeline timeline{.refreshRequired = true}; - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kInnerDisplayHwcId, - hal::HWConfigId(kModeId90.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90); mFlinger.commit(); @@ -567,10 +561,7 @@ TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) { // Only the outer display is powered on. mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay); - EXPECT_CALL(*mComposer, - setActiveConfigWithConstraints(kOuterDisplayHwcId, - hal::HWConfigId(kModeId60.value()), _, _)) - .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE))); + EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60); mFlinger.commit(); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp index 93c282963f..19f8debe84 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_FoldableTest.cpp @@ -17,11 +17,15 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DualDisplayTransactionTest.h" #include <gmock/gmock.h> #include <gtest/gtest.h> +using namespace com::android::graphics::surfaceflinger; + namespace android { namespace { @@ -163,6 +167,7 @@ TEST_F(FoldableTest, requestsHardwareVsyncForBothDisplays) { } TEST_F(FoldableTest, requestVsyncOnPowerOn) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kInnerDisplayId, true)) .Times(1); EXPECT_CALL(mFlinger.scheduler()->mockRequestHardwareVsync, Call(kOuterDisplayId, true)) @@ -173,6 +178,7 @@ TEST_F(FoldableTest, requestVsyncOnPowerOn) { } TEST_F(FoldableTest, disableVsyncOnPowerOffPacesetter) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); // When the device boots, the inner display should be the pacesetter. ASSERT_EQ(mFlinger.scheduler()->pacesetterDisplayId(), kInnerDisplayId); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp index 29acfaa1a5..4e9fba7bda 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp @@ -17,79 +17,16 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerGetDisplayStatsTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> #include <gmock/gmock.h> #include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> #include <ui/DisplayStatInfo.h> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace testing; +#include "CommitAndCompositeTest.h" namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerGetDisplayStatsTest : public Test { -public: - void SetUp() override; - -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerGetDisplayStatsTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +struct SurfaceFlingerGetDisplayStatsTest : CommitAndCompositeTest {}; // TODO (b/277364366): Clients should be updated to pass in the display they want. TEST_F(SurfaceFlingerGetDisplayStatsTest, nullptrSucceeds) { diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp index a270dc91f2..897f9a0319 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp @@ -17,8 +17,14 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h> +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" +using namespace com::android::graphics::surfaceflinger; +using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent; + namespace android { class HotplugTest : public DisplayTransactionTest {}; @@ -87,6 +93,8 @@ TEST_F(HotplugTest, ignoresDuplicateDisconnection) { } TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { + SET_FLAG_FOR_TEST(flags::connected_display, true); + // Inject a primary display. PrimaryDisplayVariant::injectHwcDisplay(this); @@ -94,6 +102,10 @@ TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) { constexpr bool kFailedHotplug = true; ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this); + EXPECT_CALL(*mEventThread, + onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN))) + .Times(1); + // Simulate a connect event that fails to load display modes due to HWC already having // disconnected the display but SF yet having to process the queued disconnect event. EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _)) diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp index 7206e2977d..91b901838c 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyExpectedPresentTest.cpp @@ -17,31 +17,81 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <gui/SurfaceComposerClient.h> #include "DisplayTransactionTestHelpers.h" namespace android { using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; +using android::hardware::graphics::composer::V2_1::Error; class NotifyExpectedPresentTest : public DisplayTransactionTest { public: void SetUp() override { - mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); - FakeHwcDisplayInjector(mDisplay->getPhysicalId(), hal::DisplayType::PHYSICAL, kIsPrimary) + const auto display = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this).inject(); + mPhysicalDisplayId = display->getPhysicalId(); + FakeHwcDisplayInjector(mPhysicalDisplayId, hal::DisplayType::PHYSICAL, /*isPrimary=*/true) .setPowerMode(hal::PowerMode::ON) .inject(&mFlinger, mComposer); + + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(0), + kFps60Hz)); + mCompositor = std::make_unique<Compositor>(mPhysicalDisplayId, mFlinger); } protected: - sp<DisplayDevice> mDisplay; - static constexpr bool kIsPrimary = true; - static constexpr hal::HWDisplayId HWC_DISPLAY_ID = - FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -}; + struct Compositor final : ICompositor { + explicit Compositor(PhysicalDisplayId displayId, TestableSurfaceFlinger& surfaceFlinger) + : displayId(displayId), surfaceFlinger(surfaceFlinger) {} -TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { - const auto physicDisplayId = mDisplay->getPhysicalId(); - auto expectedPresentTime = systemTime() + ms2ns(10); + void sendNotifyExpectedPresentHint(PhysicalDisplayId id) override { + surfaceFlinger.sendNotifyExpectedPresentHint(id); + } + + bool commit(PhysicalDisplayId, const scheduler::FrameTargets&) override { + return committed; + } + + CompositeResultsPerDisplay composite(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& targeters) override { + pacesetterIds.composite = pacesetterId; + CompositeResultsPerDisplay results; + + for (const auto& [id, targeter] : targeters) { + vsyncIds.composite.emplace_back(id, targeter->target().vsyncId()); + surfaceFlinger.resetNotifyExpectedPresentHintState(pacesetterId); + results.try_emplace(id, + CompositeResult{.compositionCoverage = + CompositionCoverage::Hwc}); + } + + return results; + } + + void sample() override {} + void configure() override {} + + struct { + PhysicalDisplayId commit; + PhysicalDisplayId composite; + } pacesetterIds; + + using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>; + struct { + VsyncIds commit; + VsyncIds composite; + } vsyncIds; + + bool committed = true; + PhysicalDisplayId displayId; + TestableSurfaceFlinger& surfaceFlinger; + }; + + PhysicalDisplayId mPhysicalDisplayId; + std::unique_ptr<Compositor> mCompositor; + static constexpr hal::HWDisplayId kHwcDisplayId = + FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; static constexpr Fps kFps60Hz = 60_Hz; static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); static constexpr int32_t kFrameInterval60HzNs = kFps60Hz.getPeriodNsecs(); @@ -49,89 +99,171 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { static constexpr Period kVsyncPeriod = Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); - static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); +}; - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, - kLastExpectedPresentTimestamp, - kFps60Hz)); +TEST_F(NotifyExpectedPresentTest, noNotifyExpectedPresentHintCall_absentTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); + ASSERT_NO_FATAL_FAILURE( + mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, + TimePoint::fromNs(expectedPresentTime), + kFps60Hz)); + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + for (int i = 0; i < 5; i++) { + expectedPresentTime += 2 * kFrameInterval5HzNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + /*timeoutOpt*/ std::nullopt); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + } +} +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentHint_zeroTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); { - // Very first ExpectedPresent after idle, no previous timestamp + // Very first ExpectedPresent after idle, no previous timestamp. EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + + // Present frame + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens and NotifyExpectedPresentHintStatus is start. + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // Absent timeoutNs - expectedPresentTime += 2 * kFrameInterval5HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mCompositor->committed = false; + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, - /*timeoutOpt*/ std::nullopt); + Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Hint sent + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } { - // Timeout is 0 expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, Period::fromNs(0)); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + // Hint is executed + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } +} + +TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentTimeout) { + auto expectedPresentTime = systemTime() + ms2ns(10); { - // ExpectedPresent is after the timeoutNs + // Very first ExpectedPresent after idle, no previous timestamp + mCompositor->committed = false; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(Error::NONE)); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + } + { + // ExpectedPresentTime is after the timeoutNs + mCompositor->committed = true; + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), kFps60Hz, + kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + // Present happens notifyExpectedPresentHintStatus is Start + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + + // Another expectedPresent after timeout expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent has not changed - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { - // ExpectedPresent is after the last reported ExpectedPresent. + // ExpectedPresent is after the last reported ExpectedPresent and within timeout. expectedPresentTime += kFrameInterval60HzNs; - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintStatusIsStart(mPhysicalDisplayId)); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. + mCompositor->committed = false; expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - kFrameInterval60HzNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(Error::NONE)); - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, TimePoint::fromNs(expectedPresentTime), kFps60Hz, kTimeoutNs); + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)); + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)); + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + ASSERT_TRUE(mFlinger.verifyHintIsSent(mPhysicalDisplayId)); } } TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { - const auto physicDisplayId = mDisplay->getPhysicalId(); const auto now = systemTime(); auto expectedPresentTime = now; static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); - ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(physicDisplayId, + ASSERT_NO_FATAL_FAILURE(mFlinger.setNotifyExpectedPresentData(mPhysicalDisplayId, TimePoint::fromNs(now), Fps::fromValue(0))); static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); @@ -147,7 +279,7 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { struct FrameRateIntervalTestData { int32_t frameIntervalNs; - bool callExpectedPresent; + bool callNotifyExpectedPresentHint; }; const std::vector<FrameRateIntervalTestData> frameIntervals = { {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, @@ -159,21 +291,35 @@ TEST_F(NotifyExpectedPresentTest, notifyExpectedPresentRenderRateChanged) { {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, }; - for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { - { - expectedPresentTime += frameIntervalNs; - if (callExpectedPresent) { - EXPECT_CALL(*mComposer, - notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime, - frameIntervalNs)) - .WillOnce(Return(Error::NONE)); - } else { - EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0); - } - mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod, - TimePoint::fromNs(expectedPresentTime), - Fps::fromPeriodNsecs(frameIntervalNs), - kTimeoutNs); + for (size_t i = 0; i < frameIntervals.size(); i++) { + const auto& [frameIntervalNs, callNotifyExpectedPresentHint] = frameIntervals[i]; + expectedPresentTime += frameIntervalNs; + mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + + if (callNotifyExpectedPresentHint) { + mCompositor->committed = false; + ASSERT_TRUE(mFlinger.verifyHintIsScheduledOnPresent(mPhysicalDisplayId)) + << "Hint not scheduled for frameInterval " << frameIntervalNs << " at index " + << i; + EXPECT_CALL(*mComposer, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + .WillOnce(Return(Error::NONE)); + } else { + // Only lastExpectedPresentTime is updated + EXPECT_TRUE( + mFlinger.verifyLastExpectedPresentTime(mPhysicalDisplayId, expectedPresentTime)) + << "LastExpectedPresentTime for frameInterval " << frameIntervalNs + << "at index " << i << " did not match for frameInterval " << frameIntervalNs; + EXPECT_CALL(*mComposer, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); + + if (callNotifyExpectedPresentHint) { + // Present resumes the calls to the notifyExpectedPresentHint. + mCompositor->committed = true; + mFlinger.scheduler()->doFrameSignal(*mCompositor, VsyncId{42}); } } } diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index b80cb6625f..c3934e64f8 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -17,84 +17,18 @@ #undef LOG_TAG #define LOG_TAG "SurfaceFlingerPowerHintTest" -#include <compositionengine/Display.h> -#include <compositionengine/mock/DisplaySurface.h> -#include <gmock/gmock.h> -#include <gtest/gtest.h> -#include <renderengine/mock/RenderEngine.h> -#include <algorithm> #include <chrono> -#include <memory> -#include "TestableSurfaceFlinger.h" -#include "mock/DisplayHardware/MockComposer.h" -#include "mock/DisplayHardware/MockPowerAdvisor.h" -#include "mock/MockTimeStats.h" -#include "mock/system/window/MockNativeWindow.h" -using namespace android; -using namespace android::Hwc2::mock; -using namespace android::hardware::power; +#include "CommitAndCompositeTest.h" + using namespace std::chrono_literals; -using namespace testing; +using testing::_; +using testing::Return; namespace android { namespace { -using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector; -using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector; - -constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID; -constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u); -constexpr int DEFAULT_DISPLAY_WIDTH = 1920; -constexpr int DEFAULT_DISPLAY_HEIGHT = 1024; - -class SurfaceFlingerPowerHintTest : public Test { -public: - void SetUp() override; -protected: - TestableSurfaceFlinger mFlinger; - renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine(); - sp<DisplayDevice> mDisplay; - sp<compositionengine::mock::DisplaySurface> mDisplaySurface = - sp<compositionengine::mock::DisplaySurface>::make(); - sp<mock::NativeWindow> mNativeWindow = sp<mock::NativeWindow>::make(); - mock::TimeStats* mTimeStats = new mock::TimeStats(); - Hwc2::mock::PowerAdvisor* mPowerAdvisor = nullptr; - Hwc2::mock::Composer* mComposer = nullptr; -}; - -void SurfaceFlingerPowerHintTest::SetUp() { - mFlinger.setupMockScheduler({.displayId = DEFAULT_DISPLAY_ID}); - mComposer = new Hwc2::mock::Composer(); - mPowerAdvisor = new Hwc2::mock::PowerAdvisor(); - mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); - mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); - mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); - mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); - static constexpr bool kIsPrimary = true; - FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) - .setPowerMode(hal::PowerMode::ON) - .inject(&mFlinger, mComposer); - auto compostionEngineDisplayArgs = - compositionengine::DisplayCreationArgsBuilder() - .setId(DEFAULT_DISPLAY_ID) - .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT}) - .setPowerAdvisor(mPowerAdvisor) - .setName("injected display") - .build(); - auto compositionDisplay = - compositionengine::impl::createDisplay(mFlinger.getCompositionEngine(), - std::move(compostionEngineDisplayArgs)); - mDisplay = - FakeDisplayDeviceInjector(mFlinger, compositionDisplay, - ui::DisplayConnectionType::Internal, HWC_DISPLAY, kIsPrimary) - .setDisplaySurface(mDisplaySurface) - .setNativeWindow(mNativeWindow) - .setPowerMode(hal::PowerMode::ON) - .setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector()) - .skipRegisterDisplay() - .inject(); -} +class SurfaceFlingerPowerHintTest : public CommitAndCompositeTest {}; TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp index 15fe6008a5..83e2f980ce 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp @@ -17,11 +17,15 @@ #undef LOG_TAG #define LOG_TAG "LibSurfaceFlingerUnittests" +#include <com_android_graphics_surfaceflinger_flags.h> +#include <common/test/FlagUtils.h> #include "DisplayTransactionTestHelpers.h" #include <gmock/gmock.h> #include <gtest/gtest.h> +using namespace com::android::graphics::surfaceflinger; + namespace android { namespace { @@ -453,6 +457,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToUnknownPrimaryDisplay } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToOnExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>(); } @@ -461,6 +466,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOffToDozeSuspendExternalD } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>(); } @@ -473,6 +479,7 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeExternalDisplay) } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>(); } @@ -481,10 +488,12 @@ TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeToOnExternalDisplay) } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToOnExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToOnVariant>>(); } TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToDozeSuspendExternalDisplay) { + SET_FLAG_FOR_TEST(flags::multithreaded_present, true); transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToDozeSuspendVariant>>(); } diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp index e0b736677d..7b92a5bb3b 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp @@ -21,11 +21,10 @@ namespace android::scheduler { TestableScheduler::TestableScheduler(RefreshRateSelectorPtr selectorPtr, TestableSurfaceFlinger& testableSurfaceFlinger, - ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& callback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), std::make_shared<android::mock::VSyncTracker>(), std::move(selectorPtr), testableSurfaceFlinger.getFactory(), - testableSurfaceFlinger.getTimeStats(), callback, vsyncTrackerCallback) {} + testableSurfaceFlinger.getTimeStats(), callback) {} } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 6213713f3a..25a85dfa20 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -41,18 +41,16 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: TestableScheduler(RefreshRateSelectorPtr selectorPtr, - TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback, - IVsyncTrackerCallback& vsyncTrackerCallback); + TestableSurfaceFlinger& testableSurfaceFlinger, ISchedulerCallback& callback); TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, surfaceflinger::Factory& factory, TimeStats& timeStats, - ISchedulerCallback& schedulerCallback, - IVsyncTrackerCallback& vsyncTrackerCallback) + ISchedulerCallback& schedulerCallback) : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - factory, selectorPtr->getActiveMode().fps, timeStats, vsyncTrackerCallback) { + factory, selectorPtr->getActiveMode().fps, timeStats) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); @@ -210,6 +208,7 @@ private: return {}; } void sample() override {} + void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {} }; } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 9ef0749dc6..46a079cfa1 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -53,7 +53,6 @@ #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" -#include "mock/MockVsyncTrackerCallback.h" #include "mock/system/window/MockNativeWindow.h" #include "Scheduler/VSyncTracker.h" @@ -205,8 +204,6 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; - enum class VsyncTrackerCallbackImpl { kNoOp, kMock }; - struct DefaultDisplayMode { // The ID of the injected RefreshRateSelector and its default display mode. PhysicalDisplayId displayId; @@ -220,14 +217,13 @@ public: TimeStats& getTimeStats() { return *mFlinger->mTimeStats; } - void setupScheduler( - std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, - DisplayModesVariant modesVariant, - SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp, - bool useNiceMock = false) { + void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, + std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, + SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, + bool useNiceMock = false) { RefreshRateSelectorPtr selectorPtr = ftl::match( modesVariant, [](DefaultDisplayMode arg) { @@ -245,12 +241,6 @@ public: ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback) : static_cast<ISchedulerCallback&>(mSchedulerCallback); - using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback; - VsyncTrackerCallback& vsyncTrackerCallback = - vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp - ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) - : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); - if (useNiceMock) { mScheduler = new testing::NiceMock<scheduler::TestableScheduler>(std::move(vsyncController), @@ -258,14 +248,12 @@ public: std::move(selectorPtr), mFactory, *mFlinger->mTimeStats, - schedulerCallback, - vsyncTrackerCallback); + schedulerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), mFactory, - *mFlinger->mTimeStats, schedulerCallback, - vsyncTrackerCallback); + *mFlinger->mTimeStats, schedulerCallback); } mScheduler->initVsync(*mTokenManager, 0ms); @@ -307,8 +295,7 @@ public: EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_, _)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, - SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, - options.useNiceMock); + SchedulerCallbackImpl::kNoOp, options.useNiceMock); } void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -698,6 +685,36 @@ public: frameInterval, timeoutOpt); } + void sendNotifyExpectedPresentHint(PhysicalDisplayId displayId) { + ftl::FakeGuard guard(kMainThreadContext); + mFlinger->sendNotifyExpectedPresentHint(displayId); + } + + bool verifyHintIsScheduledOnPresent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnPresent; + } + + bool verifyHintIsSent(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Sent; + } + + bool verifyHintStatusIsStart(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + + bool verifyHintStatusIsScheduledOnTx(PhysicalDisplayId displayId) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus == + SurfaceFlinger::NotifyExpectedPresentHintStatus::ScheduleOnTx; + } + + bool verifyLastExpectedPresentTime(PhysicalDisplayId displayId, nsecs_t expectedPresentTime) { + return mFlinger->mNotifyExpectedPresentMap.at(displayId) + .lastExpectedPresentTimestamp.ns() == expectedPresentTime; + } + void setNotifyExpectedPresentData(PhysicalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, Fps lastFrameInterval) { @@ -706,6 +723,11 @@ public: displayData.lastFrameInterval = lastFrameInterval; } + void resetNotifyExpectedPresentHintState(PhysicalDisplayId displayId) { + mFlinger->mNotifyExpectedPresentMap.at(displayId).hintStatus = + SurfaceFlinger::NotifyExpectedPresentHintStatus::Start; + } + ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does // not report a leaked object, since the SurfaceFlinger instance may @@ -1099,8 +1121,6 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; - scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; Hwc2::mock::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 4bf58de05c..c9a409455d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -734,7 +734,7 @@ TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) { // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); @@ -754,7 +754,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule // b/1450138150 TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); @@ -821,7 +821,7 @@ TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) { } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 600)); @@ -839,7 +839,7 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) } TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -1051,7 +1051,7 @@ TEST_F(VSyncDispatchTimerQueueTest, basicAlarmSettingFutureWithReadyDuration) { } TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -1074,7 +1074,7 @@ TEST_F(VSyncDispatchTimerQueueTest, updatesVsyncTimeForCloseWakeupTime) { } TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); @@ -1098,7 +1098,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { } TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, false); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, false); EXPECT_CALL(mMockClock, alarmAt(_, 500)); CountingCallback cb(mDispatch); @@ -1117,7 +1117,7 @@ TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { } TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { - SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); + SET_FLAG_FOR_TEST(flags::dont_skip_on_early_ro, true); Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index d952b70d4b..b9f3d70c6b 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -26,7 +26,6 @@ #include <common/test/FlagUtils.h> #include "Scheduler/VSyncPredictor.h" #include "mock/DisplayHardware/MockDisplayMode.h" -#include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -82,14 +81,13 @@ struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; ftl::NonNull<DisplayModePtr> mMode = displayMode(mPeriod); - scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; VSyncPredictor tracker{mMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + kOutlierTolerancePercent}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -410,8 +408,7 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { const auto mode = displayMode(mPeriod); - VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent, - mVsyncTrackerCallback}; + VSyncPredictor tracker{mode, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -658,48 +655,6 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } -TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - NotifyExpectedPresentConfig notifyExpectedPresentConfig; - notifyExpectedPresentConfig.timeoutNs = Period::fromNs(30).ns(); - - hal::VrrConfig vrrConfig; - vrrConfig.notifyExpectedPresentConfig = notifyExpectedPresentConfig; - vrrConfig.minFrameIntervalNs = refreshRate.getPeriodNsecs(); - - const int32_t kGroup = 0; - const auto kResolution = ui::Size(1920, 1080); - const auto mode = - ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfig, kGroup, - kResolution, DEFAULT_DISPLAY_ID)); - - tracker.setDisplayModePtr(mode); - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(last + mPeriod), mode, - FpsMatcher(refreshRate))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } - - tracker.setRenderRate(refreshRate / 2); - { - // out of render rate phase - EXPECT_CALL(mVsyncTrackerCallback, - onVsyncGenerated(TimePoint::fromNs(mNow + 3 * mPeriod), mode, - FpsMatcher(refreshRate / 2))) - .Times(1); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), - Eq(mNow + 3 * mPeriod)); - } -} - TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { SET_FLAG_FOR_TEST(flags::vrr_config, true); @@ -716,7 +671,7 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { .build()); VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent, mVsyncTrackerCallback}; + kOutlierTolerancePercent}; vrrTracker.setRenderRate(minFrameRate); vrrTracker.addVsyncTimestamp(0); @@ -733,28 +688,6 @@ TEST_F(VSyncPredictorTest, adjustsVrrTimeline) { EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500)); EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000)); } - -TEST_F(VSyncPredictorTest, absentVrrConfigNoVsyncTrackerCallback) { - SET_FLAG_FOR_TEST(flags::vrr_config, true); - const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - const std::optional<hal::VrrConfig> vrrConfigOpt = std::nullopt; - constexpr int32_t kGroup = 0; - constexpr auto kResolution = ui::Size(1920, 1080); - const auto mode = - ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfigOpt, - kGroup, kResolution, DEFAULT_DISPLAY_ID)); - tracker.setDisplayModePtr(mode); - - auto last = mNow; - for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { - EXPECT_CALL(mVsyncTrackerCallback, onVsyncGenerated(_, _, _)).Times(0); - EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); - mNow += mPeriod; - last = mNow; - tracker.addVsyncTimestamp(mNow); - } -} - } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h index e298e7cd3d..685d8f9e95 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h @@ -24,7 +24,7 @@ inline DisplayMode::Builder createDisplayModeBuilder( DisplayModeId modeId, Fps displayRefreshRate, int32_t group = 0, ui::Size resolution = ui::Size(1920, 1080), PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) { - return DisplayMode::Builder(hal::HWConfigId(modeId.value())) + return DisplayMode::Builder(hal::HWConfigId(ftl::to_underlying(modeId))) .setId(modeId) .setPhysicalDisplayId(displayId) .setVsyncPeriod(displayRefreshRate.getPeriodNsecs()) diff --git a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h index a71e82cc75..7b18a82283 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDisplayModeSpecs.h @@ -18,12 +18,15 @@ #include <android/gui/DisplayModeSpecs.h> +#include "DisplayHardware/DisplayMode.h" + namespace android::mock { -inline gui::DisplayModeSpecs createDisplayModeSpecs(int32_t defaultMode, bool allowGroupSwitching, - float minFps, float maxFps) { +inline gui::DisplayModeSpecs createDisplayModeSpecs(DisplayModeId defaultMode, + bool allowGroupSwitching, float minFps, + float maxFps) { gui::DisplayModeSpecs specs; - specs.defaultMode = defaultMode; + specs.defaultMode = ftl::to_underlying(defaultMode); specs.allowGroupSwitching = allowGroupSwitching; specs.primaryRanges.physical.min = minFps; specs.primaryRanges.physical.max = maxFps; diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h index 22b2cccf03..4ca05423d7 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h +++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h @@ -28,6 +28,8 @@ struct SchedulerCallback final : ISchedulerCallback { MOCK_METHOD(void, kernelTimerChanged, (bool), (override)); MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override)); MOCK_METHOD(void, onChoreographerAttached, (), (override)); + MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), + (override)); }; struct NoOpSchedulerCallback final : ISchedulerCallback { @@ -36,6 +38,7 @@ struct NoOpSchedulerCallback final : ISchedulerCallback { void kernelTimerChanged(bool) override {} void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} + void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {} }; } // namespace android::scheduler::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h deleted file mode 100644 index b48529f4ac..0000000000 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include <gmock/gmock.h> - -#include "Scheduler/VSyncTracker.h" - -namespace android::scheduler::mock { - -struct VsyncTrackerCallback final : IVsyncTrackerCallback { - MOCK_METHOD(void, onVsyncGenerated, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps), (override)); -}; - -struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { - void onVsyncGenerated(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override{}; -}; -} // namespace android::scheduler::mock diff --git a/services/vibratorservice/benchmarks/Android.bp b/services/vibratorservice/benchmarks/Android.bp index a468146bc0..5437995899 100644 --- a/services/vibratorservice/benchmarks/Android.bp +++ b/services/vibratorservice/benchmarks/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_haptics_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp index 971a0b960e..e11a809bc1 100644 --- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp +++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp @@ -152,6 +152,7 @@ BENCHMARK_WRAPPER(VibratorBench, off, { BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -180,6 +181,7 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitude, { BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -200,6 +202,7 @@ BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, { BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -221,6 +224,7 @@ BENCHMARK_WRAPPER(VibratorBench, setExternalControl, { BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -239,6 +243,7 @@ BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, { BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, { if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } @@ -331,9 +336,11 @@ protected: BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -357,9 +364,11 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, { BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -384,6 +393,7 @@ BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, { BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, { if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -441,9 +451,11 @@ protected: BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) { + state.SkipWithMessage("missing capability"); return; } if (!hasArgs(state)) { + state.SkipWithMessage("missing args"); return; } @@ -468,4 +480,4 @@ BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, { } }); -BENCHMARK_MAIN();
\ No newline at end of file +BENCHMARK_MAIN(); diff --git a/services/vibratorservice/test/Android.bp b/services/vibratorservice/test/Android.bp index 32947246d8..be71dc2ce3 100644 --- a/services/vibratorservice/test/Android.bp +++ b/services/vibratorservice/test/Android.bp @@ -13,6 +13,7 @@ // limitations under the License. package { + default_team: "trendy_team_haptics_framework", // See: http://go/android-license-faq // A large-scale-change added 'default_applicable_licenses' to import // all of the 'license_kinds' from "frameworks_native_license" diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 867fc45516..13141933ac 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "vulkan/vulkan_core.h" #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <aidl/android/hardware/graphics/common/Dataspace.h> @@ -158,6 +159,25 @@ int InvertTransformToNative(VkSurfaceTransformFlagBitsKHR transform) { } } +const static VkColorSpaceKHR colorSpaceSupportedByVkEXTSwapchainColorspace[] = { + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT, + VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT, + VK_COLOR_SPACE_BT709_LINEAR_EXT, + VK_COLOR_SPACE_BT709_NONLINEAR_EXT, + VK_COLOR_SPACE_BT2020_LINEAR_EXT, + VK_COLOR_SPACE_HDR10_ST2084_EXT, + VK_COLOR_SPACE_HDR10_HLG_EXT, + VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT, + VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT, + VK_COLOR_SPACE_PASS_THROUGH_EXT, + VK_COLOR_SPACE_DCI_P3_LINEAR_EXT}; + +const static VkColorSpaceKHR + colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly[] = { + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT, + VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}; + class TimingInfo { public: TimingInfo(const VkPresentTimeGOOGLE* qp, uint64_t nativeFrameId) @@ -745,16 +765,22 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, }; if (colorspace_ext) { - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); - all_formats.emplace_back(VkSurfaceFormatKHR{ - VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace(colorSpace, GetNativePixelFormat( + VK_FORMAT_R8G8B8A8_UNORM)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_UNORM, colorSpace}); + } + + if (GetNativeDataspace(colorSpace, GetNativePixelFormat( + VK_FORMAT_R8G8B8A8_SRGB)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back( + VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_SRGB, colorSpace}); + } + } } // NOTE: Any new formats that are added must be coordinated across different @@ -766,9 +792,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R5G6B5_UNORM_PACK16, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R5G6B5_UNORM_PACK16)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R5G6B5_UNORM_PACK16, colorSpace}); + } + } } } @@ -777,15 +810,28 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, all_formats.emplace_back(VkSurfaceFormatKHR{ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT, - VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + } + } + + for ( + VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace}); + } + } } } @@ -795,12 +841,16 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, GetNativePixelFormat( + VK_FORMAT_A2B10G10R10_UNORM_PACK32)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_A2B10G10R10_UNORM_PACK32, colorSpace}); + } + } } } @@ -833,12 +883,18 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}); if (colorspace_ext) { - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_COLOR_SPACE_PASS_THROUGH_EXT}); - all_formats.emplace_back( - VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, - VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT}); + for (VkColorSpaceKHR colorSpace : + colorSpaceSupportedByVkEXTSwapchainColorspace) { + if (GetNativeDataspace( + colorSpace, + GetNativePixelFormat( + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)) != + DataSpace::UNKNOWN) { + all_formats.emplace_back(VkSurfaceFormatKHR{ + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + colorSpace}); + } + } } } |