Merge "InputReader: dump orientation as a string" into main
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 481c28e..5ee6b15 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1563,6 +1563,13 @@
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 0000000..5d16d51
--- /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 7478f29..4486bd6 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -4017,24 +4017,37 @@
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));
- struct stat stFromPath;
- if (fstat(rfd.get(), &stFromPath) < 0) {
- *_aidl_return = errno;
+ 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 (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 f2b578a..023491f 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -194,6 +194,12 @@
});
}
+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 @@
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_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_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 @@
// 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 @@
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 6115da7..5cdcb23 100644
--- a/cmds/lshal/Lshal.cpp
+++ b/cmds/lshal/Lshal.cpp
@@ -232,6 +232,11 @@
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 7cffcf8..1576486 100644
--- a/cmds/lshal/NullableOStream.h
+++ b/cmds/lshal/NullableOStream.h
@@ -59,6 +59,11 @@
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 e00c2a2..3897197 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -95,6 +95,16 @@
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 0000000..b080939
--- /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 a401838..a828b52 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 @@
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 @@
}
};
+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 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 @@
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::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 @@
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 0000000..39d20b0
--- /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 97e500d..b575053 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -361,6 +361,24 @@
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 72ab732..2a55370 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 95b8110..beb69f8 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 855b110..4c9932d 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/keycodes.h b/include/android/keycodes.h
index f8fb256..79cdbca 100644
--- a/include/android/keycodes.h
+++ b/include/android/keycodes.h
@@ -839,6 +839,10 @@
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 08d339b..3c82d88 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -233,11 +233,11 @@
* @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 @@
*
* @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 @@
*
* @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 0000000..12b6102
--- /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 bacd1b2..e601251 100644
--- a/include/ftl/fake_guard.h
+++ b/include/ftl/fake_guard.h
@@ -85,6 +85,5 @@
#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 49cde7f..83d5967 100644
--- a/include/ftl/small_map.h
+++ b/include/ftl/small_map.h
@@ -107,12 +107,20 @@
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 @@
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 11294c3..43e9fac 100644
--- a/include/ftl/small_vector.h
+++ b/include/ftl/small_vector.h
@@ -124,30 +124,29 @@
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 @@
//
// 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 b7751f7..57b659d 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -75,6 +75,17 @@
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 @@
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 @@
return mKeyboardLayoutInfo;
}
+ inline const InputDeviceViewBehavior& getViewBehavior() const { return mViewBehavior; }
+
inline void setKeyCharacterMap(const std::shared_ptr<KeyCharacterMap> value) {
mKeyCharacterMap = value;
}
@@ -359,6 +373,8 @@
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 2d64872..42dcd3c 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 @@
* 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 @@
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 @@
/* 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 @@
*/
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;
+ static std::unique_ptr<InputChannel> create(const std::string& name,
+ android::base::unique_fd fd, sp<IBinder> token);
- std::string mName;
- base::unique_fd mFd;
-
- sp<IBinder> mToken;
+ InputChannel(const std::string name, android::base::unique_fd fd, sp<IBinder> token);
};
/*
@@ -357,7 +342,7 @@
~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/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 7de94e3..fb2781b 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -492,6 +492,7 @@
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 525ba2e..de2a69f 100644
--- a/libs/binder/RecordedTransaction.cpp
+++ b/libs/binder/RecordedTransaction.cpp
@@ -114,8 +114,8 @@
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 @@
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 @@
DATA_PARCEL_CHUNK = 2,
REPLY_PARCEL_CHUNK = 3,
INTERFACE_NAME_CHUNK = 4,
+ DATA_PARCEL_OBJECT_CHUNK = 5,
END_CHUNK = 0x00ffffff,
};
@@ -265,21 +275,30 @@
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 @@
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 @@
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/Parcel.h b/libs/binder/include/binder/Parcel.h
index 09da6e3..d7096d8 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -55,6 +55,9 @@
class TextOutput;
namespace binder {
class Status;
+namespace debug {
+class RecordedTransaction;
+}
}
class Parcel {
@@ -1443,6 +1446,9 @@
// 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 505c199..f0bee7f 100644
--- a/libs/binder/include/binder/RecordedTransaction.h
+++ b/libs/binder/include/binder/RecordedTransaction.h
@@ -50,6 +50,7 @@
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 @@
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/tests/Android.bp b/libs/binder/tests/Android.bp
index dd2be94..aba2319 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -69,10 +69,14 @@
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,9 @@
enabled: true,
platform_apis: true,
},
+ ndk: {
+ enabled: false,
+ },
},
}
diff --git a/libs/binder/tests/IBinderRecordReplayTest.aidl b/libs/binder/tests/IBinderRecordReplayTest.aidl
index bd6b03c..29267e9 100644
--- a/libs/binder/tests/IBinderRecordReplayTest.aidl
+++ b/libs/binder/tests/IBinderRecordReplayTest.aidl
@@ -69,4 +69,10 @@
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/binderRecordReplayTest.cpp b/libs/binder/tests/binderRecordReplayTest.cpp
index 73c0a94..b975fad 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 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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
&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 83db6c9..fbab8f0 100644
--- a/libs/binder/tests/parcel_fuzzer/Android.bp
+++ b/libs/binder/tests/parcel_fuzzer/Android.bp
@@ -131,6 +131,13 @@
"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_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 @@
"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 8fc9263..7a1688b 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 8a1fecb..f41c35b 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;
-
- // 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;
-
- // 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);
-
- return OK;
+ if (mProvider.ConsumeBool()) {
+ return mProvider.ConsumeIntegral<status_t>();
}
-private:
- String16 mDescriptor;
+ if (reply == nullptr) return OK;
- // note may not all be used
- std::vector<uint8_t> mBytes;
- FuzzedDataProvider mProvider;
-};
+ // 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;
+
+ // 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);
+
+ 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 4e58dc4..62b8433 100644
--- a/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
+++ b/libs/binder/tests/parcel_fuzzer/random_parcel.cpp
@@ -73,7 +73,7 @@
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 @@
}
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 7b3c806..fd9777a 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 @@
} // 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 @@
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/ftl/Android.bp b/libs/ftl/Android.bp
index 918680d..5ac965f 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -10,11 +10,15 @@
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 0000000..8cb07e4
--- /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 634877f..e96d70d 100644
--- a/libs/ftl/small_map_test.cpp
+++ b/libs/ftl/small_map_test.cpp
@@ -189,9 +189,20 @@
}
}
-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 @@
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 @@
} // 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 @@
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 @@
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 @@
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/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 19693e3..fb69fda 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -887,6 +887,9 @@
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 @@
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 @@
// 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 @@
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::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, 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 7d37fd3..4518b67 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -116,7 +116,7 @@
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 38fab9c..7564c26 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -199,7 +199,7 @@
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 @@
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/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 95b2641..9429d2c 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -109,7 +109,8 @@
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 @@
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 @@
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 @@
<< "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/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 6b8e824..920310e 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -161,6 +161,9 @@
// 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/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index e72fd59..32d60be 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -246,6 +246,10 @@
// 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/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index b9ab803..a9d6e8d 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -63,8 +63,7 @@
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 @@
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/input/Android.bp b/libs/input/Android.bp
index 3d3bf13..e5fb2c5 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -175,6 +175,7 @@
],
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 8eaff00..0d29b4d 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -374,7 +374,7 @@
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 @@
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 9c7c0c1..d4dbc45 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -190,14 +190,16 @@
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 @@
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 0e627e5..8db0ca5 100644
--- a/libs/input/InputEventLabels.cpp
+++ b/libs/input/InputEventLabels.cpp
@@ -348,7 +348,9 @@
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 37de00c..e49f4eb 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -95,6 +95,21 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
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 @@
}
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();
+void InputChannel::copyTo(android::os::InputChannelCore& outChannel) const {
+ outChannel.name = getName();
+ outChannel.fd.reset(dupChannelFd(fd.get()));
+ outChannel.token = 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);
-}
-
-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 00ebd4d..c1aacfb 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
similarity index 72%
rename from libs/input/android/InputChannel.aidl
rename to libs/input/android/os/InputChannelCore.aidl
index c2d1112..888a553 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 0c850fe..ec7a284 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -26,6 +26,13 @@
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 +111,10 @@
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 867af79..b1d7760 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -79,7 +79,7 @@
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 650c930..60feb53 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 @@
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, 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 2000335..3543020 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -220,7 +220,6 @@
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 @@
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 @@
};
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/renderengine/Android.bp b/libs/renderengine/Android.bp
index ba2eb7d..fd45840 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -48,11 +48,19 @@
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 55c34cd..87e21c2 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -37,6 +37,7 @@
static_libs: [
"librenderengine",
"libshaders",
+ "libsurfaceflinger_common",
"libtonemap",
],
cflags: [
@@ -54,6 +55,7 @@
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
data: ["resources/*"],
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 330cc66..6e393f0 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 @@
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 @@
// 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())) {
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 3af85c0..bff12ce 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 @@
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 @@
VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr;
VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr;
VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr;
+ VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr;
GrVkGetProc grGetProc;
bool isProtected;
bool isRealtimePriority;
@@ -100,6 +110,8 @@
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 @@
}
};
+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 @@
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 @@
delete interface->physicalDeviceFeatures2;
}
+ if (interface->deviceFaultFeatures) {
+ delete interface->deviceFaultFeatures;
+ }
+
interface->samplerYcbcrConversionFeatures = nullptr;
interface->physicalDeviceFeatures2 = nullptr;
interface->protectedMemoryFeatures = nullptr;
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 50e166d..473e1d4 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -44,6 +44,7 @@
"librenderengine_mocks",
"libshaders",
"libtonemap",
+ "libsurfaceflinger_common",
],
header_libs: [
"libtonemap_headers",
@@ -61,5 +62,6 @@
"libsync",
"libui",
"libutils",
+ "server_configurable_flags",
],
}
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
index a8b38e9..ef4d737 100644
--- a/libs/sensor/libsensor_flags.aconfig
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -2,7 +2,7 @@
flag {
name: "sensormanager_ping_binder"
- namespace: "libsensors"
+ namespace: "sensors"
description: "Whether to pingBinder on SensorManager init"
bug: "322228259"
is_fixed_read_only: true
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index 7fd9c3a..f73fa5d 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -22,8 +22,10 @@
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 @@
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 951ecff..695b571 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -20,6 +20,7 @@
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 b2ea041..ea55179 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 6dffac5..8e452fb 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 523bc57..75e1704 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 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 be8b3e3..f1f0ac5 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 @@
{
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 @@
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;
- }
+// 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);
- if (cls == egldisplayClass &&
- (EGLDisplay)handle == EGL_NO_DISPLAY) {
- return eglNoDisplayObject;
- }
-
- if (cls == eglsurfaceClass &&
- (EGLSurface)handle == EGL_NO_SURFACE) {
- return eglNoSurfaceObject;
- }
-
- 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 85f743d..78b0819 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 dd17ca4..1fa9275 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 dd17ca4..1fa9275 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 dd17ca4..1fa9275 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 dd17ca4..1fa9275 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 b2bbdf6..4004a7d 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 b039bc9..c5bdf32 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 dd00e92..2260a80 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 88e00be..130612d 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 3e7ec8b..5446fc2 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 9cab1d6..c3534bf 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 a7955cf..d244b1a 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 @@
],
static_libs: [
"libattestation",
+ "libperfetto_client_experimental",
"libpalmrejection",
"libui-types",
],
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 4863513..ae066c0 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -260,13 +260,16 @@
}
// 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 @@
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 df944ef..c479aaf 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -136,7 +136,8 @@
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 e200f8b..2d12574 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 c7bacee..6d71acc 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 @@
"Monitor.cpp",
"TouchedWindow.cpp",
"TouchState.cpp",
+ "trace/*.cpp",
],
}
@@ -72,6 +74,7 @@
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 f304712..9dee66f 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 c17baea..a834a8c 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -42,7 +42,6 @@
};
Status status;
- std::shared_ptr<InputChannel> inputChannel; // never null
bool monitor;
InputPublisher inputPublisher;
InputState inputState;
@@ -59,12 +58,14 @@
// 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 2153d8a..264dc03 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -282,9 +282,10 @@
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 @@
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 a915805..1298b5d 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 @@
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 @@
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 d50e2b8..6719006 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 {
+// 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 @@
// 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 @@
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> 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 @@
// 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 @@
}
}
+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 @@
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 @@
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 @@
}
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 @@
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 @@
* 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 @@
}
}
- // 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 @@
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::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 @@
}
}
- 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 @@
return;
}
InputTarget target;
- target.inputChannel = channel;
+ target.connection = connection;
entry->dispatchInProgress = true;
dispatchEventLocked(currentTime, entry, {target});
@@ -1711,12 +1735,12 @@
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;
@@ -1994,12 +2018,12 @@
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});
}
@@ -2052,17 +2076,8 @@
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);
}
}
@@ -2073,12 +2088,24 @@
// 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() {
@@ -2135,7 +2162,8 @@
// 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;
}
@@ -2256,17 +2284,11 @@
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;
@@ -2643,7 +2665,7 @@
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;
}
@@ -2841,13 +2863,13 @@
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;
@@ -2871,8 +2893,7 @@
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();
@@ -2912,8 +2933,7 @@
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
@@ -2963,7 +2983,7 @@
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()) {
@@ -3334,10 +3354,17 @@
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.
@@ -3456,7 +3483,7 @@
<< 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.
@@ -3477,7 +3504,7 @@
}
dispatchPointerDownOutsideFocus(resolvedMotion->source, resolvedMotion->action,
- inputTarget.inputChannel->getConnectionToken());
+ inputTarget.connection->getToken());
break;
}
@@ -3560,13 +3587,9 @@
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);
}
@@ -3586,7 +3609,7 @@
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()) {
@@ -3800,7 +3823,7 @@
connection->outboundQueue.erase(connection->outboundQueue.begin());
traceOutboundQueueLength(*connection);
if (connection->responsive) {
- mAnrTracker.insert(timeoutTime, connection->inputChannel->getConnectionToken());
+ mAnrTracker.insert(timeoutTime, connection->getToken());
}
traceWaitQueueLength(*connection);
}
@@ -3890,7 +3913,7 @@
auto command = [this, connection]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
- mPolicy.notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
+ mPolicy.notifyInputChannelBroken(connection->getToken());
};
postCommandLocked(std::move(command));
}
@@ -3948,10 +3971,9 @@
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;
@@ -3972,8 +3994,7 @@
} 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",
@@ -3982,38 +4003,84 @@
}
// 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;
}
@@ -4040,8 +4107,8 @@
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]);
@@ -4050,10 +4117,7 @@
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 {
@@ -4064,11 +4128,7 @@
}
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++) {
@@ -4142,8 +4202,12 @@
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) {
@@ -4161,8 +4225,8 @@
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;
@@ -4197,17 +4261,6 @@
}
}
-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) {
@@ -4901,7 +4954,7 @@
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;
}
}
@@ -5119,15 +5172,6 @@
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()) {
@@ -5147,7 +5191,7 @@
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 =
@@ -5226,6 +5270,7 @@
// 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);
@@ -5234,7 +5279,7 @@
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
- onFocusChangedLocked(*changes);
+ onFocusChangedLocked(*changes, removedFocusedWindowHandle);
}
std::unordered_map<int32_t, TouchState>::iterator stateIt =
@@ -5246,19 +5291,16 @@
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);
@@ -5356,15 +5398,16 @@
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;
@@ -5544,9 +5587,10 @@
}
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;
}
@@ -5559,7 +5603,6 @@
// 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.
@@ -5591,7 +5634,7 @@
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);
@@ -5826,11 +5869,10 @@
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));
@@ -5884,8 +5926,8 @@
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";
}
}
@@ -5915,20 +5957,20 @@
{ // 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
@@ -5940,9 +5982,9 @@
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;
}
@@ -5955,21 +5997,23 @@
<< " 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);
}
@@ -6008,7 +6052,7 @@
removeMonitorChannelLocked(connectionToken);
}
- mLooper->removeFd(connection->inputChannel->getFd().get());
+ mLooper->removeFd(connection->inputPublisher.getChannel().getFd());
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
@@ -6021,7 +6065,7 @@
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()) {
@@ -6038,8 +6082,8 @@
}
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;
@@ -6072,16 +6116,14 @@
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;
@@ -6147,7 +6189,7 @@
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;
}
}
@@ -6179,8 +6221,8 @@
}
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,
@@ -6202,12 +6244,11 @@
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);
}
@@ -6228,7 +6269,7 @@
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);
@@ -6239,9 +6280,18 @@
}
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));
}
@@ -6275,7 +6325,7 @@
// 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;
}
/**
@@ -6290,10 +6340,10 @@
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));
@@ -6392,15 +6442,15 @@
*/
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) {
@@ -6414,7 +6464,7 @@
* 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);
@@ -6465,22 +6515,26 @@
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);
}
@@ -6510,8 +6564,8 @@
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;
@@ -6556,10 +6610,13 @@
}
}
- 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;
@@ -6619,7 +6676,8 @@
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());
}
}
@@ -6627,7 +6685,8 @@
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());
}
}
@@ -6696,15 +6755,19 @@
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 e635852..f2fd0ca 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -369,8 +369,6 @@
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 @@
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 @@
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 @@
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 c02c5d6..28e3c6d 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -83,9 +83,9 @@
}
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 aef866b..5728bdf 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 @@
* 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 @@
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 0000000..7497543
--- /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 204791e..4e77d90 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 1b1eb3a..d15a222 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 9e6209b..62c2b02 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -131,6 +131,18 @@
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 @@
/* 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 0000000..a61fa85
--- /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 0000000..8a46f15
--- /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 0000000..8a855c2
--- /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 0000000..9fe395d
--- /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 b430a5b..bc47817 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 @@
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 0000000..4442ad8
--- /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 0000000..2777cfe
--- /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/reader/Android.bp b/services/inputflinger/reader/Android.bp
index f954370..ba586d7 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 a41064b..f3f15df 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -251,6 +251,7 @@
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 @@
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 0582649..9608210 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -164,19 +164,6 @@
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 @@
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 ba7234b..0719b0c 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -199,6 +199,7 @@
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 55fc282..45f09ae 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -165,8 +165,9 @@
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;
@@ -516,7 +517,11 @@
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/reporter/Android.bp b/services/inputflinger/reporter/Android.bp
index b1e1aee..e85a104 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/lib.rs b/services/inputflinger/rust/lib.rs
index da72134..25dfb03 100644
--- a/services/inputflinger/rust/lib.rs
+++ b/services/inputflinger/rust/lib.rs
@@ -70,7 +70,9 @@
/// 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 55aa226..a26153e 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 @@
"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 7b793d8..8c17221 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -105,7 +105,7 @@
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 @@
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 @@
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 88f514f..9e93712 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -41,15 +41,21 @@
}
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::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 4ef9c3e..da5085d 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -102,9 +102,11 @@
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 0000000..1d27107
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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);
+}
+
+} // namespace
+
+// --- VerifyingTrace ---
+
+void VerifyingTrace::expectKeyDispatchTraced(const KeyEvent& event) {
+ std::scoped_lock lock(mLock);
+ mExpectedEvents.emplace_back(event);
+}
+
+void VerifyingTrace::expectMotionDispatchTraced(const MotionEvent& event) {
+ std::scoped_lock lock(mLock);
+ mExpectedEvents.emplace_back(event);
+}
+
+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 : mExpectedEvents) {
+ std::visit([&](const auto& event)
+ REQUIRES(mLock) { result = verifyEventTraced(event); },
+ 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();
+ mExpectedEvents.clear();
+}
+
+template <typename Event>
+base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent) 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);
+ }
+
+ 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();
+}
+
+} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
new file mode 100644
index 0000000..e5dd546
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -0,0 +1,88 @@
+/*
+ * 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);
+
+ /** Add an expectation for a motion event to be traced. */
+ void expectMotionDispatchTraced(const MotionEvent& event);
+
+ /**
+ * 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);
+ std::vector<std::variant<KeyEvent, MotionEvent>> 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&) 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 094e957..31c7c67 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 @@
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 @@
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 @@
}
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 @@
mInterceptKeyTimeout = timeout;
}
+ std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; }
+
void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
void assertUserActivityNotPoked() {
@@ -521,16 +524,14 @@
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 @@
// --- 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 @@
}
void TearDown() override {
+ ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced());
ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.reset();
mDispatcher.reset();
@@ -1083,8 +1093,8 @@
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 @@
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 @@
}
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 @@
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,13 +1443,7 @@
int getChannelFd() { return mInputReceiver->getChannelFd(); }
-private:
- FakeWindowHandle(std::string name) : mName(name){};
- const std::string mName;
- std::shared_ptr<FakeInputReceiver> mInputReceiver;
- static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
- friend class sp<FakeWindowHandle>;
-
+ // 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";
@@ -1452,8 +1452,47 @@
if (event == nullptr) {
ADD_FAILURE() << "Consume failed: no event";
}
+ expectReceivedEventTraced(event);
return event;
}
+
+private:
+ FakeWindowHandle(std::string name) : mName(name){};
+ const std::string mName;
+ std::shared_ptr<FakeInputReceiver> mInputReceiver;
+ static std::atomic<int32_t> sId; // each window gets a unique id, like in surfaceflinger
+ friend class sp<FakeWindowHandle>;
+
+ // 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) {
+ ADD_FAILURE() << "Invalid receive event on window with no receiver";
+ return std::make_pair(std::nullopt, nullptr);
+ }
+ 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));
+ break;
+ }
+ case InputEventType::MOTION: {
+ gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event));
+ break;
+ }
+ default:
+ break;
+ }
+ }
};
std::atomic<int32_t> FakeWindowHandle::sId{1};
@@ -1512,7 +1551,7 @@
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 +5816,27 @@
<< "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) {
@@ -7261,7 +7300,7 @@
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());
@@ -7294,6 +7333,71 @@
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;
@@ -7303,12 +7407,9 @@
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();
}
@@ -8226,13 +8327,13 @@
}
}
- 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);
}
};
@@ -8240,15 +8341,15 @@
// 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) {
@@ -8259,21 +8360,21 @@
// 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) {
@@ -8283,22 +8384,25 @@
// 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) {
@@ -8308,37 +8412,37 @@
// 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) {
@@ -8348,20 +8452,20 @@
// 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);
}
/**
@@ -8405,7 +8509,7 @@
.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})});
}
@@ -8827,16 +8931,11 @@
/**
* 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});
@@ -8845,23 +8944,18 @@
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);
@@ -8876,7 +8970,10 @@
* 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});
@@ -8891,15 +8988,19 @@
.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));
@@ -8956,6 +9057,61 @@
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();
@@ -9547,7 +9703,7 @@
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";
@@ -9620,10 +9776,10 @@
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 460a7b1..c1dc7ff 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2706,6 +2706,31 @@
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 8a4f6f0..81c3353 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/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 9e35717..575a30e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -97,13 +97,13 @@
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/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 4f81482..5f20cd9 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -239,10 +239,8 @@
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();
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index 17f6f31..362ab9c 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -343,7 +343,9 @@
}
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 ba0825c..224f50e 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;
-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 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/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index cf1c3c1..bac24c7 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -343,14 +343,18 @@
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 @@
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 fb32ff4..7fbbb1a 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 @@
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 @@
// 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 @@
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 @@
// 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/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index 38974a2..3ef9e69 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -315,6 +315,7 @@
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 8df5d8c..0966fe0 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -1044,6 +1044,8 @@
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 209df79..2cf4c1b 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -170,6 +170,9 @@
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 c80c8fd..96eccf2 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -148,7 +148,7 @@
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 @@
}();
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 @@
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 8970103..90e61a9 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -142,6 +142,7 @@
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 3b61de7..9f4f5b6 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -30,6 +30,8 @@
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 5ce883c..dcb6254 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -221,9 +221,8 @@
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/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 7614453..e696e8c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -286,7 +286,8 @@
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());
}
@@ -854,7 +855,7 @@
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 a1a7c28..6051e89 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 c76d4bd..6979f03 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::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 @@
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 @@
}
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 @@
// 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 9912622..9f29e9f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -111,7 +111,7 @@
public:
Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, surfaceflinger::Factory&,
- Fps activeRefreshRate, TimeStats&, IVsyncTrackerCallback&);
+ Fps activeRefreshRate, TimeStats&);
virtual ~Scheduler();
void startTimers();
@@ -458,6 +458,7 @@
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 @@
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 a05c123..0000000
--- 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 f978016..ed8f8fe 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 @@
*/
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 5cb0ffb..963f9e9 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -267,15 +267,15 @@
}
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 @@
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 @@
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 3d08410..81c746e 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 @@
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 @@
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 6e12b33..8697696 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -48,14 +48,12 @@
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 @@
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 9191003..8fd7e60 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -37,11 +37,9 @@
* \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 @@
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 417163f..37bd4b4 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 3491600..001938c 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -57,11 +57,10 @@
};
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 @@
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 6f656d2..85cd3e7 100644
--- a/services/surfaceflinger/Scheduler/VsyncSchedule.h
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -57,8 +57,7 @@
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 @@
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 12ee36e..8673a22 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/interface/ICompositor.h
@@ -47,6 +47,9 @@
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 09e8a1e..eeb9c60 100644
--- a/services/surfaceflinger/Scheduler/src/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/src/Timer.cpp
@@ -159,7 +159,7 @@
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 aa6be36..173b6fe 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1083,7 +1083,7 @@
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();
@@ -1132,7 +1132,7 @@
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());
@@ -1148,7 +1148,7 @@
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);
}
}
}
@@ -1311,7 +1311,7 @@
[](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;
}
@@ -1420,12 +1420,12 @@
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());
@@ -1617,7 +1617,7 @@
[](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;
}
@@ -2753,23 +2753,11 @@
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();
@@ -2833,6 +2821,7 @@
scheduleComposite(FrameHint::kNone);
}
+ mNotifyExpectedPresentMap[pacesetterId].hintStatus = NotifyExpectedPresentHintStatus::Start;
onCompositionPresented(pacesetterId, frameTargeters, presentTime);
const bool hadGpuComposited =
@@ -3293,7 +3282,7 @@
std::pair<DisplayModes, DisplayModePtr> SurfaceFlinger::loadDisplayModes(
PhysicalDisplayId displayId) const {
std::vector<HWComposer::HWCDisplayMode> hwcModes;
- std::optional<hal::HWDisplayId> activeModeHwcId;
+ std::optional<hal::HWConfigId> activeModeHwcIdOpt;
int attempt = 0;
constexpr int kMaxAttempts = 3;
@@ -3301,10 +3290,10 @@
hwcModes = getHwComposer().getModes(displayId,
scheduler::RefreshRateSelector::kMinSupportedFrameRate
.getPeriodNsecs());
- activeModeHwcId = getHwComposer().getActiveMode(displayId);
+ activeModeHwcIdOpt = getHwComposer().getActiveMode(displayId).value_opt();
- const auto isActiveMode = [activeModeHwcId](const HWComposer::HWCDisplayMode& mode) {
- return mode.hwcId == activeModeHwcId;
+ const auto isActiveMode = [activeModeHwcIdOpt](const HWComposer::HWCDisplayMode& mode) {
+ return mode.hwcId == activeModeHwcIdOpt;
};
if (std::any_of(hwcModes.begin(), hwcModes.end(), isActiveMode)) {
@@ -3314,7 +3303,7 @@
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 +3316,15 @@
})
.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,8 +3347,8 @@
// 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;
return {modes, activeMode};
@@ -3406,8 +3395,12 @@
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;
}
@@ -3775,6 +3768,9 @@
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 +4080,8 @@
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 +4107,9 @@
}
}
-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 +4130,91 @@
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);
+ 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 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);
+ const bool expectedPresentWithinTimeout =
+ isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp,
+ timeoutOpt, threshold);
+ if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ return;
+ }
- using fps_approx_ops::operator!=;
- if (frameIntervalIsOnCadence && frameInterval != lastFrameInterval) {
- data.lastExpectedPresentTimestamp = expectedPresentTime;
- }
-
- if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) {
+ 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 +4256,7 @@
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 +4315,6 @@
}
mDrawingState = mCurrentState;
- // clear the "changed" flags in current state
mCurrentState.colorMatrixChanged = false;
if (mVisibleRegionsDirty) {
@@ -5733,7 +5774,7 @@
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();
@@ -8334,11 +8375,11 @@
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;
}
@@ -8426,7 +8467,7 @@
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);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c8e2a4d..992bc00 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -199,8 +199,7 @@
private HWC2::ComposerCallback,
private ICompositor,
private scheduler::ISchedulerCallback,
- private compositionengine::ICEPowerCallback,
- private scheduler::IVsyncTrackerCallback {
+ private compositionengine::ICEPowerCallback {
public:
struct SkipInitializationTag {};
@@ -686,14 +685,12 @@
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);
@@ -1482,14 +1479,28 @@
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);
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 5ef22b5..e125bbe 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -18,7 +18,7 @@
"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 b07e7ac..425d2da 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -130,6 +130,8 @@
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);
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -206,6 +208,8 @@
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")
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "")
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 2a30a40..86efd30 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -70,6 +70,8 @@
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;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
index 68237c8..682079f 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_displayhardware_fuzzer.cpp
@@ -143,7 +143,6 @@
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 @@
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 @@
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 fa79956..0d15f62 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h
@@ -228,9 +228,9 @@
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 @@
}
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 @@
} // 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 @@
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 @@
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 @@
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 a6b12d0..8cd6e1b 100644
--- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
+++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp
@@ -103,6 +103,7 @@
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 @@
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 fcbef01..0ebc41b 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -174,3 +174,25 @@
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
+ }
+}
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 5809ea0..da4e47f 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -95,6 +95,7 @@
"MessageQueueTest.cpp",
"PowerAdvisorTest.cpp",
"SmallAreaDetectionAllowMappingsTest.cpp",
+ "SurfaceFlinger_ColorMatrixTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
"SurfaceFlinger_DisplayModeSwitching.cpp",
@@ -128,7 +129,6 @@
"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 0000000..34e4ba5
--- /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 beb2147..7d8a30a 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 @@
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 @@
.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 @@
.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 @@
.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 @@
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 @@
.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 @@
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 @@
.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 @@
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 1379665..fa31643 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -80,8 +80,7 @@
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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 45db0c5..d5ec654 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -85,6 +85,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index a5c0657..a25804c 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -104,7 +104,7 @@
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 @@
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 734fddb..110f324 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -165,12 +165,8 @@
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 @@
};
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, 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, 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 9456e37..bc06a31 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -146,11 +146,8 @@
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 {
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index 22cfbd8..9fe9ee8 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -64,10 +64,8 @@
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 {
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 13b47ff..3baa48d 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1237,4 +1237,25 @@
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 249ed40..f5661fc 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -41,6 +41,7 @@
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 c03cbd7..39a8aac 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -103,7 +103,7 @@
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,10 +1619,11 @@
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;
@@ -1631,9 +1632,10 @@
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;
@@ -1641,10 +1643,11 @@
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;
@@ -1652,10 +1655,11 @@
lr2.vote = LayerVoteType::ExplicitCategory;
lr2.frameRateCategory = FrameRateCategory::Low;
lr2.name = "ExplicitCategory Low";
- 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;
@@ -1663,10 +1667,11 @@
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;
@@ -1674,14 +1679,17 @@
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 6986689..b059525 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -95,10 +95,8 @@
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 @@
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 @@
}
void sample() override {}
+ void sendNotifyExpectedPresentHint(PhysicalDisplayId) override {}
} compositor(*mScheduler);
mScheduler->doFrameSignal(compositor, VsyncId(42));
@@ -568,7 +622,7 @@
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 @@
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 45b7610..0000000
--- 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 0000000..f127213
--- /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 8b16a8a..9efb503 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -24,6 +24,14 @@
#include <ftl/fake_guard.h>
#include <scheduler/Fps.h>
+#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 +169,7 @@
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 +177,7 @@
// 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 +206,7 @@
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 +215,7 @@
// 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 +238,20 @@
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 +273,7 @@
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 +282,7 @@
// 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 +319,7 @@
}
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 +333,15 @@
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;
}
@@ -376,22 +365,19 @@
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 +398,7 @@
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();
@@ -447,27 +430,20 @@
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 +462,8 @@
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 +471,7 @@
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();
@@ -530,13 +503,13 @@
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 +518,7 @@
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 +537,7 @@
// 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 93c2829..19f8deb 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, 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, 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 29acfaa..4e9fba7 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 a270dc9..897f9a0 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, rejectsHotplugIfFailedToLoadDisplayModes) {
+ SET_FLAG_FOR_TEST(flags::connected_display, true);
+
// Inject a primary display.
PrimaryDisplayVariant::injectHwcDisplay(this);
@@ -94,6 +102,10 @@
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 7206e29..91b9018 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 @@
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));
-
- {
- // Very first ExpectedPresent after idle, no previous timestamp
- EXPECT_CALL(*mComposer,
- notifyExpectedPresent(HWC_DISPLAY_ID, expectedPresentTime,
- kFrameInterval60HzNs))
- .WillOnce(Return(Error::NONE));
- mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
- TimePoint::fromNs(expectedPresentTime), kFps60Hz,
- kTimeoutNs);
- }
- {
- // Absent timeoutNs
+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;
- EXPECT_CALL(*mComposer, notifyExpectedPresent(HWC_DISPLAY_ID, _, _)).Times(0);
- mFlinger.notifyExpectedPresentIfRequired(physicDisplayId, kVsyncPeriod,
+ 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);
{
- // Timeout is 0
- expectedPresentTime += kFrameInterval60HzNs;
+ // 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,
- TimePoint::fromNs(expectedPresentTime), kFps60Hz,
- Period::fromNs(0));
- }
- {
- // ExpectedPresent is after the timeoutNs
- expectedPresentTime += 2 * kFrameInterval5HzNs;
- EXPECT_CALL(*mComposer,
- notifyExpectedPresent(HWC_DISPLAY_ID, 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));
+ }
+ {
+ mCompositor->committed = false;
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ mFlinger.notifyExpectedPresentIfRequired(mPhysicalDisplayId, kVsyncPeriod,
+ TimePoint::fromNs(expectedPresentTime), kFps60Hz,
+ 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));
+ }
+ {
+ expectedPresentTime += kFrameInterval60HzNs;
+ EXPECT_CALL(*mComposer,
+ notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ 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);
+ {
+ // 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(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs))
+ .WillOnce(Return(Error::NONE));
+ 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 @@
struct FrameRateIntervalTestData {
int32_t frameIntervalNs;
- bool callExpectedPresent;
+ bool callNotifyExpectedPresentHint;
};
const std::vector<FrameRateIntervalTestData> frameIntervals = {
{kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true},
@@ -159,21 +291,35 @@
{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 b80cb66..c3934e6 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 15fe600..83e2f98 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, transitionsDisplayFromOffToOnExternalDisplay) {
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOffToOnVariant>>();
}
@@ -461,6 +466,7 @@
}
TEST_F(SetPowerModeInternalTest, transitionsDisplayFromOnToOffExternalDisplay) {
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionOnToOffVariant>>();
}
@@ -473,6 +479,7 @@
}
TEST_F(SetPowerModeInternalTest, transitionsDisplayFromDozeSuspendToDozeExternalDisplay) {
+ SET_FLAG_FOR_TEST(flags::multithreaded_present, true);
transitionDisplayCommon<ExternalDisplayPowerCase<TransitionDozeSuspendToDozeVariant>>();
}
@@ -481,10 +488,12 @@
}
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 e0b7366..7b92a5b 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.cpp
@@ -21,11 +21,10 @@
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 6213713..25a85df 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -41,18 +41,16 @@
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 @@
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 9ef0749..46a079c 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 @@
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 @@
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 @@
? 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 @@
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 @@
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 @@
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 @@
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 @@
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/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d952b70..b9f3d70 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 @@
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 @@
// 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 @@
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 @@
.build());
VSyncPredictor vrrTracker{kMode, kHistorySize, kMinimumSamplesForPrediction,
- kOutlierTolerancePercent, mVsyncTrackerCallback};
+ kOutlierTolerancePercent};
vrrTracker.setRenderRate(minFrameRate);
vrrTracker.addVsyncTimestamp(0);
@@ -733,28 +688,6 @@
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 e298e7c..685d8f9 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -24,7 +24,7 @@
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 a71e82c..7b18a82 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 22b2ccc..4ca0542 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -28,6 +28,8 @@
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 @@
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 b48529f..0000000
--- 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/test/Android.bp b/services/vibratorservice/test/Android.bp
index 3294724..be71dc2 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 867fc45..fe3be45 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>
@@ -786,6 +787,12 @@
all_formats.emplace_back(
VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT});
}
}