Merge "SF: Propagate uniqueID when creating virtual displays" into main
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 341fabb..a1c10f5 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -37,9 +37,6 @@
name: "libdumpstateutil",
defaults: ["dumpstate_cflag_defaults"],
vendor_available: true,
- vndk: {
- enabled: true,
- },
srcs: [
"DumpstateInternal.cpp",
"DumpstateUtil.cpp",
@@ -82,7 +79,10 @@
cc_defaults {
name: "dumpstate_defaults",
- defaults: ["dumpstate_cflag_defaults"],
+ defaults: [
+ "aconfig_lib_cc_static_link.defaults",
+ "dumpstate_cflag_defaults",
+ ],
shared_libs: [
"android.hardware.dumpstate@1.0",
"android.hardware.dumpstate@1.1",
diff --git a/cmds/servicemanager/Access.cpp b/cmds/servicemanager/Access.cpp
index 711038c..8098724 100644
--- a/cmds/servicemanager/Access.cpp
+++ b/cmds/servicemanager/Access.cpp
@@ -22,6 +22,8 @@
#include <selinux/android.h>
#include <selinux/avc.h>
+#include <sstream>
+
namespace android {
#ifdef VENDORSERVICEMANAGER
@@ -80,6 +82,12 @@
}
#endif
+std::string Access::CallingContext::toDebugString() const {
+ std::stringstream ss;
+ ss << "Caller(pid=" << debugPid << ",uid=" << uid << ",sid=" << sid << ")";
+ return ss.str();
+}
+
Access::Access() {
#ifdef __ANDROID__
union selinux_callback cb;
diff --git a/cmds/servicemanager/Access.h b/cmds/servicemanager/Access.h
index 77c2cd4..4ee9b90 100644
--- a/cmds/servicemanager/Access.h
+++ b/cmds/servicemanager/Access.h
@@ -36,6 +36,8 @@
pid_t debugPid;
uid_t uid;
std::string sid;
+
+ std::string toDebugString() const;
};
virtual CallingContext getCallingContext();
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index a828b52..95a05cd 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -115,18 +115,20 @@
return instance.package() + "." + instance.interface() + "/" + instance.instance();
}
-static bool isVintfDeclared(const std::string& name) {
+static bool isVintfDeclared(const Access::CallingContext& ctx, 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);
+ ALOGI("%s Found %s in %s VINTF manifest.", ctx.toDebugString().c_str(),
+ 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());
+ ALOGI("%s Could not find %s in the VINTF manifest.", ctx.toDebugString().c_str(),
+ name.c_str());
}
return found;
}
@@ -136,7 +138,8 @@
bool found = forEachManifest([&](const ManifestWithDescription& mwd) {
if (mwd.manifest->hasAidlInstance(aname.package, aname.iface, aname.instance)) {
- ALOGI("Found %s in %s VINTF manifest.", name.c_str(), mwd.description);
+ ALOGI("%s Found %s in %s VINTF manifest.", ctx.toDebugString().c_str(), name.c_str(),
+ mwd.description);
return true; // break
}
return false; // continue
@@ -161,8 +164,9 @@
}
// Although it is tested, explicitly rebuilding qualified name, in case it
// becomes something unexpected.
- ALOGI("Could not find %s.%s/%s in the VINTF manifest. %s.", aname.package.c_str(),
- aname.iface.c_str(), aname.instance.c_str(), available.c_str());
+ ALOGI("%s Could not find %s.%s/%s in the VINTF manifest. %s.", ctx.toDebugString().c_str(),
+ aname.package.c_str(), aname.iface.c_str(), aname.instance.c_str(),
+ available.c_str());
}
return found;
@@ -290,12 +294,13 @@
return ret;
}
-static bool meetsDeclarationRequirements(const sp<IBinder>& binder, const std::string& name) {
+static bool meetsDeclarationRequirements(const Access::CallingContext& ctx,
+ const sp<IBinder>& binder, const std::string& name) {
if (!Stability::requiresVintfDeclaration(binder)) {
return true;
}
- return isVintfDeclared(name);
+ return isVintfDeclared(ctx, name);
}
#endif // !VENDORSERVICEMANAGER
@@ -307,7 +312,7 @@
// clear this bit so that we can abort in other cases, where it would
// mean inconsistent logic in servicemanager (unexpected and tested, but
// the original lazy service impl here had that bug).
- LOG(WARNING) << "a service was removed when there are clients";
+ ALOGW("A service was removed when there are clients");
}
}
@@ -423,25 +428,26 @@
}
if (!isValidServiceName(name)) {
- ALOGE("Invalid service name: %s", name.c_str());
+ ALOGE("%s Invalid service name: %s", ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
}
#ifndef VENDORSERVICEMANAGER
- if (!meetsDeclarationRequirements(binder, name)) {
+ if (!meetsDeclarationRequirements(ctx, binder, name)) {
// already logged
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "VINTF declaration error.");
}
#endif // !VENDORSERVICEMANAGER
if ((dumpPriority & DUMP_FLAG_PRIORITY_ALL) == 0) {
- ALOGW("Dump flag priority is not set when adding %s", name.c_str());
+ ALOGW("%s Dump flag priority is not set when adding %s", ctx.toDebugString().c_str(),
+ name.c_str());
}
// implicitly unlinked when the binder is removed
if (binder->remoteBinder() != nullptr &&
binder->linkToDeath(sp<ServiceManager>::fromExisting(this)) != OK) {
- ALOGE("Could not linkToDeath when adding %s", name.c_str());
+ ALOGE("%s Could not linkToDeath when adding %s", ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
}
@@ -543,7 +549,7 @@
}
if (!isValidServiceName(name)) {
- ALOGE("Invalid service name: %s", name.c_str());
+ ALOGE("%s Invalid service name: %s", ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Invalid service name.");
}
@@ -554,7 +560,7 @@
if (OK !=
IInterface::asBinder(callback)->linkToDeath(
sp<ServiceManager>::fromExisting(this))) {
- ALOGE("Could not linkToDeath when adding %s", name.c_str());
+ ALOGE("%s Could not linkToDeath when adding %s", ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't link to death.");
}
@@ -586,7 +592,8 @@
}
if (!found) {
- ALOGE("Trying to unregister callback, but none exists %s", name.c_str());
+ ALOGE("%s Trying to unregister callback, but none exists %s", ctx.toDebugString().c_str(),
+ name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Nothing to unregister.");
}
@@ -603,7 +610,7 @@
*outReturn = false;
#ifndef VENDORSERVICEMANAGER
- *outReturn = isVintfDeclared(name);
+ *outReturn = isVintfDeclared(ctx, name);
#endif
return Status::ok();
}
@@ -735,18 +742,16 @@
}
void ServiceManager::tryStartService(const Access::CallingContext& ctx, const std::string& name) {
- ALOGI("Since '%s' could not be found (requested by debug pid %d), trying to start it as a lazy "
- "AIDL service. (if it's not configured to be a lazy service, it may be stuck starting or "
- "still starting).",
- name.c_str(), ctx.debugPid);
+ ALOGI("%s Since '%s' could not be found trying to start it as a lazy AIDL service. (if it's "
+ "not configured to be a lazy service, it may be stuck starting or still starting).",
+ ctx.toDebugString().c_str(), name.c_str());
std::thread([=] {
if (!base::SetProperty("ctl.interface_start", "aidl/" + name)) {
- ALOGI("Tried to start aidl service %s as a lazy service, but was unable to. Usually "
- "this happens when a "
- "service is not installed, but if the service is intended to be used as a "
- "lazy service, then it may be configured incorrectly.",
- name.c_str());
+ ALOGI("%s Tried to start aidl service %s as a lazy service, but was unable to. Usually "
+ "this happens when a service is not installed, but if the service is intended to "
+ "be used as a lazy service, then it may be configured incorrectly.",
+ ctx.toDebugString().c_str(), name.c_str());
}
}).detach();
}
@@ -764,26 +769,29 @@
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- ALOGE("Could not add callback for nonexistent service: %s", name.c_str());
+ ALOGE("%s Could not add callback for nonexistent service: %s", ctx.toDebugString().c_str(),
+ name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Service doesn't exist.");
}
if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
- ALOGW("Only a server can register for client callbacks (for %s)", name.c_str());
+ ALOGW("%s Only a server can register for client callbacks (for %s)",
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
"Only service can register client callback for itself.");
}
if (serviceIt->second.binder != service) {
- ALOGW("Tried to register client callback for %s but a different service is registered "
+ ALOGW("%s Tried to register client callback for %s but a different service is registered "
"under this name.",
- name.c_str());
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT, "Service mismatch.");
}
if (OK !=
IInterface::asBinder(cb)->linkToDeath(sp<ServiceManager>::fromExisting(this))) {
- ALOGE("Could not linkToDeath when adding client callback for %s", name.c_str());
+ ALOGE("%s Could not linkToDeath when adding client callback for %s",
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Couldn't linkToDeath.");
}
@@ -921,13 +929,14 @@
auto serviceIt = mNameToService.find(name);
if (serviceIt == mNameToService.end()) {
- ALOGW("Tried to unregister %s, but that service wasn't registered to begin with.",
- name.c_str());
+ ALOGW("%s Tried to unregister %s, but that service wasn't registered to begin with.",
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE, "Service not registered.");
}
if (serviceIt->second.ctx.debugPid != IPCThreadState::self()->getCallingPid()) {
- ALOGW("Only a server can unregister itself (for %s)", name.c_str());
+ ALOGW("%s Only a server can unregister itself (for %s)", ctx.toDebugString().c_str(),
+ name.c_str());
return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION,
"Service can only unregister itself.");
}
@@ -935,8 +944,8 @@
sp<IBinder> storedBinder = serviceIt->second.binder;
if (binder != storedBinder) {
- ALOGW("Tried to unregister %s, but a different service is registered under this name.",
- name.c_str());
+ ALOGW("%s Tried to unregister %s, but a different service is registered under this name.",
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
"Different service registered under this name.");
}
@@ -944,7 +953,8 @@
// important because we don't have timer-based guarantees, we don't want to clear
// this
if (serviceIt->second.guaranteeClient) {
- ALOGI("Tried to unregister %s, but there is about to be a client.", name.c_str());
+ ALOGI("%s Tried to unregister %s, but there is about to be a client.",
+ ctx.toDebugString().c_str(), name.c_str());
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE,
"Can't unregister, pending client.");
}
@@ -954,7 +964,8 @@
constexpr size_t kKnownClients = 2;
if (handleServiceClientCallback(kKnownClients, name, false)) {
- ALOGI("Tried to unregister %s, but there are clients.", name.c_str());
+ ALOGI("%s Tried to unregister %s, but there are clients.", ctx.toDebugString().c_str(),
+ name.c_str());
// Since we had a failed registration attempt, and the HIDL implementation of
// delaying service shutdown for multiple periods wasn't ported here... this may
@@ -965,7 +976,7 @@
"Can't unregister, known client.");
}
- ALOGI("Unregistering %s", name.c_str());
+ ALOGI("%s Unregistering %s", ctx.toDebugString().c_str(), name.c_str());
mNameToService.erase(name);
return Status::ok();
diff --git a/cmds/sfdo/Android.bp b/cmds/sfdo/Android.bp
index c19c9da..a91a7dc 100644
--- a/cmds/sfdo/Android.bp
+++ b/cmds/sfdo/Android.bp
@@ -1,17 +1,10 @@
-cc_binary {
+rust_binary {
name: "sfdo",
+ srcs: ["sfdo.rs"],
- srcs: ["sfdo.cpp"],
-
- shared_libs: [
- "libutils",
- "libgui",
+ rustlibs: [
+ "android.gui-rust",
+ "libclap",
],
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wunused",
- "-Wunreachable-code",
- ],
+ edition: "2021",
}
diff --git a/cmds/sfdo/sfdo.cpp b/cmds/sfdo/sfdo.cpp
deleted file mode 100644
index de0e171..0000000
--- a/cmds/sfdo/sfdo.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * 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 <inttypes.h>
-#include <stdint.h>
-#include <any>
-#include <map>
-
-#include <cutils/properties.h>
-#include <sys/resource.h>
-#include <utils/Log.h>
-
-#include <gui/ISurfaceComposer.h>
-#include <gui/SurfaceComposerClient.h>
-#include <gui/SurfaceControl.h>
-#include <private/gui/ComposerServiceAIDL.h>
-
-using namespace android;
-
-std::map<std::string, std::any> g_functions;
-
-enum class ParseToggleResult {
- kError,
- kFalse,
- kTrue,
-};
-
-const std::map<std::string, std::string> g_function_details = {
- {"debugFlash", "[optional(delay)] Perform a debug flash."},
- {"frameRateIndicator", "[hide | show] displays the framerate in the top left corner."},
- {"scheduleComposite", "Force composite ahead of next VSYNC."},
- {"scheduleCommit", "Force commit ahead of next VSYNC."},
- {"scheduleComposite", "PENDING - if you have a good understanding let me know!"},
- {"forceClientComposition",
- "[enabled | disabled] When enabled, it disables "
- "Hardware Overlays, and routes all window composition to the GPU. This can "
- "help check if there is a bug in HW Composer."},
-};
-
-static void ShowUsage() {
- std::cout << "usage: sfdo [help, frameRateIndicator show, debugFlash enabled, ...]\n\n";
- for (const auto& sf : g_functions) {
- const std::string fn = sf.first;
- std::string fdetails = "TODO";
- if (g_function_details.find(fn) != g_function_details.end())
- fdetails = g_function_details.find(fn)->second;
- std::cout << " " << fn << ": " << fdetails << "\n";
- }
-}
-
-// Returns 1 for positive keywords and 0 for negative keywords.
-// If the string does not match any it will return -1.
-ParseToggleResult parseToggle(const char* str) {
- const std::unordered_set<std::string> positive{"1", "true", "y", "yes",
- "on", "enabled", "show"};
- const std::unordered_set<std::string> negative{"0", "false", "n", "no",
- "off", "disabled", "hide"};
-
- const std::string word(str);
- if (positive.count(word)) {
- return ParseToggleResult::kTrue;
- }
- if (negative.count(word)) {
- return ParseToggleResult::kFalse;
- }
-
- return ParseToggleResult::kError;
-}
-
-int frameRateIndicator(int argc, char** argv) {
- bool hide = false, show = false;
- if (argc == 3) {
- show = strcmp(argv[2], "show") == 0;
- hide = strcmp(argv[2], "hide") == 0;
- }
-
- if (show || hide) {
- ComposerServiceAIDL::getComposerService()->enableRefreshRateOverlay(show);
- } else {
- std::cerr << "Incorrect usage of frameRateIndicator. Missing [hide | show].\n";
- return -1;
- }
- return 0;
-}
-
-int debugFlash(int argc, char** argv) {
- int delay = 0;
- if (argc == 3) {
- delay = atoi(argv[2]) == 0;
- }
-
- ComposerServiceAIDL::getComposerService()->setDebugFlash(delay);
- return 0;
-}
-
-int scheduleComposite([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
- ComposerServiceAIDL::getComposerService()->scheduleComposite();
- return 0;
-}
-
-int scheduleCommit([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {
- ComposerServiceAIDL::getComposerService()->scheduleCommit();
- return 0;
-}
-
-int forceClientComposition(int argc, char** argv) {
- bool enabled = true;
- // A valid command looks like this:
- // adb shell sfdo forceClientComposition enabled
- if (argc >= 3) {
- const ParseToggleResult toggle = parseToggle(argv[2]);
- if (toggle == ParseToggleResult::kError) {
- std::cerr << "Incorrect usage of forceClientComposition. "
- "Missing [enabled | disabled].\n";
- return -1;
- }
- if (argc > 3) {
- std::cerr << "Too many arguments after [enabled | disabled]. "
- "Ignoring extra arguments.\n";
- }
- enabled = (toggle == ParseToggleResult::kTrue);
- } else {
- std::cerr << "Incorrect usage of forceClientComposition. Missing [enabled | disabled].\n";
- return -1;
- }
-
- ComposerServiceAIDL::getComposerService()->forceClientComposition(enabled);
- return 0;
-}
-
-int main(int argc, char** argv) {
- std::cout << "Execute SurfaceFlinger internal commands.\n";
- std::cout << "sfdo requires to be run with root permissions..\n";
-
- g_functions["frameRateIndicator"] = frameRateIndicator;
- g_functions["debugFlash"] = debugFlash;
- g_functions["scheduleComposite"] = scheduleComposite;
- g_functions["scheduleCommit"] = scheduleCommit;
- g_functions["forceClientComposition"] = forceClientComposition;
-
- if (argc > 1 && g_functions.find(argv[1]) != g_functions.end()) {
- std::cout << "Running: " << argv[1] << "\n";
- const std::string key(argv[1]);
- const auto fn = g_functions[key];
- int result = std::any_cast<int (*)(int, char**)>(fn)(argc, argv);
- if (result == 0) {
- std::cout << "Success.\n";
- }
- return result;
- } else {
- ShowUsage();
- }
- return 0;
-}
\ No newline at end of file
diff --git a/cmds/sfdo/sfdo.rs b/cmds/sfdo/sfdo.rs
new file mode 100644
index 0000000..863df6b
--- /dev/null
+++ b/cmds/sfdo/sfdo.rs
@@ -0,0 +1,155 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! sfdo: Make surface flinger do things
+use android_gui::{aidl::android::gui::ISurfaceComposer::ISurfaceComposer, binder};
+use clap::{Parser, Subcommand};
+use std::fmt::Debug;
+
+const SERVICE_IDENTIFIER: &str = "SurfaceFlingerAIDL";
+
+fn print_result<T, E>(function_name: &str, res: Result<T, E>)
+where
+ E: Debug,
+{
+ match res {
+ Ok(_) => println!("{}: Operation successful!", function_name),
+ Err(err) => println!("{}: Operation failed: {:?}", function_name, err),
+ }
+}
+
+fn parse_toggle(toggle_value: &str) -> Option<bool> {
+ let positive = ["1", "true", "y", "yes", "on", "enabled", "show"];
+ let negative = ["0", "false", "n", "no", "off", "disabled", "hide"];
+
+ let word = toggle_value.to_lowercase(); // Case-insensitive comparison
+
+ if positive.contains(&word.as_str()) {
+ Some(true)
+ } else if negative.contains(&word.as_str()) {
+ Some(false)
+ } else {
+ None
+ }
+}
+
+#[derive(Parser)]
+#[command(version = "0.1", about = "Execute SurfaceFlinger internal commands.")]
+#[command(propagate_version = true)]
+struct Cli {
+ #[command(subcommand)]
+ command: Option<Commands>,
+}
+
+#[derive(Subcommand, Debug)]
+enum Commands {
+ #[command(about = "[optional(--delay)] Perform a debug flash.")]
+ DebugFlash {
+ #[arg(short, long, default_value_t = 0)]
+ delay: i32,
+ },
+
+ #[command(
+ about = "state = [enabled | disabled] When enabled, it disables Hardware Overlays, \
+ and routes all window composition to the GPU. This can help check if \
+ there is a bug in HW Composer."
+ )]
+ ForceClientComposition { state: Option<String> },
+
+ #[command(about = "state = [hide | show], displays the framerate in the top left corner.")]
+ FrameRateIndicator { state: Option<String> },
+
+ #[command(about = "Force composite ahead of next VSYNC.")]
+ ScheduleComposite,
+
+ #[command(about = "Force commit ahead of next VSYNC.")]
+ ScheduleCommit,
+}
+
+/// sfdo command line tool
+///
+/// sfdo allows you to call different functions from the SurfaceComposer using
+/// the adb shell.
+fn main() {
+ binder::ProcessState::start_thread_pool();
+ let composer_service = match binder::get_interface::<dyn ISurfaceComposer>(SERVICE_IDENTIFIER) {
+ Ok(service) => service,
+ Err(err) => {
+ eprintln!("Unable to connect to ISurfaceComposer: {}", err);
+ return;
+ }
+ };
+
+ let cli = Cli::parse();
+
+ match &cli.command {
+ Some(Commands::FrameRateIndicator { state }) => {
+ if let Some(op_state) = state {
+ let toggle = parse_toggle(op_state);
+ match toggle {
+ Some(true) => {
+ let res = composer_service.enableRefreshRateOverlay(true);
+ print_result("enableRefreshRateOverlay", res);
+ }
+ Some(false) => {
+ let res = composer_service.enableRefreshRateOverlay(false);
+ print_result("enableRefreshRateOverlay", res);
+ }
+ None => {
+ eprintln!("Invalid state: {}, choices are [hide | show]", op_state);
+ }
+ }
+ } else {
+ eprintln!("No state, choices are [hide | show]");
+ }
+ }
+ Some(Commands::DebugFlash { delay }) => {
+ let res = composer_service.setDebugFlash(*delay);
+ print_result("setDebugFlash", res);
+ }
+ Some(Commands::ScheduleComposite) => {
+ let res = composer_service.scheduleComposite();
+ print_result("scheduleComposite", res);
+ }
+ Some(Commands::ScheduleCommit) => {
+ let res = composer_service.scheduleCommit();
+ print_result("scheduleCommit", res);
+ }
+ Some(Commands::ForceClientComposition { state }) => {
+ if let Some(op_state) = state {
+ let toggle = parse_toggle(op_state);
+ match toggle {
+ Some(true) => {
+ let res = composer_service.forceClientComposition(true);
+ print_result("forceClientComposition", res);
+ }
+ Some(false) => {
+ let res = composer_service.forceClientComposition(false);
+ print_result("forceClientComposition", res);
+ }
+ None => {
+ eprintln!("Invalid state: {}, choices are [enabled | disabled]", op_state);
+ }
+ }
+ } else {
+ eprintln!("No state, choices are [enabled | disabled]");
+ }
+ }
+ None => {
+ println!("Execute SurfaceFlinger internal commands.");
+ println!("run `adb shell sfdo help` for more to view the commands.");
+ println!("run `adb shell sfdo [COMMAND] --help` for more info on the command.");
+ }
+ }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 99f4fbd..0300f8c 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -377,12 +377,6 @@
}
prebuilt_etc {
- name: "android.software.contextualsearch.prebuilt.xml",
- src: "android.software.contextualsearch.xml",
- defaults: ["frameworks_native_data_etc_defaults"],
-}
-
-prebuilt_etc {
name: "android.software.device_id_attestation.prebuilt.xml",
src: "android.software.device_id_attestation.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.software.contextualsearch.xml b/data/etc/android.software.contextualsearch.xml
deleted file mode 100644
index 4564ac8..0000000
--- a/data/etc/android.software.contextualsearch.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<!-- Feature for devices supporting contextual search helper. -->
-<permissions>
- <feature name="android.software.contextualsearch" />
-</permissions>
diff --git a/data/etc/input/motion_predictor_config.xml b/data/etc/input/motion_predictor_config.xml
index 39772ae..c3f2fed 100644
--- a/data/etc/input/motion_predictor_config.xml
+++ b/data/etc/input/motion_predictor_config.xml
@@ -31,5 +31,11 @@
the UX issue mentioned above.
-->
<distance-noise-floor>0.2</distance-noise-floor>
+ <!-- The low and high jerk thresholds for prediction pruning.
+
+ The jerk thresholds are based on normalized dt = 1 calculations.
+ -->
+ <low-jerk>1.0</low-jerk>
+ <high-jerk>1.1</high-jerk>
</motion-predictor>
diff --git a/include/android/OWNERS b/include/android/OWNERS
index bc53d7a..fad8c1b 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -1,4 +1,4 @@
-per-file input.h, keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
+per-file input.h,keycodes.h = file:platform/frameworks/base:/INPUT_OWNERS
# Window manager
per-file surface_control_input_receiver.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index d4f30ef..bec3283 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -17,8 +17,20 @@
/**
* @addtogroup Choreographer
*
- * Choreographer coordinates the timing of frame rendering. This is the C version of the
- * android.view.Choreographer object in Java.
+ * Choreographer coordinates the timing of frame rendering. This is the C
+ * version of the android.view.Choreographer object in Java. If you do not use
+ * Choreographer to pace your render loop, you may render too quickly for the
+ * display, increasing latency between frame submission and presentation.
+ *
+ * Input events are guaranteed to be processed before the frame callback is
+ * called, and will not be run concurrently. Input and sensor events should not
+ * be handled in the Choregrapher callback.
+ *
+ * The frame callback is also the appropriate place to run any per-frame state
+ * update logic. For example, in a game, the frame callback should be
+ * responsible for updating things like physics, AI, game state, and rendering
+ * the frame. Input and sensors should be handled separately via callbacks
+ * registered with AInputQueue and ASensorManager.
*
* As of API level 33, apps can follow proper frame pacing and even choose a future frame to render.
* The API is used as follows:
@@ -38,6 +50,11 @@
* 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or
* latching buffers before the desired presentation time.
*
+ * On older devices, AChoreographer_postFrameCallback64 or
+ * AChoreographer_postFrameCallback can be used to lesser effect. They cannot be
+ * used to precisely plan your render timeline, but will rate limit to avoid
+ * overloading the display pipeline and increasing frame latency.
+ *
* @{
*/
@@ -58,7 +75,7 @@
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
#if !defined(__DEPRECATED_IN)
-#define __DEPRECATED_IN(__api_level) __attribute__((__deprecated__))
+#define __DEPRECATED_IN(__api_level, ...) __attribute__((__deprecated__))
#endif
__BEGIN_DECLS
@@ -129,23 +146,60 @@
AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
/**
- * Deprecated: Use AChoreographer_postFrameCallback64 instead.
+ * Post a callback to be run when the application should begin rendering the
+ * next frame. The data pointer provided will be passed to the callback function
+ * when it's called.
+ *
+ * The callback will only be run for the next frame, not all subsequent frames,
+ * so to render continuously the callback should itself call
+ * AChoreographer_postFrameCallback.
+ *
+ * \bug The callback receives the frame time in nanoseconds as a long. On 32-bit
+ * systems, long is 32-bit, so the frame time will roll over roughly every two
+ * seconds. If your minSdkVersion is 29 or higher, switch to
+ * AChoreographer_postFrameCallback64, which uses a 64-bit frame time for all
+ * platforms. For older OS versions, you must combine the argument with the
+ * upper bits of clock_gettime(CLOCK_MONOTONIC, ...) on 32-bit systems.
+ *
+ * \deprecated Use AChoreographer_postFrameCallback64, which does not have the
+ * bug described above.
*/
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data)
- __INTRODUCED_IN(24) __DEPRECATED_IN(29);
+ __INTRODUCED_IN(24) __DEPRECATED_IN(29, "Use AChoreographer_postFrameCallback64 instead");
/**
- * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
+ * Post a callback to be run when the application should begin rendering the
+ * next frame following the specified delay. The data pointer provided will be
+ * passed to the callback function when it's called.
+ *
+ * The callback will only be run for the next frame after the delay, not all
+ * subsequent frames, so to render continuously the callback should itself call
+ * AChoreographer_postFrameCallbackDelayed.
+ *
+ * \bug The callback receives the frame time in nanoseconds as a long. On 32-bit
+ * systems, long is 32-bit, so the frame time will roll over roughly every two
+ * seconds. If your minSdkVersion is 29 or higher, switch to
+ * AChoreographer_postFrameCallbackDelayed64, which uses a 64-bit frame time for
+ * all platforms. For older OS versions, you must combine the argument with the
+ * upper bits of clock_gettime(CLOCK_MONOTONIC, ...) on 32-bit systems.
+ *
+ * \deprecated Use AChoreographer_postFrameCallbackDelayed64, which does not
+ * have the bug described above.
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) __INTRODUCED_IN(24)
- __DEPRECATED_IN(29);
+ __DEPRECATED_IN(29, "Use AChoreographer_postFrameCallbackDelayed64 instead");
/**
- * Post a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Post a callback to be run when the application should begin rendering the
+ * next frame. The data pointer provided will be passed to the callback function
+ * when it's called.
+ *
+ * The callback will only be run on the next frame, not all subsequent frames,
+ * so to render continuously the callback should itself call
+ * AChoreographer_postFrameCallback64.
*
* Available since API level 29.
*/
@@ -154,9 +208,13 @@
__INTRODUCED_IN(29);
/**
- * Post a callback to be run on the frame following the specified delay. The
- * data pointer provided will be passed to the callback function when it's
- * called.
+ * Post a callback to be run when the application should begin rendering the
+ * next frame following the specified delay. The data pointer provided will be
+ * passed to the callback function when it's called.
+ *
+ * The callback will only be run for the next frame after the delay, not all
+ * subsequent frames, so to render continuously the callback should itself call
+ * AChoreographer_postFrameCallbackDelayed64.
*
* Available since API level 29.
*/
@@ -165,8 +223,13 @@
uint32_t delayMillis) __INTRODUCED_IN(29);
/**
- * Posts a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Posts a callback to be run when the application should begin rendering the
+ * next frame. The data pointer provided will be passed to the callback function
+ * when it's called.
+ *
+ * The callback will only be run for the next frame, not all subsequent frames,
+ * so to render continuously the callback should itself call
+ * AChoreographer_postVsyncCallback.
*
* Available since API level 33.
*/
diff --git a/include/android/looper.h b/include/android/looper.h
index e50730d..d80a366 100644
--- a/include/android/looper.h
+++ b/include/android/looper.h
@@ -35,7 +35,7 @@
// This file may also be built on glibc or on Windows/MacOS libc's, so
// deprecated definitions are provided.
#if !defined(__REMOVED_IN)
-#define __REMOVED_IN(__api_level) __attribute__((__deprecated__))
+#define __REMOVED_IN(__api_level, msg) __attribute__((__deprecated__(msg)))
#endif
struct ALooper;
@@ -182,23 +182,27 @@
* If the timeout is zero, returns immediately without blocking.
* If the timeout is negative, waits indefinitely until an event appears.
*
- * Returns ALOOPER_POLL_WAKE if the poll was awoken using wake() before
+ * Returns ALOOPER_POLL_WAKE if the poll was awoken using ALooper_wake() before
* the timeout expired and no callbacks were invoked and no other file
- * descriptors were ready.
+ * descriptors were ready. **All return values may also imply
+ * ALOOPER_POLL_WAKE.**
*
- * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked.
+ * Returns ALOOPER_POLL_CALLBACK if one or more callbacks were invoked. The poll
+ * may also have been explicitly woken by ALooper_wake.
*
- * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given
- * timeout expired.
+ * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given timeout
+ * expired. The poll may also have been explicitly woken by ALooper_wake.
*
- * Returns ALOOPER_POLL_ERROR if an error occurred.
+ * Returns ALOOPER_POLL_ERROR if the calling thread has no associated Looper or
+ * for unrecoverable internal errors. The poll may also have been explicitly
+ * woken by ALooper_wake.
*
- * Returns a value >= 0 containing an identifier (the same identifier
- * `ident` passed to ALooper_addFd()) if its file descriptor has data
- * and it has no callback function (requiring the caller here to
- * handle it). In this (and only this) case outFd, outEvents and
- * outData will contain the poll events and data associated with the
- * fd, otherwise they will be set to NULL.
+ * Returns a value >= 0 containing an identifier (the same identifier `ident`
+ * passed to ALooper_addFd()) if its file descriptor has data and it has no
+ * callback function (requiring the caller here to handle it). In this (and
+ * only this) case outFd, outEvents and outData will contain the poll events and
+ * data associated with the fd, otherwise they will be set to NULL. The poll may
+ * also have been explicitly woken by ALooper_wake.
*
* This method does not return until it has finished invoking the appropriate callbacks
* for all file descriptors that were signalled.
@@ -210,10 +214,21 @@
* data has been consumed or a file descriptor is available with no callback.
* This function will never return ALOOPER_POLL_CALLBACK.
*
- * Removed in API 34 as ALooper_pollAll can swallow ALooper_wake calls.
- * Use ALooper_pollOnce instead.
+ * This API cannot be used safely, but a safe alternative exists (see below). As
+ * such, new builds will not be able to call this API and must migrate to the
+ * safe API. Binary compatibility is preserved to support already-compiled apps.
+ *
+ * \bug ALooper_pollAll will not wake in response to ALooper_wake calls if it
+ * also handles another event at the same time.
+ *
+ * \deprecated Calls to ALooper_pollAll should be replaced with
+ * ALooper_pollOnce. If you call ALooper_pollOnce in a loop, you *must* treat
+ * all return values as if they also indicate ALOOPER_POLL_WAKE.
*/
-int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) __REMOVED_IN(1);
+int ALooper_pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData)
+ __REMOVED_IN(1,
+ "ALooper_pollAll may ignore wakes. Use ALooper_pollOnce instead. See "
+ "The API documentation for more information");
/**
* Wakes the poll asynchronously.
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 3c82d88..97e4dc0 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -114,6 +114,9 @@
* API, the client is expected to call {@link APerformanceHint_reportActualWorkDuration} each
* cycle to report the actual time taken to complete to the system.
*
+ * Note, methods of {@link APerformanceHintSession_*} are not thread safe so callers must
+ * ensure thread safety.
+ *
* All timings should be from `std::chrono::steady_clock` or `clock_gettime(CLOCK_MONOTONIC, ...)`
*/
typedef struct APerformanceHintSession APerformanceHintSession;
diff --git a/include/android/sensor.h b/include/android/sensor.h
index a618393..e3c1ccf 100644
--- a/include/android/sensor.h
+++ b/include/android/sensor.h
@@ -60,7 +60,7 @@
#define __INTRODUCED_IN(__api_level) /* nothing */
#endif
#if !defined(__DEPRECATED_IN)
-#define __DEPRECATED_IN(__api_level) __attribute__((__deprecated__))
+#define __DEPRECATED_IN(__api_level, msg) __attribute__((__deprecated__(msg)))
#endif
#ifdef __cplusplus
@@ -748,7 +748,8 @@
* ASensorManager* sensorManager = ASensorManager_getInstance();
*
*/
-ASensorManager* ASensorManager_getInstance() __DEPRECATED_IN(26);
+ASensorManager* ASensorManager_getInstance()
+ __DEPRECATED_IN(26, "Use ASensorManager_getInstanceForPackage instead");
/**
* Get a reference to the sensor manager. ASensorManager is a singleton
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index fb1419f..099a2bc 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -62,16 +62,18 @@
*
* Available since API level 29.
*/
-ASurfaceControl* ASurfaceControl_createFromWindow(ANativeWindow* parent, const char* debug_name)
- __INTRODUCED_IN(29);
+ASurfaceControl* _Nullable ASurfaceControl_createFromWindow(ANativeWindow* _Nonnull parent,
+ const char* _Nonnull debug_name)
+ __INTRODUCED_IN(29);
/**
* See ASurfaceControl_createFromWindow.
*
* Available since API level 29.
*/
-ASurfaceControl* ASurfaceControl_create(ASurfaceControl* parent, const char* debug_name)
- __INTRODUCED_IN(29);
+ASurfaceControl* _Nullable ASurfaceControl_create(ASurfaceControl* _Nonnull parent,
+ const char* _Nonnull debug_name)
+ __INTRODUCED_IN(29);
/**
* Acquires a reference on the given ASurfaceControl object. This prevents the object
@@ -81,7 +83,7 @@
*
* Available since API level 31.
*/
-void ASurfaceControl_acquire(ASurfaceControl* surface_control) __INTRODUCED_IN(31);
+void ASurfaceControl_acquire(ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(31);
/**
* Removes a reference that was previously acquired with one of the following functions:
@@ -92,7 +94,7 @@
*
* Available since API level 29.
*/
-void ASurfaceControl_release(ASurfaceControl* surface_control) __INTRODUCED_IN(29);
+void ASurfaceControl_release(ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29);
struct ASurfaceTransaction;
@@ -108,14 +110,14 @@
*
* Available since API level 29.
*/
-ASurfaceTransaction* ASurfaceTransaction_create() __INTRODUCED_IN(29);
+ASurfaceTransaction* _Nonnull ASurfaceTransaction_create() __INTRODUCED_IN(29);
/**
* Destroys the \a transaction object.
*
* Available since API level 29.
*/
-void ASurfaceTransaction_delete(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+void ASurfaceTransaction_delete(ASurfaceTransaction* _Nullable transaction) __INTRODUCED_IN(29);
/**
* Applies the updates accumulated in \a transaction.
@@ -126,7 +128,7 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_apply(ASurfaceTransaction* transaction) __INTRODUCED_IN(29);
+void ASurfaceTransaction_apply(ASurfaceTransaction* _Nonnull transaction) __INTRODUCED_IN(29);
/**
* An opaque handle returned during a callback that can be used to query general stats and stats for
@@ -154,9 +156,9 @@
*
* Available since API level 29.
*/
-typedef void (*ASurfaceTransaction_OnComplete)(void* context, ASurfaceTransactionStats* stats)
- __INTRODUCED_IN(29);
-
+typedef void (*ASurfaceTransaction_OnComplete)(void* _Null_unspecified context,
+ ASurfaceTransactionStats* _Nonnull stats)
+ __INTRODUCED_IN(29);
/**
* The ASurfaceTransaction_OnCommit callback is invoked when transaction is applied and the updates
@@ -183,8 +185,9 @@
*
* Available since API level 31.
*/
-typedef void (*ASurfaceTransaction_OnCommit)(void* context, ASurfaceTransactionStats* stats)
- __INTRODUCED_IN(31);
+typedef void (*ASurfaceTransaction_OnCommit)(void* _Null_unspecified context,
+ ASurfaceTransactionStats* _Nonnull stats)
+ __INTRODUCED_IN(31);
/**
* Returns the timestamp of when the frame was latched by the framework. Once a frame is
@@ -192,8 +195,8 @@
*
* Available since API level 29.
*/
-int64_t ASurfaceTransactionStats_getLatchTime(ASurfaceTransactionStats* surface_transaction_stats)
- __INTRODUCED_IN(29);
+int64_t ASurfaceTransactionStats_getLatchTime(
+ ASurfaceTransactionStats* _Nonnull surface_transaction_stats) __INTRODUCED_IN(29);
/**
* Returns a sync fence that signals when the transaction has been presented.
@@ -204,8 +207,8 @@
*
* Available since API level 29.
*/
-int ASurfaceTransactionStats_getPresentFenceFd(ASurfaceTransactionStats* surface_transaction_stats)
- __INTRODUCED_IN(29);
+int ASurfaceTransactionStats_getPresentFenceFd(
+ ASurfaceTransactionStats* _Nonnull surface_transaction_stats) __INTRODUCED_IN(29);
/**
* \a outASurfaceControls returns an array of ASurfaceControl pointers that were updated during the
@@ -217,18 +220,18 @@
*
* \a outASurfaceControlsSize returns the size of the ASurfaceControls array.
*/
-void ASurfaceTransactionStats_getASurfaceControls(ASurfaceTransactionStats* surface_transaction_stats,
- ASurfaceControl*** outASurfaceControls,
- size_t* outASurfaceControlsSize)
- __INTRODUCED_IN(29);
+void ASurfaceTransactionStats_getASurfaceControls(
+ ASurfaceTransactionStats* _Nonnull surface_transaction_stats,
+ ASurfaceControl* _Nullable* _Nullable* _Nonnull outASurfaceControls,
+ size_t* _Nonnull outASurfaceControlsSize) __INTRODUCED_IN(29);
/**
* Releases the array of ASurfaceControls that were returned by
* ASurfaceTransactionStats_getASurfaceControls().
*
* Available since API level 29.
*/
-void ASurfaceTransactionStats_releaseASurfaceControls(ASurfaceControl** surface_controls)
- __INTRODUCED_IN(29);
+void ASurfaceTransactionStats_releaseASurfaceControls(
+ ASurfaceControl* _Nonnull* _Nonnull surface_controls) __INTRODUCED_IN(29);
/**
* Returns the timestamp of when the CURRENT buffer was acquired. A buffer is considered
@@ -241,9 +244,9 @@
* fence has signaled, depending on internal timing differences. Therefore the caller should
* use the acquire fence passed in to setBuffer and query the signal time.
*/
-int64_t ASurfaceTransactionStats_getAcquireTime(ASurfaceTransactionStats* surface_transaction_stats,
- ASurfaceControl* surface_control)
- __INTRODUCED_IN(29);
+int64_t ASurfaceTransactionStats_getAcquireTime(
+ ASurfaceTransactionStats* _Nonnull surface_transaction_stats,
+ ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29);
/**
* The returns the fence used to signal the release of the PREVIOUS buffer set on
@@ -268,9 +271,8 @@
* Available since API level 29.
*/
int ASurfaceTransactionStats_getPreviousReleaseFenceFd(
- ASurfaceTransactionStats* surface_transaction_stats,
- ASurfaceControl* surface_control)
- __INTRODUCED_IN(29);
+ ASurfaceTransactionStats* _Nonnull surface_transaction_stats,
+ ASurfaceControl* _Nonnull surface_control) __INTRODUCED_IN(29);
/**
* Sets the callback that will be invoked when the updates from this transaction
@@ -279,8 +281,10 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* transaction, void* context,
- ASurfaceTransaction_OnComplete func) __INTRODUCED_IN(29);
+void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* _Nonnull transaction,
+ void* _Null_unspecified context,
+ ASurfaceTransaction_OnComplete _Nonnull func)
+ __INTRODUCED_IN(29);
/**
* Sets the callback that will be invoked when the updates from this transaction are applied and are
@@ -289,8 +293,10 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* transaction, void* context,
- ASurfaceTransaction_OnCommit func) __INTRODUCED_IN(31);
+void ASurfaceTransaction_setOnCommit(ASurfaceTransaction* _Nonnull transaction,
+ void* _Null_unspecified context,
+ ASurfaceTransaction_OnCommit _Nonnull func)
+ __INTRODUCED_IN(31);
/**
* Reparents the \a surface_control from its old parent to the \a new_parent surface control.
@@ -300,14 +306,14 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_reparent(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, ASurfaceControl* new_parent)
- __INTRODUCED_IN(29);
+void ASurfaceTransaction_reparent(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ ASurfaceControl* _Nullable new_parent) __INTRODUCED_IN(29);
/**
* Parameter for ASurfaceTransaction_setVisibility().
*/
-enum {
+enum ASurfaceTransactionVisibility : int8_t {
ASURFACE_TRANSACTION_VISIBILITY_HIDE = 0,
ASURFACE_TRANSACTION_VISIBILITY_SHOW = 1,
};
@@ -318,9 +324,10 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setVisibility(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int8_t visibility)
- __INTRODUCED_IN(29);
+void ASurfaceTransaction_setVisibility(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ enum ASurfaceTransactionVisibility visibility)
+ __INTRODUCED_IN(29);
/**
* Updates the z order index for \a surface_control. Note that the z order for a surface
@@ -331,9 +338,9 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setZOrder(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int32_t z_order)
- __INTRODUCED_IN(29);
+void ASurfaceTransaction_setZOrder(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, int32_t z_order)
+ __INTRODUCED_IN(29);
/**
* Updates the AHardwareBuffer displayed for \a surface_control. If not -1, the
@@ -348,9 +355,10 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setBuffer(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, AHardwareBuffer* buffer,
- int acquire_fence_fd) __INTRODUCED_IN(29);
+void ASurfaceTransaction_setBuffer(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ AHardwareBuffer* _Nonnull buffer, int acquire_fence_fd)
+ __INTRODUCED_IN(29);
/**
* Updates the color for \a surface_control. This will make the background color for the
@@ -360,23 +368,23 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setColor(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, float r, float g, float b,
- float alpha, ADataSpace dataspace)
- __INTRODUCED_IN(29);
+void ASurfaceTransaction_setColor(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, float r, float g,
+ float b, float alpha, enum ADataSpace dataspace)
+ __INTRODUCED_IN(29);
/**
* \param source The sub-rect within the buffer's content to be rendered inside the surface's area
* The surface's source rect is clipped by the bounds of its current buffer. The source rect's width
* and height must be > 0.
*
- * \param destination Specifies the rect in the parent's space where this surface will be drawn. The post
- * source rect bounds are scaled to fit the destination rect. The surface's destination rect is
+ * \param destination Specifies the rect in the parent's space where this surface will be drawn. The
+ * post source rect bounds are scaled to fit the destination rect. The surface's destination rect is
* clipped by the bounds of its parent. The destination rect's width and height must be > 0.
*
- * \param transform The transform applied after the source rect is applied to the buffer. This parameter
- * should be set to 0 for no transform. To specify a transfrom use the NATIVE_WINDOW_TRANSFORM_*
- * enum.
+ * \param transform The transform applied after the source rect is applied to the buffer. This
+ * parameter should be set to 0 for no transform. To specify a transfrom use the
+ * NATIVE_WINDOW_TRANSFORM_* enum.
*
* Available since API level 29.
*
@@ -385,10 +393,10 @@
* to set different properties at different times, instead of having to specify all the desired
* properties at once.
*/
-void ASurfaceTransaction_setGeometry(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, const ARect& source,
+void ASurfaceTransaction_setGeometry(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, const ARect& source,
const ARect& destination, int32_t transform)
- __INTRODUCED_IN(29);
+ __INTRODUCED_IN(29);
/**
* Bounds the surface and its children to the bounds specified. The crop and buffer size will be
@@ -399,9 +407,9 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setCrop(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, const ARect& crop)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setCrop(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, const ARect& crop)
+ __INTRODUCED_IN(31);
/**
* Specifies the position in the parent's space where the surface will be drawn.
@@ -411,9 +419,9 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setPosition(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int32_t x, int32_t y)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setPosition(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, int32_t x,
+ int32_t y) __INTRODUCED_IN(31);
/**
* \param transform The transform applied after the source rect is applied to the buffer. This
@@ -422,9 +430,9 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int32_t transform)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setBufferTransform(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ int32_t transform) __INTRODUCED_IN(31);
/**
* Sets an x and y scale of a surface with (0, 0) as the centerpoint of the scale.
@@ -434,13 +442,13 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setScale(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, float xScale, float yScale)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setScale(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, float xScale,
+ float yScale) __INTRODUCED_IN(31);
/**
* Parameter for ASurfaceTransaction_setBufferTransparency().
*/
-enum {
+enum ASurfaceTransactionTransparency : int8_t {
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSPARENT = 0,
ASURFACE_TRANSACTION_TRANSPARENCY_TRANSLUCENT = 1,
ASURFACE_TRANSACTION_TRANSPARENCY_OPAQUE = 2,
@@ -452,9 +460,9 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
- int8_t transparency)
+void ASurfaceTransaction_setBufferTransparency(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ enum ASurfaceTransactionTransparency transparency)
__INTRODUCED_IN(29);
/**
@@ -463,9 +471,10 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, const ARect rects[],
- uint32_t count) __INTRODUCED_IN(29);
+void ASurfaceTransaction_setDamageRegion(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ const ARect* _Nullable rects, uint32_t count)
+ __INTRODUCED_IN(29);
/**
* Specifies a desiredPresentTime for the transaction. The framework will try to present
@@ -479,7 +488,7 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setDesiredPresentTime(ASurfaceTransaction* _Nonnull transaction,
int64_t desiredPresentTime) __INTRODUCED_IN(29);
/**
@@ -489,8 +498,8 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, float alpha)
+void ASurfaceTransaction_setBufferAlpha(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, float alpha)
__INTRODUCED_IN(29);
/**
@@ -500,9 +509,9 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, ADataSpace data_space)
- __INTRODUCED_IN(29);
+void ASurfaceTransaction_setBufferDataSpace(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ enum ADataSpace data_space) __INTRODUCED_IN(29);
/**
* SMPTE ST 2086 "Mastering Display Color Volume" static metadata
@@ -512,9 +521,9 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
- struct AHdrMetadata_smpte2086* metadata)
+void ASurfaceTransaction_setHdrMetadata_smpte2086(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ struct AHdrMetadata_smpte2086* _Nullable metadata)
__INTRODUCED_IN(29);
/**
@@ -525,9 +534,9 @@
*
* Available since API level 29.
*/
-void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
- struct AHdrMetadata_cta861_3* metadata)
+void ASurfaceTransaction_setHdrMetadata_cta861_3(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ struct AHdrMetadata_cta861_3* _Nullable metadata)
__INTRODUCED_IN(29);
/**
@@ -577,10 +586,10 @@
*
* Available since API level 34.
*/
-void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
- float currentBufferRatio,
- float desiredRatio) __INTRODUCED_IN(__ANDROID_API_U__);
+void ASurfaceTransaction_setExtendedRangeBrightness(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ float currentBufferRatio, float desiredRatio)
+ __INTRODUCED_IN(__ANDROID_API_U__);
/**
* Sets the desired HDR headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
@@ -616,8 +625,8 @@
*
* Available since API level 35.
*/
-void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
+void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
float desiredHeadroom)
__INTRODUCED_IN(__ANDROID_API_V__);
@@ -629,8 +638,8 @@
*
* Available since API level 30.
*/
-void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, float frameRate,
+void ASurfaceTransaction_setFrameRate(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control, float frameRate,
int8_t compatibility) __INTRODUCED_IN(30);
/**
@@ -665,10 +674,11 @@
*
* Available since API level 31.
*/
-void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, float frameRate,
- int8_t compatibility, int8_t changeFrameRateStrategy)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setFrameRateWithChangeStrategy(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ float frameRate, int8_t compatibility,
+ int8_t changeFrameRateStrategy)
+ __INTRODUCED_IN(31);
/**
* Clears the frame rate which is set for \a surface_control.
@@ -691,8 +701,8 @@
*
* Available since API level 34.
*/
-void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control)
+void ASurfaceTransaction_clearFrameRate(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control)
__INTRODUCED_IN(__ANDROID_API_U__);
/**
@@ -721,10 +731,9 @@
* \param surface_control The ASurfaceControl on which to control buffer backpressure behavior.
* \param enableBackPressure Whether to enable back pressure.
*/
-void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control,
- bool enableBackPressure)
- __INTRODUCED_IN(31);
+void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* _Nonnull transaction,
+ ASurfaceControl* _Nonnull surface_control,
+ bool enableBackPressure) __INTRODUCED_IN(31);
/**
* Sets the frame timeline to use in SurfaceFlinger.
@@ -744,7 +753,7 @@
* to the corresponding expected presentation time and deadline from the frame to be rendered. A
* stale or invalid value will be ignored.
*/
-void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
+void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* _Nonnull transaction,
AVsyncId vsyncId) __INTRODUCED_IN(33);
__END_DECLS
diff --git a/include/ftl/algorithm.h b/include/ftl/algorithm.h
index c0f6768..68826bb 100644
--- a/include/ftl/algorithm.h
+++ b/include/ftl/algorithm.h
@@ -24,6 +24,18 @@
namespace android::ftl {
+// Determines if a container contains a value. This is a simplified version of the C++23
+// std::ranges::contains function.
+//
+// const ftl::StaticVector vector = {1, 2, 3};
+// assert(ftl::contains(vector, 1));
+//
+// TODO: Remove in C++23.
+template <typename Container, typename Value>
+auto contains(const Container& container, const Value& value) -> bool {
+ return std::find(container.begin(), container.end(), value) != container.end();
+}
+
// Adapter for std::find_if that converts the return value from iterator to optional.
//
// const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv};
diff --git a/include/ftl/details/hash.h b/include/ftl/details/hash.h
new file mode 100644
index 0000000..230ae51
--- /dev/null
+++ b/include/ftl/details/hash.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <cinttypes>
+#include <cstring>
+
+namespace android::ftl::details {
+
+// Based on CityHash64 v1.0.1 (http://code.google.com/p/cityhash/), but slightly
+// modernized and trimmed for cases with bounded lengths.
+
+template <typename T = std::uint64_t>
+inline T read_unaligned(const void* ptr) {
+ T v;
+ std::memcpy(&v, ptr, sizeof(T));
+ return v;
+}
+
+template <bool NonZeroShift = false>
+constexpr std::uint64_t rotate(std::uint64_t v, std::uint8_t shift) {
+ if constexpr (!NonZeroShift) {
+ if (shift == 0) return v;
+ }
+ return (v >> shift) | (v << (64 - shift));
+}
+
+constexpr std::uint64_t shift_mix(std::uint64_t v) {
+ return v ^ (v >> 47);
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+constexpr std::uint64_t hash_length_16(std::uint64_t u, std::uint64_t v) {
+ constexpr std::uint64_t kPrime = 0x9ddfea08eb382d69ull;
+ auto a = (u ^ v) * kPrime;
+ a ^= (a >> 47);
+ auto b = (v ^ a) * kPrime;
+ b ^= (b >> 47);
+ b *= kPrime;
+ return b;
+}
+
+constexpr std::uint64_t kPrime0 = 0xc3a5c85c97cb3127ull;
+constexpr std::uint64_t kPrime1 = 0xb492b66fbe98f273ull;
+constexpr std::uint64_t kPrime2 = 0x9ae16a3b2f90404full;
+constexpr std::uint64_t kPrime3 = 0xc949d7c7509e6557ull;
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_0_to_16(const char* str, std::uint64_t length) {
+ if (length > 8) {
+ const auto a = read_unaligned(str);
+ const auto b = read_unaligned(str + length - 8);
+ return hash_length_16(a, rotate<true>(b + length, static_cast<std::uint8_t>(length))) ^ b;
+ }
+ if (length >= 4) {
+ const auto a = read_unaligned<std::uint32_t>(str);
+ const auto b = read_unaligned<std::uint32_t>(str + length - 4);
+ return hash_length_16(length + (a << 3), b);
+ }
+ if (length > 0) {
+ const auto a = static_cast<unsigned char>(str[0]);
+ const auto b = static_cast<unsigned char>(str[length >> 1]);
+ const auto c = static_cast<unsigned char>(str[length - 1]);
+ const auto y = static_cast<std::uint32_t>(a) + (static_cast<std::uint32_t>(b) << 8);
+ const auto z = static_cast<std::uint32_t>(length) + (static_cast<std::uint32_t>(c) << 2);
+ return shift_mix(y * kPrime2 ^ z * kPrime3) * kPrime2;
+ }
+ return kPrime2;
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_17_to_32(const char* str, std::uint64_t length) {
+ const auto a = read_unaligned(str) * kPrime1;
+ const auto b = read_unaligned(str + 8);
+ const auto c = read_unaligned(str + length - 8) * kPrime2;
+ const auto d = read_unaligned(str + length - 16) * kPrime0;
+ return hash_length_16(rotate(a - b, 43) + rotate(c, 30) + d,
+ a + rotate(b ^ kPrime3, 20) - c + length);
+}
+
+__attribute__((no_sanitize("unsigned-integer-overflow")))
+inline std::uint64_t hash_length_33_to_64(const char* str, std::uint64_t length) {
+ auto z = read_unaligned(str + 24);
+ auto a = read_unaligned(str) + (length + read_unaligned(str + length - 16)) * kPrime0;
+ auto b = rotate(a + z, 52);
+ auto c = rotate(a, 37);
+
+ a += read_unaligned(str + 8);
+ c += rotate(a, 7);
+ a += read_unaligned(str + 16);
+
+ const auto vf = a + z;
+ const auto vs = b + rotate(a, 31) + c;
+
+ a = read_unaligned(str + 16) + read_unaligned(str + length - 32);
+ z += read_unaligned(str + length - 8);
+ b = rotate(a + z, 52);
+ c = rotate(a, 37);
+ a += read_unaligned(str + length - 24);
+ c += rotate(a, 7);
+ a += read_unaligned(str + length - 16);
+
+ const auto wf = a + z;
+ const auto ws = b + rotate(a, 31) + c;
+ const auto r = shift_mix((vf + ws) * kPrime2 + (wf + vs) * kPrime0);
+ return shift_mix(r * kPrime0 + vs) * kPrime2;
+}
+
+} // namespace android::ftl::details
diff --git a/include/ftl/hash.h b/include/ftl/hash.h
new file mode 100644
index 0000000..29a6f71
--- /dev/null
+++ b/include/ftl/hash.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <cinttypes>
+#include <optional>
+#include <string_view>
+
+#include <ftl/details/hash.h>
+
+namespace android::ftl {
+
+// Non-cryptographic hash function (namely CityHash64) for strings with at most 64 characters.
+// Unlike std::hash, which returns std::size_t and is only required to produce the same result
+// for the same input within a single execution of a program, this hash is stable.
+inline std::optional<std::uint64_t> stable_hash(std::string_view view) {
+ const auto length = view.length();
+ if (length <= 16) {
+ return details::hash_length_0_to_16(view.data(), length);
+ }
+ if (length <= 32) {
+ return details::hash_length_17_to_32(view.data(), length);
+ }
+ if (length <= 64) {
+ return details::hash_length_33_to_64(view.data(), length);
+ }
+ return {};
+}
+
+} // namespace android::ftl
diff --git a/include/ftl/non_null.h b/include/ftl/non_null.h
index 35d09d7..4a5d8bf 100644
--- a/include/ftl/non_null.h
+++ b/include/ftl/non_null.h
@@ -68,26 +68,28 @@
constexpr NonNull(const NonNull&) = default;
constexpr NonNull& operator=(const NonNull&) = default;
- constexpr const Pointer& get() const { return pointer_; }
- constexpr explicit operator const Pointer&() const { return get(); }
+ [[nodiscard]] constexpr const Pointer& get() const { return pointer_; }
+ [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); }
// Move operations. These break the invariant, so care must be taken to avoid subsequent access.
constexpr NonNull(NonNull&&) = default;
constexpr NonNull& operator=(NonNull&&) = default;
- constexpr Pointer take() && { return std::move(pointer_); }
- constexpr explicit operator Pointer() && { return take(); }
+ [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); }
+ [[nodiscard]] constexpr explicit operator Pointer() && { return take(); }
// Dereferencing.
- constexpr decltype(auto) operator*() const { return *get(); }
- constexpr decltype(auto) operator->() const { return get(); }
+ [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); }
+ [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); }
+
+ [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); }
// Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
// through the passkey idiom, for clear compilation errors.
template <typename P>
constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
- if (!pointer_) std::abort();
+ if (pointer_ == nullptr) std::abort();
}
private:
@@ -98,11 +100,13 @@
};
template <typename P>
-constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
+[[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
return {Passkey{}, std::forward<P>(pointer)};
}
+// NonNull<P> <=> NonNull<Q>
+
template <typename P, typename Q>
constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
return lhs.get() == rhs.get();
@@ -113,4 +117,96 @@
return !operator==(lhs, rhs);
}
+template <typename P, typename Q>
+constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() < rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() <= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() >= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
+ return lhs.get() > rhs.get();
+}
+
+// NonNull<P> <=> Q
+
+template <typename P, typename Q>
+constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() == rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() != rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() < rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() <= rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() >= rhs;
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) {
+ return lhs.get() > rhs;
+}
+
+// P <=> NonNull<Q>
+
+template <typename P, typename Q>
+constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs == rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs != rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs < rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs <= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs >= rhs.get();
+}
+
+template <typename P, typename Q>
+constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) {
+ return lhs > rhs.get();
+}
+
} // namespace android::ftl
+
+// Specialize std::hash for ftl::NonNull<T>
+template <typename P>
+struct std::hash<android::ftl::NonNull<P>> {
+ std::size_t operator()(const android::ftl::NonNull<P>& ptr) const {
+ return std::hash<P>()(ptr.get());
+ }
+};
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index b0eceef..97c2ab8 100644
--- a/include/input/DisplayViewport.h
+++ b/include/input/DisplayViewport.h
@@ -19,7 +19,6 @@
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
#include <ftl/string.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <ui/Rotation.h>
@@ -47,7 +46,7 @@
* See com.android.server.display.DisplayViewport.
*/
struct DisplayViewport {
- int32_t displayId; // -1 if invalid
+ ui::LogicalDisplayId displayId; // ADISPLAY_ID_NONE if invalid
ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
@@ -67,7 +66,7 @@
ViewportType type;
DisplayViewport()
- : displayId(ADISPLAY_ID_NONE),
+ : displayId(ui::ADISPLAY_ID_NONE),
orientation(ui::ROTATION_0),
logicalLeft(0),
logicalTop(0),
@@ -99,12 +98,10 @@
return !(*this == other);
}
- inline bool isValid() const {
- return displayId >= 0;
- }
+ inline bool isValid() const { return displayId.isValid(); }
void setNonDisplayViewport(int32_t width, int32_t height) {
- displayId = ADISPLAY_ID_NONE;
+ displayId = ui::ADISPLAY_ID_NONE;
orientation = ui::ROTATION_0;
logicalLeft = 0;
logicalTop = 0;
@@ -123,12 +120,13 @@
}
std::string toString() const {
- return StringPrintf("Viewport %s: displayId=%d, uniqueId=%s, port=%s, orientation=%d, "
+ return StringPrintf("Viewport %s: displayId=%s, uniqueId=%s, port=%s, orientation=%d, "
"logicalFrame=[%d, %d, %d, %d], "
"physicalFrame=[%d, %d, %d, %d], "
"deviceSize=[%d, %d], "
"isActive=[%d]",
- ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(),
+ ftl::enum_string(type).c_str(), displayId.toString().c_str(),
+ uniqueId.c_str(),
physicalPort ? ftl::to_string(*physicalPort).c_str() : "<none>",
static_cast<int>(orientation), logicalLeft, logicalTop, logicalRight,
logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom,
diff --git a/include/input/Input.h b/include/input/Input.h
index 19f4ab3..092a698 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -26,8 +26,10 @@
#ifdef __linux__
#include <android/os/IInputConstants.h>
#endif
+#include <android/os/PointerIconType.h>
#include <math.h>
#include <stdint.h>
+#include <ui/LogicalDisplayId.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -42,7 +44,7 @@
#ifdef __linux__
/* This event was generated or modified by accessibility service. */
AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
+ android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
#else
AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
#endif
@@ -53,11 +55,11 @@
AKEY_EVENT_FLAG_START_TRACKING = 0x40000000,
/* Key event is inconsistent with previously sent key events. */
- AKEY_EVENT_FLAG_TAINTED = 0x80000000,
+ AKEY_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
};
enum {
-
+ // AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED is defined in include/android/input.h
/**
* This flag indicates that the window that received this motion event is partly
* or wholly obscured by another visible window above it. This flag is set to true
@@ -68,13 +70,16 @@
* to drop the suspect touches or to take additional precautions to confirm the user's
* actual intent.
*/
- AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2,
-
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED =
+ android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED,
+ AMOTION_EVENT_FLAG_HOVER_EXIT_PENDING =
+ android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING,
/**
* This flag indicates that the event has been generated by a gesture generator. It
* provides a hint to the GestureDetector to not apply any touch slop.
*/
- AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8,
+ AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE =
+ android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE,
/**
* This flag indicates that the event will not cause a focus change if it is directed to an
@@ -82,20 +87,24 @@
* gestures to allow the user to direct gestures to an unfocused window without bringing it
* into focus.
*/
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE =
+ android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE,
#if defined(__linux__)
/**
* This event was generated or modified by accessibility service.
*/
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT =
- android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT, // 0x800,
+ android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
#else
AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
#endif
+ AMOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS =
+ android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS,
+
/* Motion event is inconsistent with previously sent motion events. */
- AMOTION_EVENT_FLAG_TAINTED = 0x80000000,
+ AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED,
};
/**
@@ -115,9 +124,10 @@
/**
* This flag indicates that the point up event has been canceled.
* Typically this is used for palm event when the user has accidental touches.
- * TODO: Adjust flag to public api
+ * TODO(b/338143308): Add this to NDK
*/
-constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20;
+constexpr int32_t AMOTION_EVENT_FLAG_CANCELED =
+ android::os::IInputConstants::INPUT_EVENT_FLAG_CANCELED;
enum {
/*
@@ -538,9 +548,9 @@
inline void setSource(uint32_t source) { mSource = source; }
- inline int32_t getDisplayId() const { return mDisplayId; }
+ inline ui::LogicalDisplayId getDisplayId() const { return mDisplayId; }
- inline void setDisplayId(int32_t displayId) { mDisplayId = displayId; }
+ inline void setDisplayId(ui::LogicalDisplayId displayId) { mDisplayId = displayId; }
inline std::array<uint8_t, 32> getHmac() const { return mHmac; }
@@ -549,7 +559,7 @@
bool operator==(const InputEvent&) const = default;
protected:
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac);
void initialize(const InputEvent& from);
@@ -557,7 +567,7 @@
int32_t mId;
DeviceId mDeviceId;
uint32_t mSource;
- int32_t mDisplayId;
+ ui::LogicalDisplayId mDisplayId{ui::ADISPLAY_ID_NONE};
std::array<uint8_t, 32> mHmac;
};
@@ -593,7 +603,7 @@
static const char* getLabel(int32_t keyCode);
static std::optional<int> getKeyCodeFromLabel(const char* label);
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t flags, int32_t keyCode,
int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime,
nsecs_t eventTime);
@@ -855,7 +865,7 @@
ssize_t findPointerIndex(int32_t pointerId) const;
- void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
+ void initialize(int32_t id, DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
@@ -1064,7 +1074,7 @@
DeviceId deviceId;
nsecs_t eventTimeNanos;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
};
/**
@@ -1213,43 +1223,41 @@
*
* Due to backwards compatibility and public api constraints, this is a duplicate (but type safe)
* definition of PointerIcon.java.
- *
- * TODO(b/235023317) move this definition to an aidl and statically assign to the below java public
- * api values.
- *
- * WARNING: Keep these definitions in sync with
- * frameworks/base/core/java/android/view/PointerIcon.java
*/
enum class PointerIconStyle : int32_t {
- TYPE_CUSTOM = -1,
- TYPE_NULL = 0,
- TYPE_NOT_SPECIFIED = 1,
- TYPE_ARROW = 1000,
- TYPE_CONTEXT_MENU = 1001,
- TYPE_HAND = 1002,
- TYPE_HELP = 1003,
- TYPE_WAIT = 1004,
- TYPE_CELL = 1006,
- TYPE_CROSSHAIR = 1007,
- TYPE_TEXT = 1008,
- TYPE_VERTICAL_TEXT = 1009,
- TYPE_ALIAS = 1010,
- TYPE_COPY = 1011,
- TYPE_NO_DROP = 1012,
- TYPE_ALL_SCROLL = 1013,
- TYPE_HORIZONTAL_DOUBLE_ARROW = 1014,
- TYPE_VERTICAL_DOUBLE_ARROW = 1015,
- TYPE_TOP_RIGHT_DOUBLE_ARROW = 1016,
- TYPE_TOP_LEFT_DOUBLE_ARROW = 1017,
- TYPE_ZOOM_IN = 1018,
- TYPE_ZOOM_OUT = 1019,
- TYPE_GRAB = 1020,
- TYPE_GRABBING = 1021,
- TYPE_HANDWRITING = 1022,
+ TYPE_CUSTOM = static_cast<int32_t>(::android::os::PointerIconType::CUSTOM),
+ TYPE_NULL = static_cast<int32_t>(::android::os::PointerIconType::TYPE_NULL),
+ TYPE_NOT_SPECIFIED = static_cast<int32_t>(::android::os::PointerIconType::NOT_SPECIFIED),
+ TYPE_ARROW = static_cast<int32_t>(::android::os::PointerIconType::ARROW),
+ TYPE_CONTEXT_MENU = static_cast<int32_t>(::android::os::PointerIconType::CONTEXT_MENU),
+ TYPE_HAND = static_cast<int32_t>(::android::os::PointerIconType::HAND),
+ TYPE_HELP = static_cast<int32_t>(::android::os::PointerIconType::HELP),
+ TYPE_WAIT = static_cast<int32_t>(::android::os::PointerIconType::WAIT),
+ TYPE_CELL = static_cast<int32_t>(::android::os::PointerIconType::CELL),
+ TYPE_CROSSHAIR = static_cast<int32_t>(::android::os::PointerIconType::CROSSHAIR),
+ TYPE_TEXT = static_cast<int32_t>(::android::os::PointerIconType::TEXT),
+ TYPE_VERTICAL_TEXT = static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_TEXT),
+ TYPE_ALIAS = static_cast<int32_t>(::android::os::PointerIconType::ALIAS),
+ TYPE_COPY = static_cast<int32_t>(::android::os::PointerIconType::COPY),
+ TYPE_NO_DROP = static_cast<int32_t>(::android::os::PointerIconType::NO_DROP),
+ TYPE_ALL_SCROLL = static_cast<int32_t>(::android::os::PointerIconType::ALL_SCROLL),
+ TYPE_HORIZONTAL_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::HORIZONTAL_DOUBLE_ARROW),
+ TYPE_VERTICAL_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::VERTICAL_DOUBLE_ARROW),
+ TYPE_TOP_RIGHT_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::TOP_RIGHT_DOUBLE_ARROW),
+ TYPE_TOP_LEFT_DOUBLE_ARROW =
+ static_cast<int32_t>(::android::os::PointerIconType::TOP_LEFT_DOUBLE_ARROW),
+ TYPE_ZOOM_IN = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_IN),
+ TYPE_ZOOM_OUT = static_cast<int32_t>(::android::os::PointerIconType::ZOOM_OUT),
+ TYPE_GRAB = static_cast<int32_t>(::android::os::PointerIconType::GRAB),
+ TYPE_GRABBING = static_cast<int32_t>(::android::os::PointerIconType::GRABBING),
+ TYPE_HANDWRITING = static_cast<int32_t>(::android::os::PointerIconType::HANDWRITING),
- TYPE_SPOT_HOVER = 2000,
- TYPE_SPOT_TOUCH = 2001,
- TYPE_SPOT_ANCHOR = 2002,
+ TYPE_SPOT_HOVER = static_cast<int32_t>(::android::os::PointerIconType::SPOT_HOVER),
+ TYPE_SPOT_TOUCH = static_cast<int32_t>(::android::os::PointerIconType::SPOT_TOUCH),
+ TYPE_SPOT_ANCHOR = static_cast<int32_t>(::android::os::PointerIconType::SPOT_ANCHOR),
};
} // namespace android
diff --git a/include/input/InputConsumer.h b/include/input/InputConsumer.h
index 560e804..611478c 100644
--- a/include/input/InputConsumer.h
+++ b/include/input/InputConsumer.h
@@ -111,6 +111,11 @@
std::shared_ptr<InputChannel> mChannel;
+ // TODO(b/311142655): delete this temporary tracing after the ANR bug is fixed
+ const std::string mProcessingTraceTag;
+ const std::string mLifetimeTraceTag;
+ const int32_t mLifetimeTraceCookie;
+
// The current input message.
InputMessage mMsg;
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 57b659d..8783d9c 100644
--- a/include/input/InputDevice.h
+++ b/include/input/InputDevice.h
@@ -115,6 +115,8 @@
ACCURACY_LOW = 1,
ACCURACY_MEDIUM = 2,
ACCURACY_HIGH = 3,
+
+ ftl_last = ACCURACY_HIGH,
};
enum class InputDeviceSensorReportingMode : int32_t {
@@ -128,8 +130,9 @@
INPUT = 0,
PLAYER_ID = 1,
KEYBOARD_BACKLIGHT = 2,
+ KEYBOARD_MIC_MUTE = 3,
- ftl_last = KEYBOARD_BACKLIGHT
+ ftl_last = KEYBOARD_MIC_MUTE
};
enum class InputDeviceLightCapability : uint32_t {
@@ -277,8 +280,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,
- InputDeviceViewBehavior viewBehavior = {{}});
+ bool isExternal, bool hasMic, ui::LogicalDisplayId associatedDisplayId,
+ InputDeviceViewBehavior viewBehavior = {{}}, bool enabled = true);
inline int32_t getId() const { return mId; }
inline int32_t getControllerNumber() const { return mControllerNumber; }
@@ -345,7 +348,10 @@
}
inline std::optional<InputDeviceUsiVersion> getUsiVersion() const { return mUsiVersion; }
- inline int32_t getAssociatedDisplayId() const { return mAssociatedDisplayId; }
+ inline ui::LogicalDisplayId getAssociatedDisplayId() const { return mAssociatedDisplayId; }
+
+ inline void setEnabled(bool enabled) { mEnabled = enabled; }
+ inline bool isEnabled() const { return mEnabled; }
private:
int32_t mId;
@@ -360,7 +366,8 @@
int32_t mKeyboardType;
std::shared_ptr<KeyCharacterMap> mKeyCharacterMap;
std::optional<InputDeviceUsiVersion> mUsiVersion;
- int32_t mAssociatedDisplayId;
+ ui::LogicalDisplayId mAssociatedDisplayId{ui::ADISPLAY_ID_NONE};
+ bool mEnabled;
bool mHasVibrator;
bool mHasBattery;
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index c0c5e24..837e70e 100644
--- a/include/input/InputEventBuilders.h
+++ b/include/input/InputEventBuilders.h
@@ -18,7 +18,6 @@
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
@@ -83,7 +82,7 @@
return *this;
}
- MotionEventBuilder& displayId(int32_t displayId) {
+ MotionEventBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -159,7 +158,7 @@
int32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::ADISPLAY_ID_DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
int32_t mFlags{0};
@@ -209,7 +208,7 @@
return *this;
}
- KeyEventBuilder& displayId(int32_t displayId) {
+ KeyEventBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -248,7 +247,7 @@
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::ADISPLAY_ID_DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mFlags{0};
int32_t mKeyCode{AKEYCODE_UNKNOWN};
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 5f9c8f5..6548810 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -353,9 +353,10 @@
* Other errors probably indicate that the channel is broken.
*/
status_t publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
- int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime, nsecs_t eventTime);
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime);
/* Publishes a motion event to the input channel.
*
@@ -366,9 +367,9 @@
* Other errors probably indicate that the channel is broken.
*/
status_t publishMotionEvent(uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
- int32_t displayId, std::array<uint8_t, 32> hmac, int32_t action,
- int32_t actionButton, int32_t flags, int32_t edgeFlags,
- int32_t metaState, int32_t buttonState,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags,
+ int32_t edgeFlags, int32_t metaState, int32_t buttonState,
MotionClassification classification, const ui::Transform& transform,
float xPrecision, float yPrecision, float xCursorPosition,
float yCursorPosition, const ui::Transform& rawTransform,
diff --git a/include/input/MotionPredictor.h b/include/input/MotionPredictor.h
index 3b6e401..f715039 100644
--- a/include/input/MotionPredictor.h
+++ b/include/input/MotionPredictor.h
@@ -16,6 +16,7 @@
#pragma once
+#include <array>
#include <cstdint>
#include <memory>
#include <mutex>
@@ -28,6 +29,7 @@
#include <android/sysprop/InputProperties.sysprop.h>
#include <input/Input.h>
#include <input/MotionPredictorMetricsManager.h>
+#include <input/RingBuffer.h>
#include <input/TfLiteMotionPredictor.h>
#include <utils/Timers.h> // for nsecs_t
@@ -37,6 +39,31 @@
return sysprop::InputProperties::enable_motion_prediction().value_or(true);
}
+// Tracker to calculate jerk from motion position samples.
+class JerkTracker {
+public:
+ // Initialize the tracker. If normalizedDt is true, assume that each sample pushed has dt=1.
+ JerkTracker(bool normalizedDt);
+
+ // Add a position to the tracker and update derivative estimates.
+ void pushSample(int64_t timestamp, float xPos, float yPos);
+
+ // Reset JerkTracker for a new motion input.
+ void reset();
+
+ // Return last jerk calculation, if enough samples have been collected.
+ // Jerk is defined as the 3rd derivative of position (change in
+ // acceleration) and has the units of d^3p/dt^3.
+ std::optional<float> jerkMagnitude() const;
+
+private:
+ const bool mNormalizedDt;
+
+ RingBuffer<int64_t> mTimestamps{4};
+ std::array<float, 4> mXDerivatives{}; // [x, x', x'', x''']
+ std::array<float, 4> mYDerivatives{}; // [y, y', y'', y''']
+};
+
/**
* Given a set of MotionEvents for the current gesture, predict the motion. The returned MotionEvent
* contains a set of samples in the future.
@@ -97,6 +124,11 @@
std::unique_ptr<TfLiteMotionPredictorBuffers> mBuffers;
std::optional<MotionEvent> mLastEvent;
+ // mJerkTracker assumes normalized dt = 1 between recorded samples because
+ // the underlying mModel input also assumes fixed-interval samples.
+ // Normalized dt as 1 is also used to correspond with the similar Jank
+ // implementation from the JetPack MotionPredictor implementation.
+ JerkTracker mJerkTracker{true};
std::optional<MotionPredictorMetricsManager> mMetricsManager;
diff --git a/include/input/TfLiteMotionPredictor.h b/include/input/TfLiteMotionPredictor.h
index 2edc138..728a8e1 100644
--- a/include/input/TfLiteMotionPredictor.h
+++ b/include/input/TfLiteMotionPredictor.h
@@ -105,6 +105,11 @@
// The noise floor for predictions.
// Distances (r) less than this should be discarded as noise.
float distanceNoiseFloor = 0;
+
+ // Low and high jerk thresholds (with normalized dt = 1) for predictions.
+ // High jerk means more predictions will be pruned, vice versa for low.
+ float lowJerk = 0;
+ float highJerk = 0;
};
// Creates a model from an encoded Flatbuffer model.
diff --git a/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index d8f9db4..8c356d0 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -18,6 +18,7 @@
#define ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
#include <stdint.h>
+#include <android/performance_hint.h>
__BEGIN_DECLS
@@ -75,6 +76,15 @@
GPU_LOAD_RESET = 7,
};
+// Allows access to PowerHAL's SessionTags without needing to import its AIDL
+enum class SessionTag : int32_t {
+ OTHER = 0,
+ SURFACEFLINGER = 1,
+ HWUI = 2,
+ GAME = 3,
+ APP = 4,
+};
+
/**
* Sends performance hints to inform the hint session of changes in the workload.
*
@@ -83,14 +93,22 @@
* @return 0 on success
* EPIPE if communication with the system service has failed.
*/
-int APerformanceHint_sendHint(void* session, SessionHint hint);
+int APerformanceHint_sendHint(APerformanceHintSession* session, SessionHint hint);
/**
* Return the list of thread ids, this API should only be used for testing only.
*/
-int APerformanceHint_getThreadIds(void* aPerformanceHintSession,
+int APerformanceHint_getThreadIds(APerformanceHintSession* session,
int32_t* const threadIds, size_t* const size);
+/**
+ * Creates a session with additional options
+ */
+APerformanceHintSession* APerformanceHint_createSessionInternal(APerformanceHintManager* manager,
+ const int32_t* threadIds, size_t size,
+ int64_t initialTargetWorkDurationNanos, SessionTag tag);
+
+
__END_DECLS
#endif // ANDROID_PRIVATE_NATIVE_PERFORMANCE_HINT_PRIVATE_H
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 7da8d51..04b7186 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -62,6 +62,12 @@
void setState(state_t state, time_t timestamp);
+ /**
+ * Copies the current state and accumulated times-in-state from the source. Resets
+ * the accumulated value.
+ */
+ void copyStatesFrom(const MultiStateCounter<T>& source);
+
void setValue(state_t state, const T& value);
/**
@@ -193,6 +199,22 @@
}
template <class T>
+void MultiStateCounter<T>::copyStatesFrom(const MultiStateCounter<T>& source) {
+ if (stateCount != source.stateCount) {
+ ALOGE("State count mismatch: %u vs. %u\n", stateCount, source.stateCount);
+ return;
+ }
+
+ currentState = source.currentState;
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = source.states[i].timeInStateSinceUpdate;
+ states[i].counter = emptyValue;
+ }
+ lastStateChangeTimestamp = source.lastStateChangeTimestamp;
+ lastUpdateTimestamp = source.lastUpdateTimestamp;
+}
+
+template <class T>
void MultiStateCounter<T>::setValue(state_t state, const T& value) {
states[state].counter = value;
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index cb11a54..a51a38a 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -72,6 +72,22 @@
EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
}
+TEST_F(MultiStateCounterTest, copyStatesFrom) {
+ DoubleMultiStateCounter sourceCounter(3, 0);
+
+ sourceCounter.updateValue(0, 0);
+ sourceCounter.setState(1, 0);
+ sourceCounter.setState(2, 1000);
+
+ DoubleMultiStateCounter testCounter(3, 0);
+ testCounter.copyStatesFrom(sourceCounter);
+ testCounter.updateValue(6.0, 3000);
+
+ EXPECT_DOUBLE_EQ(0, testCounter.getCount(0));
+ EXPECT_DOUBLE_EQ(2.0, testCounter.getCount(1));
+ EXPECT_DOUBLE_EQ(4.0, testCounter.getCount(2));
+}
+
TEST_F(MultiStateCounterTest, setEnabled) {
DoubleMultiStateCounter testCounter(3, 0);
testCounter.updateValue(0, 0);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index ca9b08f..090e35b 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -161,7 +161,7 @@
},
android: {
lto: {
- thin: true,
+ thin: true,
},
},
},
@@ -229,6 +229,7 @@
cc_library_headers {
name: "trusty_mock_headers",
host_supported: true,
+ vendor_available: true,
export_include_dirs: [
"trusty/include",
@@ -243,12 +244,18 @@
cc_defaults {
name: "trusty_mock_defaults",
host_supported: true,
+ vendor_available: true,
header_libs: [
"libbinder_headers_base",
"liblog_stub",
"trusty_mock_headers",
],
+ export_header_lib_headers: [
+ "libbinder_headers_base",
+ "liblog_stub",
+ "trusty_mock_headers",
+ ],
shared_libs: [
"libutils_binder_sdk",
@@ -358,9 +365,6 @@
// for vndbinder
vendor_available: true,
- vndk: {
- enabled: true,
- },
recovery_available: true,
double_loadable: true,
// TODO(b/153609531): remove when no longer needed.
@@ -442,7 +446,7 @@
},
}
-cc_library_static {
+cc_library {
name: "libbinder_rpc_no_kernel",
vendor_available: true,
defaults: [
@@ -454,7 +458,7 @@
],
}
-cc_library_static {
+cc_library {
name: "libbinder_rpc_no_blob",
vendor_available: true,
defaults: [
@@ -470,7 +474,7 @@
],
}
-cc_library_static {
+cc_library {
name: "libbinder_rpc_no_native_handle",
vendor_available: true,
defaults: [
@@ -486,7 +490,7 @@
],
}
-cc_library_static {
+cc_library {
name: "libbinder_rpc_single_threaded",
defaults: [
"libbinder_common_defaults",
@@ -501,7 +505,7 @@
],
}
-cc_library_static {
+cc_library {
name: "libbinder_rpc_single_threaded_no_kernel",
defaults: [
"libbinder_common_defaults",
@@ -669,6 +673,7 @@
"//packages/modules/Virtualization:__subpackages__",
"//device/google/cuttlefish/shared/minidroid:__subpackages__",
"//system/software_defined_vehicle:__subpackages__",
+ "//visibility:any_system_partition",
],
}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 42dd691..54457fc 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -44,6 +44,7 @@
int BpBinder::sNumTrackedUids = 0;
std::atomic_bool BpBinder::sCountByUidEnabled(false);
binder_proxy_limit_callback BpBinder::sLimitCallback;
+binder_proxy_warning_callback BpBinder::sWarningCallback;
bool BpBinder::sBinderProxyThrottleCreate = false;
static StaticString16 kDescriptorUninit(u"");
@@ -52,6 +53,9 @@
uint32_t BpBinder::sBinderProxyCountHighWatermark = 2500;
// Another arbitrary value a binder count needs to drop below before another callback will be called
uint32_t BpBinder::sBinderProxyCountLowWatermark = 2000;
+// Arbitrary value between low and high watermark on a bad behaving app to
+// trigger a warning callback.
+uint32_t BpBinder::sBinderProxyCountWarningWatermark = 2250;
std::atomic<uint32_t> BpBinder::sBinderProxyCount(0);
std::atomic<uint32_t> BpBinder::sBinderProxyCountWarned(0);
@@ -63,7 +67,8 @@
enum {
LIMIT_REACHED_MASK = 0x80000000, // A flag denoting that the limit has been reached
- COUNTING_VALUE_MASK = 0x7FFFFFFF, // A mask of the remaining bits for the count value
+ WARNING_REACHED_MASK = 0x40000000, // A flag denoting that the warning has been reached
+ COUNTING_VALUE_MASK = 0x3FFFFFFF, // A mask of the remaining bits for the count value
};
BpBinder::ObjectManager::ObjectManager()
@@ -181,7 +186,13 @@
sLastLimitCallbackMap[trackedUid] = trackedValue;
}
} else {
- if ((trackedValue & COUNTING_VALUE_MASK) >= sBinderProxyCountHighWatermark) {
+ uint32_t currentValue = trackedValue & COUNTING_VALUE_MASK;
+ if (currentValue >= sBinderProxyCountWarningWatermark
+ && currentValue < sBinderProxyCountHighWatermark
+ && ((trackedValue & WARNING_REACHED_MASK) == 0)) [[unlikely]] {
+ sTrackingMap[trackedUid] |= WARNING_REACHED_MASK;
+ if (sWarningCallback) sWarningCallback(trackedUid);
+ } else if (currentValue >= sBinderProxyCountHighWatermark) {
ALOGE("Too many binder proxy objects sent to uid %d from uid %d (%d proxies held)",
getuid(), trackedUid, trackedValue);
sTrackingMap[trackedUid] |= LIMIT_REACHED_MASK;
@@ -609,11 +620,11 @@
binderHandle());
} else {
auto countingValue = trackedValue & COUNTING_VALUE_MASK;
- if ((trackedValue & LIMIT_REACHED_MASK) &&
+ if ((trackedValue & (LIMIT_REACHED_MASK | WARNING_REACHED_MASK)) &&
(countingValue <= sBinderProxyCountLowWatermark)) [[unlikely]] {
ALOGI("Limit reached bit reset for uid %d (fewer than %d proxies from uid %d held)",
getuid(), sBinderProxyCountLowWatermark, mTrackedUid);
- sTrackingMap[mTrackedUid] &= ~LIMIT_REACHED_MASK;
+ sTrackingMap[mTrackedUid] &= ~(LIMIT_REACHED_MASK | WARNING_REACHED_MASK);
sLastLimitCallbackMap.erase(mTrackedUid);
}
if (--sTrackingMap[mTrackedUid] == 0) {
@@ -730,15 +741,18 @@
void BpBinder::disableCountByUid() { sCountByUidEnabled.store(false); }
void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); }
-void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) {
+void BpBinder::setBinderProxyCountEventCallback(binder_proxy_limit_callback cbl,
+ binder_proxy_warning_callback cbw) {
RpcMutexUniqueLock _l(sTrackingLock);
- sLimitCallback = cb;
+ sLimitCallback = std::move(cbl);
+ sWarningCallback = std::move(cbw);
}
-void BpBinder::setBinderProxyCountWatermarks(int high, int low) {
+void BpBinder::setBinderProxyCountWatermarks(int high, int low, int warning) {
RpcMutexUniqueLock _l(sTrackingLock);
sBinderProxyCountHighWatermark = high;
sBinderProxyCountLowWatermark = low;
+ sBinderProxyCountWarningWatermark = warning;
}
// ---------------------------------------------------------------------------
diff --git a/libs/binder/IBatteryStats.cpp b/libs/binder/IBatteryStats.cpp
index 69b11c0..7b58046 100644
--- a/libs/binder/IBatteryStats.cpp
+++ b/libs/binder/IBatteryStats.cpp
@@ -66,14 +66,14 @@
Parcel data, reply;
data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor());
data.writeInt32(uid);
- remote()->transact(NOTE_START_AUDIO_TRANSACTION, data, &reply);
+ remote()->transact(NOTE_START_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual void noteStopAudio(int uid) {
Parcel data, reply;
data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor());
data.writeInt32(uid);
- remote()->transact(NOTE_STOP_AUDIO_TRANSACTION, data, &reply);
+ remote()->transact(NOTE_STOP_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual void noteResetVideo() {
@@ -85,7 +85,7 @@
virtual void noteResetAudio() {
Parcel data, reply;
data.writeInterfaceToken(IBatteryStats::getInterfaceDescriptor());
- remote()->transact(NOTE_RESET_AUDIO_TRANSACTION, data, &reply);
+ remote()->transact(NOTE_RESET_AUDIO_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
}
virtual void noteFlashlightOn(int uid) {
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 1ffdad5..611169d 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -30,6 +30,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
+#include <algorithm>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index f8a8843..3ddfefa 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -43,6 +43,18 @@
@utf8InCpp String[] getNamesForUids(in int[] uids);
/**
+ * Return the UID associated with the given package name.
+ * Note that the same package will have different UIDs under different UserHandle on
+ * the same device.
+ * @param packageName The full name (i.e. com.google.apps.contacts) of the desired package.
+ * @param flags Additional option flags to modify the data returned.
+ * @param userId The user handle identifier to look up the package under.
+ * @return Returns an integer UID who owns the given package name, or -1 if no such package is
+ * available to the caller.
+ */
+ int getPackageUid(in @utf8InCpp String packageName, in long flags, in int userId);
+
+ /**
* Returns the name of the installer (a package) which installed the named
* package. Preloaded packages return the string "preload". Sideloaded packages
* return an empty string. Unknown or unknowable are returned as empty strings.
diff --git a/libs/binder/binder_module.h b/libs/binder/binder_module.h
index eef07ae..b3a2d9e 100644
--- a/libs/binder/binder_module.h
+++ b/libs/binder/binder_module.h
@@ -32,77 +32,4 @@
#include <linux/android/binder.h>
#include <sys/ioctl.h>
-#ifndef BR_FROZEN_REPLY
-// Temporary definition of BR_FROZEN_REPLY. For production
-// this will come from UAPI binder.h
-#define BR_FROZEN_REPLY _IO('r', 18)
-#endif // BR_FROZEN_REPLY
-
-#ifndef BINDER_FREEZE
-/*
- * Temporary definitions for freeze support. For the final version
- * these will be defined in the UAPI binder.h file from upstream kernel.
- */
-#define BINDER_FREEZE _IOW('b', 14, struct binder_freeze_info)
-
-struct binder_freeze_info {
- //
- // Group-leader PID of process to be frozen
- //
- uint32_t pid;
- //
- // Enable(1) / Disable(0) freeze for given PID
- //
- uint32_t enable;
- //
- // Timeout to wait for transactions to drain.
- // 0: don't wait (ioctl will return EAGAIN if not drained)
- // N: number of ms to wait
- uint32_t timeout_ms;
-};
-#endif // BINDER_FREEZE
-
-#ifndef BINDER_GET_FROZEN_INFO
-
-#define BINDER_GET_FROZEN_INFO _IOWR('b', 15, struct binder_frozen_status_info)
-
-struct binder_frozen_status_info {
- //
- // Group-leader PID of process to be queried
- //
- __u32 pid;
- //
- // Indicates whether the process has received any sync calls since last
- // freeze (cleared at freeze/unfreeze)
- // bit 0: received sync transaction after being frozen
- // bit 1: new pending sync transaction during freezing
- //
- __u32 sync_recv;
- //
- // Indicates whether the process has received any async calls since last
- // freeze (cleared at freeze/unfreeze)
- //
- __u32 async_recv;
-};
-#endif // BINDER_GET_FROZEN_INFO
-
-#ifndef BR_ONEWAY_SPAM_SUSPECT
-// Temporary definition of BR_ONEWAY_SPAM_SUSPECT. For production
-// this will come from UAPI binder.h
-#define BR_ONEWAY_SPAM_SUSPECT _IO('r', 19)
-#endif // BR_ONEWAY_SPAM_SUSPECT
-
-#ifndef BINDER_ENABLE_ONEWAY_SPAM_DETECTION
-/*
- * Temporary definitions for oneway spam detection support. For the final version
- * these will be defined in the UAPI binder.h file from upstream kernel.
- */
-#define BINDER_ENABLE_ONEWAY_SPAM_DETECTION _IOW('b', 16, __u32)
-#endif // BINDER_ENABLE_ONEWAY_SPAM_DETECTION
-
-#ifndef BR_TRANSACTION_PENDING_FROZEN
-// Temporary definition of BR_TRANSACTION_PENDING_FROZEN until UAPI binder.h includes it.
-#define BR_TRANSACTION_PENDING_FROZEN _IO('r', 20)
-#endif // BR_TRANSACTION_PENDING_FROZEN
-
#endif // _BINDER_MODULE_H_
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 89a4d27..9f03907 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -35,7 +35,8 @@
}
class ProcessState;
-using binder_proxy_limit_callback = void(*)(int);
+using binder_proxy_limit_callback = std::function<void(int)>;
+using binder_proxy_warning_callback = std::function<void(int)>;
class BpBinder : public IBinder
{
@@ -86,8 +87,9 @@
static void enableCountByUid();
static void disableCountByUid();
static void setCountByUidEnabled(bool enable);
- static void setLimitCallback(binder_proxy_limit_callback cb);
- static void setBinderProxyCountWatermarks(int high, int low);
+ static void setBinderProxyCountEventCallback(binder_proxy_limit_callback cbl,
+ binder_proxy_warning_callback cbw);
+ static void setBinderProxyCountWatermarks(int high, int low, int warning);
static uint32_t getBinderProxyCount();
std::optional<int32_t> getDebugBinderHandle() const;
@@ -212,6 +214,8 @@
static std::unordered_map<int32_t,uint32_t> sLastLimitCallbackMap;
static std::atomic<uint32_t> sBinderProxyCount;
static std::atomic<uint32_t> sBinderProxyCountWarned;
+ static binder_proxy_warning_callback sWarningCallback;
+ static uint32_t sBinderProxyCountWarningWatermark;
};
} // namespace android
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 7d0acd1..392ebb5 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -73,6 +73,17 @@
const ARpcSession_FileDescriptorTransportMode modes[],
size_t modes_len);
+// Sets the maximum number of threads that the Server will use for
+// incoming client connections.
+//
+// This must be called before adding a client session. This corresponds
+// to the number of incoming connections to RpcSession objects in the
+// server, which will correspond to the number of outgoing connections
+// in client RpcSession objects.
+//
+// If this is not specified, this will be a single-threaded server.
+void ARpcServer_setMaxThreads(ARpcServer* server, size_t threads);
+
// Runs ARpcServer_join() in a background thread. Immediately returns.
void ARpcServer_start(ARpcServer* server);
diff --git a/libs/binder/libbinder_rpc_unstable.cpp b/libs/binder/libbinder_rpc_unstable.cpp
index cb44c58..21537fc 100644
--- a/libs/binder/libbinder_rpc_unstable.cpp
+++ b/libs/binder/libbinder_rpc_unstable.cpp
@@ -167,6 +167,10 @@
server->setSupportedFileDescriptorTransportModes(modevec);
}
+void ARpcServer_setMaxThreads(ARpcServer* handle, size_t threads) {
+ handleToStrongPointer<RpcServer>(handle)->setMaxThreads(threads);
+}
+
void ARpcServer_start(ARpcServer* handle) {
handleToStrongPointer<RpcServer>(handle)->start();
}
diff --git a/libs/binder/liblog_stub/include/log/log.h b/libs/binder/liblog_stub/include/log/log.h
index 91c9632..dad0020 100644
--- a/libs/binder/liblog_stub/include/log/log.h
+++ b/libs/binder/liblog_stub/include/log/log.h
@@ -54,16 +54,16 @@
#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG)
#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG)
-#define ALOG(priority, tag, fmt, ...) \
- do { \
- if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \
- std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \
- } \
- IF_ALOG(priority, tag) { \
- __android_log_print(ANDROID_##priority, tag, \
- tag ": " fmt "\n" __VA_OPT__(, ) __VA_ARGS__); \
- } \
- if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \
+#define ALOG(priority, tag, fmt, ...) \
+ do { \
+ if (false)[[/*VERY*/ unlikely]] { /* ignore unused __VA_ARGS__ */ \
+ std::fprintf(stderr, fmt __VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ IF_ALOG(priority, tag) { \
+ __android_log_print(ANDROID_##priority, tag, "%s: " fmt "\n", \
+ (tag)__VA_OPT__(, ) __VA_ARGS__); \
+ } \
+ if constexpr (ANDROID_##priority == ANDROID_LOG_FATAL) std::abort(); \
} while (false)
#define ALOGV(...) ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define ALOGD(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 30dbddd..9a2d14a 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -50,8 +50,10 @@
],
cflags: [
+ "-DBINDER_WITH_KERNEL_IPC",
"-Wall",
"-Wextra",
+ "-Wextra-semi",
"-Werror",
],
@@ -145,6 +147,46 @@
afdo: true,
}
+cc_library {
+ name: "libbinder_ndk_on_trusty_mock",
+ defaults: [
+ "trusty_mock_defaults",
+ ],
+
+ export_include_dirs: [
+ "include_cpp",
+ "include_ndk",
+ "include_platform",
+ ],
+
+ srcs: [
+ "ibinder.cpp",
+ "libbinder.cpp",
+ "parcel.cpp",
+ "stability.cpp",
+ "status.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder_on_trusty_mock",
+ ],
+
+ header_libs: [
+ "libbinder_trusty_ndk_headers",
+ ],
+ export_header_lib_headers: [
+ "libbinder_trusty_ndk_headers",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+ visibility: ["//frameworks/native/libs/binder:__subpackages__"],
+}
+
cc_library_headers {
name: "libbinder_headers_platform_shared",
export_include_dirs: ["include_cpp"],
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index e2ede3f..af280d3 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -24,6 +24,7 @@
#include <private/android_filesystem_config.h>
#endif
+#include "../BuildFlags.h"
#include "ibinder_internal.h"
#include "parcel_internal.h"
#include "status_internal.h"
@@ -44,7 +45,9 @@
static const void* kId = "ABBinder";
static void* kValue = static_cast<void*>(new bool{true});
-void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/){/* do nothing */};
+void clean(const void* /*id*/, void* /*obj*/, void* /*cookie*/) {
+ /* do nothing */
+}
static void attach(const sp<IBinder>& binder) {
auto alreadyAttached = binder->attachObject(kId, kValue, nullptr /*cookie*/, clean);
@@ -69,7 +72,7 @@
LOG_ALWAYS_FATAL_IF(id != kId, "%p %p %p", id, obj, cookie);
delete static_cast<Value*>(obj);
-};
+}
} // namespace ABpBinderTag
@@ -211,6 +214,12 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
} else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) {
+ if constexpr (!android::kEnableKernelIpc) {
+ // Non-IPC builds do not have getCallingUid(),
+ // so we have no way of authenticating the caller
+ return STATUS_PERMISSION_DENIED;
+ }
+
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
@@ -469,6 +478,21 @@
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+ if (mOnUnlinked && cookie &&
+ std::find_if(mDeathRecipients.begin(), mDeathRecipients.end(),
+ [&cookie](android::sp<TransferDeathRecipient> recipient) {
+ return recipient->getCookie() == cookie;
+ }) != mDeathRecipients.end()) {
+ ALOGE("Attempting to AIBinder_linkToDeath with the same cookie with an onUnlink callback. "
+ "This will cause the onUnlinked callback to be called multiple times with the same "
+ "cookie, which is usually not intended.");
+ }
+ if (!mOnUnlinked && cookie) {
+ ALOGW("AIBinder_linkToDeath is being called with a non-null cookie and no onUnlink "
+ "callback set. This might not be intended. AIBinder_DeathRecipient_setOnUnlinked "
+ "should be called first.");
+ }
+
sp<TransferDeathRecipient> recipient =
new TransferDeathRecipient(binder, cookie, this, mOnDied, mOnUnlinked);
@@ -602,6 +626,7 @@
return recipient->unlinkToDeath(binder->getBinder(), cookie);
}
+#ifdef BINDER_WITH_KERNEL_IPC
uid_t AIBinder_getCallingUid() {
return ::android::IPCThreadState::self()->getCallingUid();
}
@@ -613,6 +638,7 @@
bool AIBinder_isHandlingTransaction() {
return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr;
}
+#endif
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
@@ -830,9 +856,11 @@
localBinder->setRequestingSid(requestingSid);
}
+#ifdef BINDER_WITH_KERNEL_IPC
const char* AIBinder_getCallingSid() {
return ::android::IPCThreadState::self()->getCallingSid();
}
+#endif
void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index b1ab7b0..2929bce 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -718,9 +718,17 @@
* When registering the interface, add:
* std::shared_ptr<MyFoo> foo = new MyFoo; // class in AOSP codebase
* std::shared_ptr<MyBar> bar = new MyBar; // custom extension class
- * ... = AIBinder_setExtension(foo->asBinder().get(), bar->asBinder().get());
+ * SpAIBinder binder = foo->asBinder(); // target binder to extend
+ * ... = AIBinder_setExtension(binder.get(), bar->asBinder().get());
+ * ... = AServiceManager_addService(binder.get(), instanceName);
* // handle error
*
+ * Do not use foo->asBinder().get() as the target binder argument to
+ * AIBinder_setExtensions because asBinder it creates a new binder
+ * object that will be destroyed after the function is called. The same
+ * binder object must be used for AIBinder_setExtension and
+ * AServiceManager_addService to register the service with an extension.
+ *
* Then, clients of IFoo can get this extension:
* SpAIBinder binder = ...;
* std::shared_ptr<IFoo> foo = IFoo::fromBinder(binder); // handle if null
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index ca3d5e6..39cf1c4 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -23,7 +23,7 @@
using ::android::internal::Stability;
-#ifdef __ANDROID_VNDK__
+#if defined(__ANDROID_VNDK__) && !defined(__TRUSTY__)
#error libbinder_ndk should only be built in a system context
#endif
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index ce63b82..471ab0c 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -697,6 +697,56 @@
}
}
+void LambdaOnUnlinkMultiple(void* cookie) {
+ auto funcs = static_cast<DeathRecipientCookie*>(cookie);
+ (*funcs->onUnlink)();
+};
+
+TEST(NdkBinder, DeathRecipientMultipleLinks) {
+ using namespace std::chrono_literals;
+
+ ndk::SpAIBinder binder;
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName, binder.getR());
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ std::function<void(void)> onDeath = [&] {};
+
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ constexpr uint32_t kNumberOfLinksToDeath = 4;
+ uint32_t countdown = kNumberOfLinksToDeath;
+
+ std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ countdown--;
+ if (countdown == 0) {
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ }
+ };
+
+ DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+
+ ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+ AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlinkMultiple);
+
+ for (int32_t i = 0; i < kNumberOfLinksToDeath; i++) {
+ EXPECT_EQ(STATUS_OK,
+ AIBinder_linkToDeath(binder.get(), recipient.get(), static_cast<void*>(cookie)));
+ }
+
+ foo = nullptr;
+ binder = nullptr;
+
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(unlinkCv.wait_for(lockUnlink, 5s, [&] { return unlinkReceived; }))
+ << "countdown: " << countdown;
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_EQ(countdown, 0);
+}
+
TEST(NdkBinder, RetrieveNonNdkService) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index 57a38dc..ef556d7 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -35,6 +35,21 @@
}
rust_library {
+ name: "libbinder_rs_on_trusty_mock",
+ crate_name: "binder",
+ srcs: ["src/lib.rs"],
+ cfgs: [
+ "trusty",
+ ],
+ rustlibs: [
+ "libbinder_ndk_sys_on_trusty_mock",
+ "libdowncast_rs",
+ "liblibc",
+ ],
+ vendor: true,
+}
+
+rust_library {
name: "libbinder_tokio_rs",
crate_name: "binder_tokio",
srcs: ["binder_tokio/lib.rs"],
@@ -89,6 +104,26 @@
visibility: [":__subpackages__"],
}
+rust_library {
+ name: "libbinder_ndk_sys_on_trusty_mock",
+ crate_name: "binder_ndk_sys",
+ srcs: [
+ "sys/lib.rs",
+ ":libbinder_ndk_bindgen_on_trusty_mock",
+ ],
+ cfgs: [
+ "trusty",
+ ],
+ shared_libs: [
+ "libbinder_ndk_on_trusty_mock",
+ ],
+ vendor: true,
+ // Lints are checked separately for libbinder_ndk_sys.
+ // The Trusty mock copy pulls in extra headers that
+ // don't pass the lints for the bindgen output.
+ lints: "none",
+}
+
rust_bindgen {
name: "libbinder_ndk_bindgen",
crate_name: "binder_ndk_bindgen",
@@ -125,6 +160,28 @@
min_sdk_version: "Tiramisu",
}
+rust_bindgen {
+ name: "libbinder_ndk_bindgen_on_trusty_mock",
+ crate_name: "binder_ndk_bindgen",
+ wrapper_src: "sys/BinderBindings.hpp",
+ source_stem: "bindings",
+ defaults: [
+ "trusty_mock_defaults",
+ ],
+
+ bindgen_flag_files: [
+ // Unfortunately the only way to specify the rust_non_exhaustive enum
+ // style for a type is to make it the default
+ // and then specify constified enums for the enums we don't want
+ // rustified
+ "libbinder_ndk_bindgen_flags.txt",
+ ],
+ shared_libs: [
+ "libbinder_ndk_on_trusty_mock",
+ "libc++",
+ ],
+}
+
rust_test {
name: "libbinder_rs-internal_test",
crate_name: "binder",
diff --git a/libs/binder/rust/rpcbinder/Android.bp b/libs/binder/rust/rpcbinder/Android.bp
index 535ce01..2e46345 100644
--- a/libs/binder/rust/rpcbinder/Android.bp
+++ b/libs/binder/rust/rpcbinder/Android.bp
@@ -18,6 +18,7 @@
"libbinder_ndk_sys",
"libbinder_rpc_unstable_bindgen_sys",
"libbinder_rs",
+ "libcfg_if",
"libdowncast_rs",
"libforeign_types",
"liblibc",
diff --git a/libs/binder/rust/rpcbinder/src/lib.rs b/libs/binder/rust/rpcbinder/src/lib.rs
index 163f000..7e5c9dd 100644
--- a/libs/binder/rust/rpcbinder/src/lib.rs
+++ b/libs/binder/rust/rpcbinder/src/lib.rs
@@ -16,10 +16,10 @@
//! API for RPC Binder services.
-#[cfg(not(target_os = "trusty"))]
mod server;
mod session;
+pub use server::RpcServer;
#[cfg(not(target_os = "trusty"))]
-pub use server::{RpcServer, RpcServerRef};
+pub use server::RpcServerRef;
pub use session::{FileDescriptorTransportMode, RpcSession, RpcSessionRef};
diff --git a/libs/binder/rust/rpcbinder/src/server.rs b/libs/binder/rust/rpcbinder/src/server.rs
index 6fda878..d6bdbd8 100644
--- a/libs/binder/rust/rpcbinder/src/server.rs
+++ b/libs/binder/rust/rpcbinder/src/server.rs
@@ -14,160 +14,12 @@
* limitations under the License.
*/
-use crate::session::FileDescriptorTransportMode;
-use binder::{unstable_api::AsNative, SpIBinder};
-use binder_rpc_unstable_bindgen::ARpcServer;
-use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
-use std::ffi::CString;
-use std::io::{Error, ErrorKind};
-use std::os::unix::io::{IntoRawFd, OwnedFd};
-
-foreign_type! {
- type CType = binder_rpc_unstable_bindgen::ARpcServer;
- fn drop = binder_rpc_unstable_bindgen::ARpcServer_free;
-
- /// A type that represents a foreign instance of RpcServer.
- #[derive(Debug)]
- pub struct RpcServer;
- /// A borrowed RpcServer.
- pub struct RpcServerRef;
-}
-
-/// SAFETY: The opaque handle can be cloned freely.
-unsafe impl Send for RpcServer {}
-/// SAFETY: The underlying C++ RpcServer class is thread-safe.
-unsafe impl Sync for RpcServer {}
-
-impl RpcServer {
- /// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// vsock port. Only connections from the given CID are accepted.
- ///
- // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
- // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
- pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
- let service = service.as_native_mut();
-
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
- service, cid, port,
- ))
- }
- }
-
- /// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// socket file descriptor. The socket should be bound to an address before calling this
- /// function.
- pub fn new_bound_socket(
- mut service: SpIBinder,
- socket_fd: OwnedFd,
- ) -> Result<RpcServer, Error> {
- let service = service.as_native_mut();
-
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- // The server takes ownership of the socket FD.
- unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket(
- service,
- socket_fd.into_raw_fd(),
- ))
- }
- }
-
- /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket
- /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix
- /// domain sockets, pass one to the server and the other to the client. Multiple client session
- /// can be created from the client end of the pair.
- pub fn new_unix_domain_bootstrap(
- mut service: SpIBinder,
- bootstrap_fd: OwnedFd,
- ) -> Result<RpcServer, Error> {
- let service = service.as_native_mut();
-
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- // The server takes ownership of the bootstrap FD.
- unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap(
- service,
- bootstrap_fd.into_raw_fd(),
- ))
- }
- }
-
- /// Creates a binder RPC server, serving the supplied binder service implementation on the given
- /// IP address and port.
- pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> {
- let address = match CString::new(address) {
- Ok(s) => s,
- Err(e) => {
- log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
- return Err(Error::from(ErrorKind::InvalidInput));
- }
- };
- let service = service.as_native_mut();
-
- // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
- // Plus the binder objects are threadsafe.
- unsafe {
- Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet(
- service,
- address.as_ptr(),
- port,
- ))
- }
- }
-
- unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
- if ptr.is_null() {
- return Err(Error::new(ErrorKind::Other, "Failed to start server"));
- }
- // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not
- // null.
- Ok(unsafe { RpcServer::from_ptr(ptr) })
- }
-}
-
-impl RpcServerRef {
- /// Sets the list of file descriptor transport modes supported by this server.
- pub fn set_supported_file_descriptor_transport_modes(
- &self,
- modes: &[FileDescriptorTransportMode],
- ) {
- // SAFETY: Does not keep the pointer after returning does, nor does it
- // read past its boundary. Only passes the 'self' pointer as an opaque handle.
- unsafe {
- binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
- self.as_ptr(),
- modes.as_ptr(),
- modes.len(),
- )
- }
- }
-
- /// Starts a new background thread and calls join(). Returns immediately.
- pub fn start(&self) {
- // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
- unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
- }
-
- /// Joins the RpcServer thread. The call blocks until the server terminates.
- /// This must be called from exactly one thread.
- pub fn join(&self) {
- // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
- unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
- }
-
- /// Shuts down the running RpcServer. Can be called multiple times and from
- /// multiple threads. Called automatically during drop().
- pub fn shutdown(&self) -> Result<(), Error> {
- // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
- if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
- Ok(())
- } else {
- Err(Error::from(ErrorKind::UnexpectedEof))
- }
+cfg_if::cfg_if! {
+ if #[cfg(target_os = "trusty")] {
+ mod trusty;
+ pub use trusty::*;
+ } else {
+ mod android;
+ pub use android::*;
}
}
diff --git a/libs/binder/rust/rpcbinder/src/server/android.rs b/libs/binder/rust/rpcbinder/src/server/android.rs
new file mode 100644
index 0000000..2ab3447
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/server/android.rs
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::session::FileDescriptorTransportMode;
+use binder::{unstable_api::AsNative, SpIBinder};
+use binder_rpc_unstable_bindgen::ARpcServer;
+use foreign_types::{foreign_type, ForeignType, ForeignTypeRef};
+use std::ffi::CString;
+use std::io::{Error, ErrorKind};
+use std::os::unix::io::{IntoRawFd, OwnedFd};
+
+foreign_type! {
+ type CType = binder_rpc_unstable_bindgen::ARpcServer;
+ fn drop = binder_rpc_unstable_bindgen::ARpcServer_free;
+
+ /// A type that represents a foreign instance of RpcServer.
+ #[derive(Debug)]
+ pub struct RpcServer;
+ /// A borrowed RpcServer.
+ pub struct RpcServerRef;
+}
+
+/// SAFETY: The opaque handle can be cloned freely.
+unsafe impl Send for RpcServer {}
+/// SAFETY: The underlying C++ RpcServer class is thread-safe.
+unsafe impl Sync for RpcServer {}
+
+impl RpcServer {
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// vsock port. Only connections from the given CID are accepted.
+ ///
+ // Set `cid` to libc::VMADDR_CID_ANY to accept connections from any client.
+ // Set `cid` to libc::VMADDR_CID_LOCAL to only bind to the local vsock interface.
+ pub fn new_vsock(mut service: SpIBinder, cid: u32, port: u32) -> Result<RpcServer, Error> {
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newVsock(
+ service, cid, port,
+ ))
+ }
+ }
+
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// socket file descriptor. The socket should be bound to an address before calling this
+ /// function.
+ pub fn new_bound_socket(
+ mut service: SpIBinder,
+ socket_fd: OwnedFd,
+ ) -> Result<RpcServer, Error> {
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ // The server takes ownership of the socket FD.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newBoundSocket(
+ service,
+ socket_fd.into_raw_fd(),
+ ))
+ }
+ }
+
+ /// Creates a binder RPC server that bootstraps sessions using an existing Unix domain socket
+ /// pair, with a given root IBinder object. Callers should create a pair of SOCK_STREAM Unix
+ /// domain sockets, pass one to the server and the other to the client. Multiple client session
+ /// can be created from the client end of the pair.
+ pub fn new_unix_domain_bootstrap(
+ mut service: SpIBinder,
+ bootstrap_fd: OwnedFd,
+ ) -> Result<RpcServer, Error> {
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ // The server takes ownership of the bootstrap FD.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newUnixDomainBootstrap(
+ service,
+ bootstrap_fd.into_raw_fd(),
+ ))
+ }
+ }
+
+ /// Creates a binder RPC server, serving the supplied binder service implementation on the given
+ /// IP address and port.
+ pub fn new_inet(mut service: SpIBinder, address: &str, port: u32) -> Result<RpcServer, Error> {
+ let address = match CString::new(address) {
+ Ok(s) => s,
+ Err(e) => {
+ log::error!("Cannot convert {} to CString. Error: {:?}", address, e);
+ return Err(Error::from(ErrorKind::InvalidInput));
+ }
+ };
+ let service = service.as_native_mut();
+
+ // SAFETY: Service ownership is transferring to the server and won't be valid afterward.
+ // Plus the binder objects are threadsafe.
+ unsafe {
+ Self::checked_from_ptr(binder_rpc_unstable_bindgen::ARpcServer_newInet(
+ service,
+ address.as_ptr(),
+ port,
+ ))
+ }
+ }
+
+ unsafe fn checked_from_ptr(ptr: *mut ARpcServer) -> Result<RpcServer, Error> {
+ if ptr.is_null() {
+ return Err(Error::new(ErrorKind::Other, "Failed to start server"));
+ }
+ // SAFETY: Our caller must pass us a valid or null pointer, and we've checked that it's not
+ // null.
+ Ok(unsafe { RpcServer::from_ptr(ptr) })
+ }
+}
+
+impl RpcServerRef {
+ /// Sets the list of file descriptor transport modes supported by this server.
+ pub fn set_supported_file_descriptor_transport_modes(
+ &self,
+ modes: &[FileDescriptorTransportMode],
+ ) {
+ // SAFETY: Does not keep the pointer after returning does, nor does it
+ // read past its boundary. Only passes the 'self' pointer as an opaque handle.
+ unsafe {
+ binder_rpc_unstable_bindgen::ARpcServer_setSupportedFileDescriptorTransportModes(
+ self.as_ptr(),
+ modes.as_ptr(),
+ modes.len(),
+ )
+ }
+ }
+
+ /// Sets the max number of threads this Server uses for incoming client connections.
+ ///
+ /// This must be called before adding a client session. This corresponds
+ /// to the number of incoming connections to RpcSession objects in the
+ /// server, which will correspond to the number of outgoing connections
+ /// in client RpcSession objects. Specifically this is useful for handling
+ /// client-side callback connections.
+ ///
+ /// If this is not specified, this will be a single-threaded server.
+ pub fn set_max_threads(&self, count: usize) {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_setMaxThreads(self.as_ptr(), count) };
+ }
+
+ /// Starts a new background thread and calls join(). Returns immediately.
+ pub fn start(&self) {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_start(self.as_ptr()) };
+ }
+
+ /// Joins the RpcServer thread. The call blocks until the server terminates.
+ /// This must be called from exactly one thread.
+ pub fn join(&self) {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
+ unsafe { binder_rpc_unstable_bindgen::ARpcServer_join(self.as_ptr()) };
+ }
+
+ /// Shuts down the running RpcServer. Can be called multiple times and from
+ /// multiple threads. Called automatically during drop().
+ pub fn shutdown(&self) -> Result<(), Error> {
+ // SAFETY: RpcServerRef wraps a valid pointer to an ARpcServer.
+ if unsafe { binder_rpc_unstable_bindgen::ARpcServer_shutdown(self.as_ptr()) } {
+ Ok(())
+ } else {
+ Err(Error::from(ErrorKind::UnexpectedEof))
+ }
+ }
+}
diff --git a/libs/binder/rust/rpcbinder/src/server/trusty.rs b/libs/binder/rust/rpcbinder/src/server/trusty.rs
new file mode 100644
index 0000000..fe45dec
--- /dev/null
+++ b/libs/binder/rust/rpcbinder/src/server/trusty.rs
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+use binder::{unstable_api::AsNative, SpIBinder};
+use libc::size_t;
+use std::ffi::{c_char, c_void};
+use std::ptr;
+use tipc::{ConnectResult, Handle, MessageResult, PortCfg, TipcError, UnbufferedService, Uuid};
+
+pub trait PerSessionCallback: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {}
+impl<T> PerSessionCallback for T where T: Fn(Uuid) -> Option<SpIBinder> + Send + Sync + 'static {}
+
+pub struct RpcServer {
+ inner: *mut binder_rpc_server_bindgen::ARpcServerTrusty,
+}
+
+/// SAFETY: The opaque handle points to a heap allocation
+/// that should be process-wide and not tied to the current thread.
+unsafe impl Send for RpcServer {}
+/// SAFETY: The underlying C++ RpcServer class is thread-safe.
+unsafe impl Sync for RpcServer {}
+
+impl Drop for RpcServer {
+ fn drop(&mut self) {
+ // SAFETY: `ARpcServerTrusty_delete` is the correct destructor to call
+ // on pointers returned by `ARpcServerTrusty_new`.
+ unsafe {
+ binder_rpc_server_bindgen::ARpcServerTrusty_delete(self.inner);
+ }
+ }
+}
+
+impl RpcServer {
+ /// Allocates a new RpcServer object.
+ pub fn new(service: SpIBinder) -> RpcServer {
+ Self::new_per_session(move |_uuid| Some(service.clone()))
+ }
+
+ /// Allocates a new per-session RpcServer object.
+ ///
+ /// Per-session objects take a closure that gets called once
+ /// for every new connection. The closure gets the UUID of
+ /// the peer and can accept or reject that connection.
+ pub fn new_per_session<F: PerSessionCallback>(f: F) -> RpcServer {
+ // SAFETY: Takes ownership of the returned handle, which has correct refcount.
+ let inner = unsafe {
+ binder_rpc_server_bindgen::ARpcServerTrusty_newPerSession(
+ Some(per_session_callback_wrapper::<F>),
+ Box::into_raw(Box::new(f)).cast(),
+ Some(per_session_callback_deleter::<F>),
+ )
+ };
+ RpcServer { inner }
+ }
+}
+
+unsafe extern "C" fn per_session_callback_wrapper<F: PerSessionCallback>(
+ uuid_ptr: *const c_void,
+ len: size_t,
+ cb_ptr: *mut c_char,
+) -> *mut binder_rpc_server_bindgen::AIBinder {
+ // SAFETY: This callback should only get called while the RpcServer is alive.
+ let cb = unsafe { &mut *cb_ptr.cast::<F>() };
+
+ if len != std::mem::size_of::<Uuid>() {
+ return ptr::null_mut();
+ }
+
+ // SAFETY: On the previous lines we check that we got exactly the right amount of bytes.
+ let uuid = unsafe {
+ let mut uuid = std::mem::MaybeUninit::<Uuid>::uninit();
+ uuid.as_mut_ptr().copy_from(uuid_ptr.cast(), 1);
+ uuid.assume_init()
+ };
+
+ cb(uuid).map_or_else(ptr::null_mut, |b| {
+ // Prevent AIBinder_decStrong from being called before AIBinder_toPlatformBinder.
+ // The per-session callback in C++ is supposed to call AIBinder_decStrong on the
+ // pointer we return here.
+ std::mem::ManuallyDrop::new(b).as_native_mut().cast()
+ })
+}
+
+unsafe extern "C" fn per_session_callback_deleter<F: PerSessionCallback>(cb: *mut c_char) {
+ // SAFETY: shared_ptr calls this to delete the pointer we gave it.
+ // It should only get called once the last shared reference goes away.
+ unsafe {
+ drop(Box::<F>::from_raw(cb.cast()));
+ }
+}
+
+pub struct RpcServerConnection {
+ ctx: *mut c_void,
+}
+
+impl Drop for RpcServerConnection {
+ fn drop(&mut self) {
+ // We do not need to close handle_fd since we do not own it.
+ unsafe {
+ binder_rpc_server_bindgen::ARpcServerTrusty_handleChannelCleanup(self.ctx);
+ }
+ }
+}
+
+impl UnbufferedService for RpcServer {
+ type Connection = RpcServerConnection;
+
+ fn on_connect(
+ &self,
+ _port: &PortCfg,
+ handle: &Handle,
+ peer: &Uuid,
+ ) -> tipc::Result<ConnectResult<Self::Connection>> {
+ let mut conn = RpcServerConnection { ctx: std::ptr::null_mut() };
+ let rc = unsafe {
+ binder_rpc_server_bindgen::ARpcServerTrusty_handleConnect(
+ self.inner,
+ handle.as_raw_fd(),
+ peer.as_ptr().cast(),
+ &mut conn.ctx,
+ )
+ };
+ if rc < 0 {
+ Err(TipcError::from_uapi(rc.into()))
+ } else {
+ Ok(ConnectResult::Accept(conn))
+ }
+ }
+
+ fn on_message(
+ &self,
+ conn: &Self::Connection,
+ _handle: &Handle,
+ buffer: &mut [u8],
+ ) -> tipc::Result<MessageResult> {
+ assert!(buffer.is_empty());
+ let rc = unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleMessage(conn.ctx) };
+ if rc < 0 {
+ Err(TipcError::from_uapi(rc.into()))
+ } else {
+ Ok(MessageResult::MaintainConnection)
+ }
+ }
+
+ fn on_disconnect(&self, conn: &Self::Connection) {
+ unsafe { binder_rpc_server_bindgen::ARpcServerTrusty_handleDisconnect(conn.ctx) };
+ }
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 16049f2..0f9c58c 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -100,7 +100,9 @@
mod native;
mod parcel;
mod proxy;
-#[cfg(not(target_os = "trusty"))]
+#[cfg(not(trusty))]
+mod service;
+#[cfg(not(trusty))]
mod state;
use binder_ndk_sys as sys;
@@ -108,16 +110,15 @@
pub use crate::binder_async::{BinderAsyncPool, BoxFuture};
pub use binder::{BinderFeatures, FromIBinder, IBinder, Interface, Strong, Weak};
pub use error::{ExceptionCode, IntoBinderResult, Status, StatusCode};
-pub use native::{
- add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service,
- LazyServiceGuard,
-};
pub use parcel::{ParcelFileDescriptor, Parcelable, ParcelableHolder};
-pub use proxy::{
- get_declared_instances, get_interface, get_service, is_declared, wait_for_interface,
- wait_for_service, DeathRecipient, SpIBinder, WpIBinder,
+pub use proxy::{DeathRecipient, SpIBinder, WpIBinder};
+#[cfg(not(trusty))]
+pub use service::{
+ add_service, force_lazy_services_persist, get_declared_instances, get_interface, get_service,
+ is_declared, is_handling_transaction, register_lazy_service, wait_for_interface,
+ wait_for_service, LazyServiceGuard,
};
-#[cfg(not(target_os = "trusty"))]
+#[cfg(not(trusty))]
pub use state::{ProcessState, ThreadState};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index 8ae010e..c87cc94 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -23,12 +23,11 @@
use crate::sys;
use std::convert::TryFrom;
-use std::ffi::{c_void, CStr, CString};
+use std::ffi::{c_void, CStr};
use std::io::Write;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::os::raw::c_char;
-use std::sync::Mutex;
/// Rust wrapper around Binder remotable objects.
///
@@ -328,7 +327,7 @@
/// contains a `T` pointer in its user data. fd should be a non-owned file
/// descriptor, and args must be an array of null-terminated string
/// pointers with length num_args.
- #[cfg(not(target_os = "trusty"))]
+ #[cfg(not(trusty))]
unsafe extern "C" fn on_dump(
binder: *mut sys::AIBinder,
fd: i32,
@@ -375,7 +374,7 @@
}
/// Called to handle the `dump` transaction.
- #[cfg(target_os = "trusty")]
+ #[cfg(trusty)]
unsafe extern "C" fn on_dump(
_binder: *mut sys::AIBinder,
_fd: i32,
@@ -462,110 +461,6 @@
}
}
-/// Register a new service with the default service manager.
-///
-/// Registers the given binder object with the given identifier. If successful,
-/// this service can then be retrieved using that identifier.
-///
-/// This function will panic if the identifier contains a 0 byte (NUL).
-pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
- let instance = CString::new(identifier).unwrap();
- let status =
- // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both pointers.
- // `AServiceManager_addService` creates a new strong reference and copies
- // the string, so both pointers need only be valid until the call returns.
- unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
- status_result(status)
-}
-
-/// Register a dynamic service via the LazyServiceRegistrar.
-///
-/// Registers the given binder object with the given identifier. If successful,
-/// this service can then be retrieved using that identifier. The service process
-/// will be shut down once all registered services are no longer in use.
-///
-/// If any service in the process is registered as lazy, all should be, otherwise
-/// the process may be shut down while a service is in use.
-///
-/// This function will panic if the identifier contains a 0 byte (NUL).
-pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
- let instance = CString::new(identifier).unwrap();
- // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
- // string pointers. Caller retains ownership of both
- // pointers. `AServiceManager_registerLazyService` creates a new strong reference
- // and copies the string, so both pointers need only be valid until the
- // call returns.
- let status = unsafe {
- sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
- };
- status_result(status)
-}
-
-/// Prevent a process which registers lazy services from being shut down even when none
-/// of the services is in use.
-///
-/// If persist is true then shut down will be blocked until this function is called again with
-/// persist false. If this is to be the initial state, call this function before calling
-/// register_lazy_service.
-///
-/// Consider using [`LazyServiceGuard`] rather than calling this directly.
-pub fn force_lazy_services_persist(persist: bool) {
- // Safety: No borrowing or transfer of ownership occurs here.
- unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
-}
-
-/// An RAII object to ensure a process which registers lazy services is not killed. During the
-/// lifetime of any of these objects the service manager will not not kill the process even if none
-/// of its lazy services are in use.
-#[must_use]
-#[derive(Debug)]
-pub struct LazyServiceGuard {
- // Prevent construction outside this module.
- _private: (),
-}
-
-// Count of how many LazyServiceGuard objects are in existence.
-static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
-
-impl LazyServiceGuard {
- /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
- /// process.
- pub fn new() -> Self {
- let mut count = GUARD_COUNT.lock().unwrap();
- *count += 1;
- if *count == 1 {
- // It's important that we make this call with the mutex held, to make sure
- // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly
- // sequenced. (That also means we can't just use an AtomicU64.)
- force_lazy_services_persist(true);
- }
- Self { _private: () }
- }
-}
-
-impl Drop for LazyServiceGuard {
- fn drop(&mut self) {
- let mut count = GUARD_COUNT.lock().unwrap();
- *count -= 1;
- if *count == 0 {
- force_lazy_services_persist(false);
- }
- }
-}
-
-impl Clone for LazyServiceGuard {
- fn clone(&self) -> Self {
- Self::new()
- }
-}
-
-impl Default for LazyServiceGuard {
- fn default() -> Self {
- Self::new()
- }
-}
-
/// Tests often create a base BBinder instance; so allowing the unit
/// type to be remotable translates nicely to Binder::new(()).
impl Remotable for () {
@@ -590,10 +485,3 @@
}
impl Interface for () {}
-
-/// Determine whether the current thread is currently executing an incoming
-/// transaction.
-pub fn is_handling_transaction() -> bool {
- // Safety: This method is always safe to call.
- unsafe { sys::AIBinder_isHandlingTransaction() }
-}
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index 7434e9d..340014a 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -29,11 +29,10 @@
use std::cmp::Ordering;
use std::convert::TryInto;
-use std::ffi::{c_void, CStr, CString};
+use std::ffi::{c_void, CString};
use std::fmt;
use std::mem;
use std::os::fd::AsRawFd;
-use std::os::raw::c_char;
use std::ptr;
use std::sync::Arc;
@@ -129,14 +128,6 @@
}
}
-fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> {
- if let Some(service) = service {
- FromIBinder::try_from(service)
- } else {
- Err(StatusCode::NAME_NOT_FOUND)
- }
-}
-
pub mod unstable_api {
use super::{sys, SpIBinder};
@@ -739,93 +730,6 @@
}
}
-/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
-/// exist.
-pub fn get_service(name: &str) -> Option<SpIBinder> {
- let name = CString::new(name).ok()?;
- // Safety: `AServiceManager_getService` returns either a null pointer or a
- // valid pointer to an owned `AIBinder`. Either of these values is safe to
- // pass to `SpIBinder::from_raw`.
- unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
-}
-
-/// Retrieve an existing service, or start it if it is configured as a dynamic
-/// service and isn't yet started.
-pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
- let name = CString::new(name).ok()?;
- // Safety: `AServiceManager_waitforService` returns either a null pointer or
- // a valid pointer to an owned `AIBinder`. Either of these values is safe to
- // pass to `SpIBinder::from_raw`.
- unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
-}
-
-/// Retrieve an existing service for a particular interface, blocking for a few
-/// seconds if it doesn't yet exist.
-pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- interface_cast(get_service(name))
-}
-
-/// Retrieve an existing service for a particular interface, or start it if it
-/// is configured as a dynamic service and isn't yet started.
-pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
- interface_cast(wait_for_service(name))
-}
-
-/// Check if a service is declared (e.g. in a VINTF manifest)
-pub fn is_declared(interface: &str) -> Result<bool> {
- let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
-
- // Safety: `interface` is a valid null-terminated C-style string and is only
- // borrowed for the lifetime of the call. The `interface` local outlives
- // this call as it lives for the function scope.
- unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
-}
-
-/// Retrieve all declared instances for a particular interface
-///
-/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo'
-/// is passed here, then ["foo"] would be returned.
-pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> {
- unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) {
- // Safety: opaque was a mutable pointer created below from a Vec of
- // CString, and outlives this callback. The null handling here is just
- // to avoid the possibility of unwinding across C code if this crate is
- // ever compiled with panic=unwind.
- if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
- // Safety: instance is a valid null-terminated C string with a
- // lifetime at least as long as this function, and we immediately
- // copy it into an owned CString.
- unsafe {
- instances.push(CStr::from_ptr(instance).to_owned());
- }
- } else {
- eprintln!("Opaque pointer was null in get_declared_instances callback!");
- }
- }
-
- let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
- let mut instances: Vec<CString> = vec![];
- // Safety: `interface` and `instances` are borrowed for the length of this
- // call and both outlive the call. `interface` is guaranteed to be a valid
- // null-terminated C-style string.
- unsafe {
- sys::AServiceManager_forEachDeclaredInstance(
- interface.as_ptr(),
- &mut instances as *mut _ as *mut c_void,
- Some(callback),
- );
- }
-
- instances
- .into_iter()
- .map(CString::into_string)
- .collect::<std::result::Result<Vec<String>, _>>()
- .map_err(|e| {
- eprintln!("An interface instance name was not a valid UTF-8 string: {}", e);
- StatusCode::BAD_VALUE
- })
-}
-
/// Safety: `SpIBinder` guarantees that `binder` always contains a valid pointer
/// to an `AIBinder`, so we can trivially extract this pointer here.
unsafe impl AsNative<sys::AIBinder> for SpIBinder {
diff --git a/libs/binder/rust/src/service.rs b/libs/binder/rust/src/service.rs
new file mode 100644
index 0000000..3ca3b54
--- /dev/null
+++ b/libs/binder/rust/src/service.rs
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::binder::{AsNative, FromIBinder, Strong};
+use crate::error::{status_result, Result, StatusCode};
+use crate::proxy::SpIBinder;
+use crate::sys;
+
+use std::ffi::{c_void, CStr, CString};
+use std::os::raw::c_char;
+use std::sync::Mutex;
+
+/// Register a new service with the default service manager.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ let status =
+ // Safety: `AServiceManager_addService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both pointers.
+ // `AServiceManager_addService` creates a new strong reference and copies
+ // the string, so both pointers need only be valid until the call returns.
+ unsafe { sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) };
+ status_result(status)
+}
+
+/// Register a dynamic service via the LazyServiceRegistrar.
+///
+/// Registers the given binder object with the given identifier. If successful,
+/// this service can then be retrieved using that identifier. The service process
+/// will be shut down once all registered services are no longer in use.
+///
+/// If any service in the process is registered as lazy, all should be, otherwise
+/// the process may be shut down while a service is in use.
+///
+/// This function will panic if the identifier contains a 0 byte (NUL).
+pub fn register_lazy_service(identifier: &str, mut binder: SpIBinder) -> Result<()> {
+ let instance = CString::new(identifier).unwrap();
+ // Safety: `AServiceManager_registerLazyService` expects valid `AIBinder` and C
+ // string pointers. Caller retains ownership of both
+ // pointers. `AServiceManager_registerLazyService` creates a new strong reference
+ // and copies the string, so both pointers need only be valid until the
+ // call returns.
+ let status = unsafe {
+ sys::AServiceManager_registerLazyService(binder.as_native_mut(), instance.as_ptr())
+ };
+ status_result(status)
+}
+
+/// Prevent a process which registers lazy services from being shut down even when none
+/// of the services is in use.
+///
+/// If persist is true then shut down will be blocked until this function is called again with
+/// persist false. If this is to be the initial state, call this function before calling
+/// register_lazy_service.
+///
+/// Consider using [`LazyServiceGuard`] rather than calling this directly.
+pub fn force_lazy_services_persist(persist: bool) {
+ // Safety: No borrowing or transfer of ownership occurs here.
+ unsafe { sys::AServiceManager_forceLazyServicesPersist(persist) }
+}
+
+/// An RAII object to ensure a process which registers lazy services is not killed. During the
+/// lifetime of any of these objects the service manager will not kill the process even if none
+/// of its lazy services are in use.
+#[must_use]
+#[derive(Debug)]
+pub struct LazyServiceGuard {
+ // Prevent construction outside this module.
+ _private: (),
+}
+
+// Count of how many LazyServiceGuard objects are in existence.
+static GUARD_COUNT: Mutex<u64> = Mutex::new(0);
+
+impl LazyServiceGuard {
+ /// Create a new LazyServiceGuard to prevent the service manager prematurely killing this
+ /// process.
+ pub fn new() -> Self {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count += 1;
+ if *count == 1 {
+ // It's important that we make this call with the mutex held, to make sure
+ // that multiple calls (e.g. if the count goes 1 -> 0 -> 1) are correctly
+ // sequenced. (That also means we can't just use an AtomicU64.)
+ force_lazy_services_persist(true);
+ }
+ Self { _private: () }
+ }
+}
+
+impl Drop for LazyServiceGuard {
+ fn drop(&mut self) {
+ let mut count = GUARD_COUNT.lock().unwrap();
+ *count -= 1;
+ if *count == 0 {
+ force_lazy_services_persist(false);
+ }
+ }
+}
+
+impl Clone for LazyServiceGuard {
+ fn clone(&self) -> Self {
+ Self::new()
+ }
+}
+
+impl Default for LazyServiceGuard {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+ // Safety: This method is always safe to call.
+ unsafe { sys::AIBinder_isHandlingTransaction() }
+}
+
+fn interface_cast<T: FromIBinder + ?Sized>(service: Option<SpIBinder>) -> Result<Strong<T>> {
+ if let Some(service) = service {
+ FromIBinder::try_from(service)
+ } else {
+ Err(StatusCode::NAME_NOT_FOUND)
+ }
+}
+
+/// Retrieve an existing service, blocking for a few seconds if it doesn't yet
+/// exist.
+pub fn get_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ // Safety: `AServiceManager_getService` returns either a null pointer or a
+ // valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) }
+}
+
+/// Retrieve an existing service, or start it if it is configured as a dynamic
+/// service and isn't yet started.
+pub fn wait_for_service(name: &str) -> Option<SpIBinder> {
+ let name = CString::new(name).ok()?;
+ // Safety: `AServiceManager_waitforService` returns either a null pointer or
+ // a valid pointer to an owned `AIBinder`. Either of these values is safe to
+ // pass to `SpIBinder::from_raw`.
+ unsafe { SpIBinder::from_raw(sys::AServiceManager_waitForService(name.as_ptr())) }
+}
+
+/// Retrieve an existing service for a particular interface, blocking for a few
+/// seconds if it doesn't yet exist.
+pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ interface_cast(get_service(name))
+}
+
+/// Retrieve an existing service for a particular interface, or start it if it
+/// is configured as a dynamic service and isn't yet started.
+pub fn wait_for_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Strong<T>> {
+ interface_cast(wait_for_service(name))
+}
+
+/// Check if a service is declared (e.g. in a VINTF manifest)
+pub fn is_declared(interface: &str) -> Result<bool> {
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+
+ // Safety: `interface` is a valid null-terminated C-style string and is only
+ // borrowed for the lifetime of the call. The `interface` local outlives
+ // this call as it lives for the function scope.
+ unsafe { Ok(sys::AServiceManager_isDeclared(interface.as_ptr())) }
+}
+
+/// Retrieve all declared instances for a particular interface
+///
+/// For instance, if 'android.foo.IFoo/foo' is declared, and 'android.foo.IFoo'
+/// is passed here, then ["foo"] would be returned.
+pub fn get_declared_instances(interface: &str) -> Result<Vec<String>> {
+ unsafe extern "C" fn callback(instance: *const c_char, opaque: *mut c_void) {
+ // Safety: opaque was a mutable pointer created below from a Vec of
+ // CString, and outlives this callback. The null handling here is just
+ // to avoid the possibility of unwinding across C code if this crate is
+ // ever compiled with panic=unwind.
+ if let Some(instances) = unsafe { opaque.cast::<Vec<CString>>().as_mut() } {
+ // Safety: instance is a valid null-terminated C string with a
+ // lifetime at least as long as this function, and we immediately
+ // copy it into an owned CString.
+ unsafe {
+ instances.push(CStr::from_ptr(instance).to_owned());
+ }
+ } else {
+ eprintln!("Opaque pointer was null in get_declared_instances callback!");
+ }
+ }
+
+ let interface = CString::new(interface).or(Err(StatusCode::UNEXPECTED_NULL))?;
+ let mut instances: Vec<CString> = vec![];
+ // Safety: `interface` and `instances` are borrowed for the length of this
+ // call and both outlive the call. `interface` is guaranteed to be a valid
+ // null-terminated C-style string.
+ unsafe {
+ sys::AServiceManager_forEachDeclaredInstance(
+ interface.as_ptr(),
+ &mut instances as *mut _ as *mut c_void,
+ Some(callback),
+ );
+ }
+
+ instances
+ .into_iter()
+ .map(CString::into_string)
+ .collect::<std::result::Result<Vec<String>, _>>()
+ .map_err(|e| {
+ eprintln!("An interface instance name was not a valid UTF-8 string: {}", e);
+ StatusCode::BAD_VALUE
+ })
+}
diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs
index c5c847b..5352473 100644
--- a/libs/binder/rust/sys/lib.rs
+++ b/libs/binder/rust/sys/lib.rs
@@ -25,7 +25,9 @@
}
// Trusty puts the full path to the auto-generated file in BINDGEN_INC_FILE
-// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)]
+// and builds it with warnings-as-errors, so we need to use #[allow(bad_style)].
+// We need to use cfg(target_os) instead of cfg(trusty) here because of
+// the difference between the two build systems, which we cannot mock.
#[cfg(target_os = "trusty")]
#[allow(bad_style)]
mod bindings {
diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp
index 2f0987f..6800a8d 100644
--- a/libs/binder/tests/Android.bp
+++ b/libs/binder/tests/Android.bp
@@ -200,9 +200,11 @@
defaults: [
"binder_test_defaults",
],
+ header_libs: [
+ "libbinder_headers_base",
+ ],
shared_libs: [
"libbase",
- "liblog",
],
srcs: [
"FileUtils.cpp",
@@ -435,6 +437,8 @@
// Add the Trusty mock library as a fake dependency so it gets built
required: [
"libbinder_on_trusty_mock",
+ "libbinder_ndk_on_trusty_mock",
+ "libbinder_rs_on_trusty_mock",
"binderRpcTestService_on_trusty_mock",
"binderRpcTest_on_trusty_mock",
],
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 0ee96e7..1f61f18 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -115,6 +115,7 @@
BINDER_LIB_TEST_GET_SCHEDULING_POLICY,
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
+ BINDER_LIB_TEST_GETUID,
BINDER_LIB_TEST_ECHO_VECTOR,
BINDER_LIB_TEST_GET_NON_BLOCKING_FD,
BINDER_LIB_TEST_REJECT_OBJECTS,
@@ -505,10 +506,11 @@
// Pass test on devices where BINDER_FREEZE ioctl is not supported
int ret = IPCThreadState::self()->freeze(pid, false, 0);
- if (ret != 0) {
+ if (ret == -EINVAL) {
GTEST_SKIP();
return;
}
+ EXPECT_EQ(NO_ERROR, ret);
EXPECT_EQ(-EAGAIN, IPCThreadState::self()->freeze(pid, true, 0));
@@ -1477,6 +1479,86 @@
EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount);
}
+static constexpr int kBpCountHighWatermark = 20;
+static constexpr int kBpCountLowWatermark = 10;
+static constexpr int kBpCountWarningWatermark = 15;
+static constexpr int kInvalidUid = -1;
+
+TEST_F(BinderLibTest, BinderProxyCountCallback) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_NE(server, nullptr);
+
+ BpBinder::enableCountByUid();
+ EXPECT_THAT(m_server->transact(BINDER_LIB_TEST_GETUID, data, &reply), StatusEq(NO_ERROR));
+ int32_t uid = reply.readInt32();
+ ASSERT_NE(uid, kInvalidUid);
+
+ uint32_t initialCount = BpBinder::getBinderProxyCount();
+ {
+ uint32_t count = initialCount;
+ BpBinder::setBinderProxyCountWatermarks(kBpCountHighWatermark,
+ kBpCountLowWatermark,
+ kBpCountWarningWatermark);
+ int limitCallbackUid = kInvalidUid;
+ int warningCallbackUid = kInvalidUid;
+ BpBinder::setBinderProxyCountEventCallback([&](int uid) { limitCallbackUid = uid; },
+ [&](int uid) { warningCallbackUid = uid; });
+
+ std::vector<sp<IBinder> > proxies;
+ auto createProxyOnce = [&](int expectedWarningCallbackUid, int expectedLimitCallbackUid) {
+ warningCallbackUid = limitCallbackUid = kInvalidUid;
+ ASSERT_THAT(server->transact(BINDER_LIB_TEST_CREATE_BINDER_TRANSACTION, data, &reply),
+ StatusEq(NO_ERROR));
+ proxies.push_back(reply.readStrongBinder());
+ EXPECT_EQ(BpBinder::getBinderProxyCount(), ++count);
+ EXPECT_EQ(warningCallbackUid, expectedWarningCallbackUid);
+ EXPECT_EQ(limitCallbackUid, expectedLimitCallbackUid);
+ };
+ auto removeProxyOnce = [&](int expectedWarningCallbackUid, int expectedLimitCallbackUid) {
+ warningCallbackUid = limitCallbackUid = kInvalidUid;
+ proxies.pop_back();
+ EXPECT_EQ(BpBinder::getBinderProxyCount(), --count);
+ EXPECT_EQ(warningCallbackUid, expectedWarningCallbackUid);
+ EXPECT_EQ(limitCallbackUid, expectedLimitCallbackUid);
+ };
+
+ // Test the increment/decrement of the binder proxies.
+ for (int i = 1; i <= kBpCountWarningWatermark; i++) {
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(uid, kInvalidUid); // Warning callback should have been triggered.
+ for (int i = kBpCountWarningWatermark + 2; i <= kBpCountHighWatermark; i++) {
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(kInvalidUid, uid); // Limit callback should have been triggered.
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ for (int i = kBpCountHighWatermark + 2; i >= kBpCountHighWatermark; i--) {
+ removeProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(kInvalidUid, kInvalidUid);
+
+ // Go down below the low watermark.
+ for (int i = kBpCountHighWatermark; i >= kBpCountLowWatermark; i--) {
+ removeProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ for (int i = kBpCountLowWatermark; i <= kBpCountWarningWatermark; i++) {
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(uid, kInvalidUid); // Warning callback should have been triggered.
+ for (int i = kBpCountWarningWatermark + 2; i <= kBpCountHighWatermark; i++) {
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(kInvalidUid, uid); // Limit callback should have been triggered.
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ for (int i = kBpCountHighWatermark + 2; i >= kBpCountHighWatermark; i--) {
+ removeProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ createProxyOnce(kInvalidUid, kInvalidUid);
+ }
+ EXPECT_EQ(BpBinder::getBinderProxyCount(), initialCount);
+}
+
class BinderLibRpcTestBase : public BinderLibTest {
public:
void SetUp() override {
@@ -1680,6 +1762,9 @@
case BINDER_LIB_TEST_GETPID:
reply->writeInt32(getpid());
return NO_ERROR;
+ case BINDER_LIB_TEST_GETUID:
+ reply->writeInt32(getuid());
+ return NO_ERROR;
case BINDER_LIB_TEST_NOP_TRANSACTION_WAIT:
usleep(5000);
[[fallthrough]];
diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h
index 62fe9e5..8832f1a 100644
--- a/libs/binder/tests/binderRpcTestCommon.h
+++ b/libs/binder/tests/binderRpcTestCommon.h
@@ -74,6 +74,12 @@
}
static inline bool hasExperimentalRpc() {
+#ifdef BINDER_RPC_TO_TRUSTY_TEST
+ // Trusty services do not support the experimental version,
+ // so that we can update the prebuilts separately.
+ // This covers the binderRpcToTrustyTest case on Android.
+ return false;
+#endif
#ifdef __ANDROID__
return base::GetProperty("ro.build.version.codename", "") != "REL";
#else
diff --git a/libs/binder/tests/binderRpcUniversalTests.cpp b/libs/binder/tests/binderRpcUniversalTests.cpp
index 885bb45..f480780 100644
--- a/libs/binder/tests/binderRpcUniversalTests.cpp
+++ b/libs/binder/tests/binderRpcUniversalTests.cpp
@@ -48,11 +48,13 @@
EXPECT_FALSE(session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_NEXT + 15));
}
+#ifndef BINDER_RPC_TO_TRUSTY_TEST
TEST(BinderRpc, CanUseExperimentalWireVersion) {
auto session = RpcSession::make();
EXPECT_EQ(hasExperimentalRpc(),
session->setProtocolVersion(RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL));
}
+#endif
TEST_P(BinderRpc, Ping) {
auto proc = createRpcTestSocketServerProcess({});
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 10912c7..f912348 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -7,9 +7,10 @@
#include <cstdlib>
#include <cstdio>
+#include <fstream>
#include <iostream>
-#include <vector>
#include <tuple>
+#include <vector>
#include <unistd.h>
#include <sys/wait.h>
@@ -63,6 +64,18 @@
uint64_t worst() {
return *max_element(data.begin(), data.end());
}
+ void dump_to_file(string filename) {
+ ofstream output;
+ output.open(filename);
+ if (!output.is_open()) {
+ cerr << "Failed to open '" << filename << "'." << endl;
+ exit(EXIT_FAILURE);
+ }
+ for (uint64_t value : data) {
+ output << value << "\n";
+ }
+ output.close();
+ }
void dump() {
if (data.size() == 0) {
// This avoids index-out-of-bounds below.
@@ -293,12 +306,8 @@
}
}
-void run_main(int iterations,
- int workers,
- int payload_size,
- int cs_pair,
- bool training_round=false)
-{
+void run_main(int iterations, int workers, int payload_size, int cs_pair,
+ bool training_round = false, bool dump_to_file = false, string dump_filename = "") {
vector<Pipe> pipes;
// Create all the workers and wait for them to spawn.
for (int i = 0; i < workers; i++) {
@@ -349,6 +358,9 @@
warn_latency = 2 * tot_results.worst();
cout << "Max latency during training: " << tot_results.worst() / 1.0E6 << "ms" << endl;
} else {
+ if (dump_to_file) {
+ tot_results.dump_to_file(dump_filename);
+ }
tot_results.dump();
}
}
@@ -361,6 +373,8 @@
bool cs_pair = false;
bool training_round = false;
int max_time_us;
+ bool dump_to_file = false;
+ string dump_filename;
// Parse arguments.
for (int i = 1; i < argc; i++) {
@@ -372,6 +386,7 @@
cout << "\t-s N : Specify payload size." << endl;
cout << "\t-t : Run training round." << endl;
cout << "\t-w N : Specify total number of workers." << endl;
+ cout << "\t-d FILE : Dump raw data to file." << endl;
return 0;
}
if (string(argv[i]) == "-w") {
@@ -430,14 +445,24 @@
i++;
continue;
}
+ if (string(argv[i]) == "-d") {
+ if (i + 1 == argc) {
+ cout << "-d requires an argument\n" << endl;
+ exit(EXIT_FAILURE);
+ }
+ dump_to_file = true;
+ dump_filename = argv[i + 1];
+ i++;
+ continue;
+ }
}
if (training_round) {
cout << "Start training round" << endl;
- run_main(iterations, workers, payload_size, cs_pair, training_round=true);
+ run_main(iterations, workers, payload_size, cs_pair, true);
cout << "Completed training round" << endl << endl;
}
- run_main(iterations, workers, payload_size, cs_pair);
+ run_main(iterations, workers, payload_size, cs_pair, false, dump_to_file, dump_filename);
return 0;
}
diff --git a/libs/binder/tests/parcel_fuzzer/main.cpp b/libs/binder/tests/parcel_fuzzer/main.cpp
index 5b1e9ea..a57d07f 100644
--- a/libs/binder/tests/parcel_fuzzer/main.cpp
+++ b/libs/binder/tests/parcel_fuzzer/main.cpp
@@ -46,7 +46,18 @@
(void)options;
std::vector<uint8_t> input = provider.ConsumeRemainingBytes<uint8_t>();
- p->setData(input.data(), input.size());
+
+ if (input.size() % 4 != 0) {
+ input.resize(input.size() + (sizeof(uint32_t) - input.size() % sizeof(uint32_t)));
+ }
+ CHECK_EQ(0, input.size() % 4);
+
+ p->setDataCapacity(input.size());
+ for (size_t i = 0; i < input.size(); i += 4) {
+ p->writeInt32(*((int32_t*)(input.data() + i)));
+ }
+
+ CHECK_EQ(0, memcmp(input.data(), p->data(), p->dataSize()));
}
static void fillRandomParcel(NdkParcelAdapter* p, FuzzedDataProvider&& provider,
RandomParcelOptions* options) {
diff --git a/libs/binder/tests/unit_fuzzers/Android.bp b/libs/binder/tests/unit_fuzzers/Android.bp
index a881582..6871cca 100644
--- a/libs/binder/tests/unit_fuzzers/Android.bp
+++ b/libs/binder/tests/unit_fuzzers/Android.bp
@@ -52,6 +52,18 @@
enabled: false,
},
},
+ fuzz_config: {
+ cc: [
+ "smoreland@google.com",
+ "waghpawan@google.com",
+ ],
+ componentid: 32456,
+ description: "The fuzzer targets the APIs of libbinder",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
+ },
}
cc_fuzz {
diff --git a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
index 0a584bf..83d0ca7 100644
--- a/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
+++ b/libs/binder/tests/unit_fuzzers/BpBinderFuzzFunctions.h
@@ -95,14 +95,16 @@
},
[](FuzzedDataProvider*, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>&) -> void {
- binder_proxy_limit_callback cb = binder_proxy_limit_callback();
- bpbinder->setLimitCallback(cb);
+ binder_proxy_limit_callback cbl = binder_proxy_limit_callback();
+ binder_proxy_warning_callback cbw = binder_proxy_warning_callback();
+ bpbinder->setBinderProxyCountEventCallback(cbl, cbw);
},
[](FuzzedDataProvider* fdp, const sp<BpBinder>& bpbinder,
const sp<IBinder::DeathRecipient>&) -> void {
int high = fdp->ConsumeIntegral<int>();
int low = fdp->ConsumeIntegral<int>();
- bpbinder->setBinderProxyCountWatermarks(high, low);
+ int warning = fdp->ConsumeIntegral<int>();
+ bpbinder->setBinderProxyCountWatermarks(high, low, warning);
}};
} // namespace android
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 1f857a0..17919c2 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -60,7 +60,7 @@
RpcServerTrusty::RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize)
- : mRpcServer(sp<RpcServer>::make(std::move(ctx))),
+ : mRpcServer(makeRpcServer(std::move(ctx))),
mPortName(std::move(portName)),
mPortAcl(std::move(portAcl)) {
mTipcPort.name = mPortName.c_str();
@@ -68,10 +68,6 @@
mTipcPort.msg_queue_len = 6; // Three each way
mTipcPort.priv = this;
- // TODO(b/266741352): follow-up to prevent needing this in the future
- // Trusty needs to be set to the latest stable version that is in prebuilts there.
- LOG_ALWAYS_FATAL_IF(!mRpcServer->setProtocolVersion(0));
-
if (mPortAcl) {
// Initialize the array of pointers to uuids.
// The pointers in mUuidPtrs should stay valid across moves of
@@ -101,8 +97,13 @@
int RpcServerTrusty::handleConnect(const tipc_port* port, handle_t chan, const uuid* peer,
void** ctx_p) {
auto* server = reinterpret_cast<RpcServerTrusty*>(const_cast<void*>(port->priv));
- server->mRpcServer->mShutdownTrigger = FdTrigger::make();
- server->mRpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread();
+ return handleConnectInternal(server->mRpcServer.get(), chan, peer, ctx_p);
+}
+
+int RpcServerTrusty::handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer,
+ void** ctx_p) {
+ rpcServer->mShutdownTrigger = FdTrigger::make();
+ rpcServer->mConnectingThreads[rpc_this_thread::get_id()] = RpcMaybeThread();
int rc = NO_ERROR;
auto joinFn = [&](sp<RpcSession>&& session, RpcSession::PreJoinSetupResult&& result) {
@@ -138,13 +139,17 @@
std::array<uint8_t, RpcServer::kRpcAddressSize> addr;
constexpr size_t addrLen = sizeof(*peer);
memcpy(addr.data(), peer, addrLen);
- RpcServer::establishConnection(sp(server->mRpcServer), std::move(transportFd), addr, addrLen,
- joinFn);
+ RpcServer::establishConnection(sp<RpcServer>::fromExisting(rpcServer), std::move(transportFd),
+ addr, addrLen, joinFn);
return rc;
}
int RpcServerTrusty::handleMessage(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) {
+ return handleMessageInternal(ctx);
+}
+
+int RpcServerTrusty::handleMessageInternal(void* ctx) {
auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
LOG_ALWAYS_FATAL_IF(channelContext == nullptr,
"bad state: message received on uninitialized channel");
@@ -162,6 +167,10 @@
}
void RpcServerTrusty::handleDisconnect(const tipc_port* /*port*/, handle_t /*chan*/, void* ctx) {
+ return handleDisconnectInternal(ctx);
+}
+
+void RpcServerTrusty::handleDisconnectInternal(void* ctx) {
auto* channelContext = reinterpret_cast<ChannelContext*>(ctx);
if (channelContext == nullptr) {
// Connections marked "incoming" (outgoing from the server's side)
diff --git a/libs/binder/trusty/build-config-usertests b/libs/binder/trusty/build-config-usertests
index d0a1fbc..72e5ff9 100644
--- a/libs/binder/trusty/build-config-usertests
+++ b/libs/binder/trusty/build-config-usertests
@@ -16,4 +16,5 @@
[
porttest("com.android.trusty.binderRpcTest"),
+ porttest("com.android.trusty.rust.binder_rpc_test.test"),
]
diff --git a/libs/binder/trusty/include/binder/ARpcServerTrusty.h b/libs/binder/trusty/include/binder/ARpcServerTrusty.h
new file mode 100644
index 0000000..c82268b
--- /dev/null
+++ b/libs/binder/trusty/include/binder/ARpcServerTrusty.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <lib/tipc/tipc_srv.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+struct AIBinder;
+struct ARpcServerTrusty;
+
+struct ARpcServerTrusty* ARpcServerTrusty_newPerSession(struct AIBinder* (*)(const void*, size_t,
+ char*),
+ char*, void (*)(char*));
+void ARpcServerTrusty_delete(struct ARpcServerTrusty*);
+int ARpcServerTrusty_handleConnect(struct ARpcServerTrusty*, handle_t, const struct uuid*, void**);
+int ARpcServerTrusty_handleMessage(void*);
+void ARpcServerTrusty_handleDisconnect(void*);
+void ARpcServerTrusty_handleChannelCleanup(void*);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index f35d6c2..fe44ea5 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -16,6 +16,7 @@
#pragma once
+#include <binder/ARpcServerTrusty.h>
#include <binder/IBinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
@@ -88,6 +89,28 @@
explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName,
std::shared_ptr<const PortAcl>&& portAcl, size_t msgMaxSize);
+ // Internal helper that creates the RpcServer.
+ // This is used both from here and Rust.
+ static sp<RpcServer> makeRpcServer(std::unique_ptr<RpcTransportCtx> ctx) {
+ auto rpcServer = sp<RpcServer>::make(std::move(ctx));
+
+ // TODO(b/266741352): follow-up to prevent needing this in the future
+ // Trusty needs to be set to the latest stable version that is in prebuilts there.
+ LOG_ALWAYS_FATAL_IF(!rpcServer->setProtocolVersion(0));
+
+ return rpcServer;
+ }
+
+ friend struct ::ARpcServerTrusty;
+ friend ::ARpcServerTrusty* ::ARpcServerTrusty_newPerSession(::AIBinder* (*)(const void*, size_t,
+ char*),
+ char*, void (*)(char*));
+ friend void ::ARpcServerTrusty_delete(::ARpcServerTrusty*);
+ friend int ::ARpcServerTrusty_handleConnect(::ARpcServerTrusty*, handle_t, const uuid*, void**);
+ friend int ::ARpcServerTrusty_handleMessage(void*);
+ friend void ::ARpcServerTrusty_handleDisconnect(void*);
+ friend void ::ARpcServerTrusty_handleChannelCleanup(void*);
+
// The Rpc-specific context maintained for every open TIPC channel.
struct ChannelContext {
sp<RpcSession> session;
@@ -99,6 +122,11 @@
static void handleDisconnect(const tipc_port* port, handle_t chan, void* ctx);
static void handleChannelCleanup(void* ctx);
+ static int handleConnectInternal(RpcServer* rpcServer, handle_t chan, const uuid* peer,
+ void** ctx_p);
+ static int handleMessageInternal(void* ctx);
+ static void handleDisconnectInternal(void* ctx);
+
static constexpr tipc_srv_ops kTipcOps = {
.on_connect = &handleConnect,
.on_message = &handleMessage,
diff --git a/libs/binder/trusty/ndk/Android.bp b/libs/binder/trusty/ndk/Android.bp
new file mode 100644
index 0000000..af9874a
--- /dev/null
+++ b/libs/binder/trusty/ndk/Android.bp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_headers {
+ name: "libbinder_trusty_ndk_headers",
+ export_include_dirs: ["include"],
+ host_supported: true,
+ vendor_available: true,
+}
diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
similarity index 80%
rename from libs/gui/aidl/android/gui/LayerDebugInfo.aidl
rename to libs/binder/trusty/ndk/include/android/llndk-versioning.h
index faca980..3ae3d8f 100644
--- a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl
+++ b/libs/binder/trusty/ndk/include/android/llndk-versioning.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#pragma once
-package android.gui;
-
-parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h";
+#define __INTRODUCED_IN_LLNDK(x) /* nothing on Trusty */
diff --git a/libs/binder/trusty/ndk/include/sys/cdefs.h b/libs/binder/trusty/ndk/include/sys/cdefs.h
index 6a48d2b..4e9b0e8 100644
--- a/libs/binder/trusty/ndk/include/sys/cdefs.h
+++ b/libs/binder/trusty/ndk/include/sys/cdefs.h
@@ -15,10 +15,15 @@
*/
#pragma once
+#if __has_include(<lk/compiler.h>)
#include <lk/compiler.h>
/* Alias the bionic macros to the ones from lk/compiler.h */
#define __BEGIN_DECLS __BEGIN_CDECLS
#define __END_DECLS __END_CDECLS
+#else // __has_include(<lk/compiler.h>)
+#include_next <sys/cdefs.h>
+#endif
+
#define __INTRODUCED_IN(x) /* nothing on Trusty */
diff --git a/libs/binder/trusty/ndk/rules.mk b/libs/binder/trusty/ndk/rules.mk
index 03fd006..7a275c2 100644
--- a/libs/binder/trusty/ndk/rules.mk
+++ b/libs/binder/trusty/ndk/rules.mk
@@ -23,6 +23,7 @@
$(LIBBINDER_NDK_DIR)/ibinder.cpp \
$(LIBBINDER_NDK_DIR)/libbinder.cpp \
$(LIBBINDER_NDK_DIR)/parcel.cpp \
+ $(LIBBINDER_NDK_DIR)/stability.cpp \
$(LIBBINDER_NDK_DIR)/status.cpp \
MODULE_EXPORT_INCLUDES += \
diff --git a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
index 672d9b7..2aaa061 100644
--- a/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
+++ b/libs/binder/trusty/rust/binder_ndk_sys/rules.mk
@@ -29,6 +29,10 @@
$(LIBBINDER_DIR)/trusty/ndk \
trusty/user/base/lib/trusty-sys \
+MODULE_RUSTFLAGS += \
+ --cfg 'android_vendor' \
+ --cfg 'trusty' \
+
MODULE_BINDGEN_SRC_HEADER := $(LIBBINDER_DIR)/rust/sys/BinderBindings.hpp
# Add the flags from the flag file
diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp
new file mode 100644
index 0000000..451383a
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/ARpcServerTrusty.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/binder_libbinder.h>
+#include <binder/RpcServer.h>
+#include <binder/RpcServerTrusty.h>
+#include <binder/RpcSession.h>
+#include <binder/RpcTransportTipcTrusty.h>
+
+using android::RpcServer;
+using android::RpcServerTrusty;
+using android::RpcSession;
+using android::RpcTransportCtxFactoryTipcTrusty;
+using android::sp;
+using android::wp;
+
+struct ARpcServerTrusty {
+ sp<RpcServer> mRpcServer;
+
+ ARpcServerTrusty() = delete;
+ ARpcServerTrusty(sp<RpcServer> rpcServer) : mRpcServer(std::move(rpcServer)) {}
+};
+
+ARpcServerTrusty* ARpcServerTrusty_newPerSession(AIBinder* (*cb)(const void*, size_t, char*),
+ char* cbArg, void (*cbArgDeleter)(char*)) {
+ std::shared_ptr<char> cbArgSp(cbArg, cbArgDeleter);
+
+ auto rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
+ if (rpcTransportCtxFactory == nullptr) {
+ return nullptr;
+ }
+
+ auto ctx = rpcTransportCtxFactory->newServerCtx();
+ if (ctx == nullptr) {
+ return nullptr;
+ }
+
+ auto rpcServer = RpcServerTrusty::makeRpcServer(std::move(ctx));
+ if (rpcServer == nullptr) {
+ return nullptr;
+ }
+
+ rpcServer->setPerSessionRootObject(
+ [cb, cbArgSp](wp<RpcSession> /*session*/, const void* addrPtr, size_t len) {
+ auto* aib = (*cb)(addrPtr, len, cbArgSp.get());
+ auto b = AIBinder_toPlatformBinder(aib);
+
+ // We have a new sp<IBinder> backed by the same binder, so we can
+ // finally release the AIBinder* from the callback
+ AIBinder_decStrong(aib);
+
+ return b;
+ });
+
+ return new (std::nothrow) ARpcServerTrusty(std::move(rpcServer));
+}
+
+void ARpcServerTrusty_delete(ARpcServerTrusty* rstr) {
+ delete rstr;
+}
+
+int ARpcServerTrusty_handleConnect(ARpcServerTrusty* rstr, handle_t chan, const uuid* peer,
+ void** ctx_p) {
+ return RpcServerTrusty::handleConnectInternal(rstr->mRpcServer.get(), chan, peer, ctx_p);
+}
+
+int ARpcServerTrusty_handleMessage(void* ctx) {
+ return RpcServerTrusty::handleMessageInternal(ctx);
+}
+
+void ARpcServerTrusty_handleDisconnect(void* ctx) {
+ RpcServerTrusty::handleDisconnectInternal(ctx);
+}
+
+void ARpcServerTrusty_handleChannelCleanup(void* ctx) {
+ RpcServerTrusty::handleChannelCleanup(ctx);
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk
new file mode 100644
index 0000000..6def634
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/cpp/rules.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := \
+ $(LOCAL_DIR)/ARpcServerTrusty.cpp \
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty \
+ $(LIBBINDER_DIR)/trusty/ndk \
+ trusty/user/base/lib/libstdc++-trusty \
+
+include make/library.mk
diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs
similarity index 74%
copy from libs/gui/aidl/android/gui/LayerDebugInfo.aidl
copy to libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs
index faca980..2e8b3ec 100644
--- a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl
+++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/lib.rs
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.gui;
+//! Generated Rust bindings to binder_rpc_server
-parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h";
+#[allow(bad_style)]
+mod sys {
+ include!(env!("BINDGEN_INC_FILE"));
+}
+
+pub use sys::*;
diff --git a/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk b/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk
new file mode 100644
index 0000000..4ee333f
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_server_bindgen/rules.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/lib.rs
+
+MODULE_CRATE_NAME := binder_rpc_server_bindgen
+
+MODULE_LIBRARY_DEPS += \
+ $(LOCAL_DIR)/cpp \
+ trusty/user/base/lib/libstdc++-trusty \
+ trusty/user/base/lib/trusty-sys \
+
+MODULE_BINDGEN_SRC_HEADER := \
+ $(LIBBINDER_DIR)/trusty/include/binder/ARpcServerTrusty.h
+
+MODULE_BINDGEN_FLAGS += \
+ --allowlist-type="ARpcServerTrusty" \
+ --allowlist-function="ARpcServerTrusty_.*" \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk
new file mode 100644
index 0000000..1b0dca0
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/aidl/rules.mk
@@ -0,0 +1,34 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_TESTS_DIR := $(LOCAL_DIR)/../../../../tests
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_AIDL_LANGUAGE := rust
+
+MODULE_CRATE_NAME := binder_rpc_test_aidl
+
+MODULE_AIDLS := \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestClientInfo.aidl \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerConfig.aidl \
+ $(LIBBINDER_TESTS_DIR)/BinderRpcTestServerInfo.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcCallback.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcSession.aidl \
+ $(LIBBINDER_TESTS_DIR)/IBinderRpcTest.aidl \
+ $(LIBBINDER_TESTS_DIR)/ParcelableCertificateData.aidl \
+
+include make/aidl.mk
diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
new file mode 100644
index 0000000..22cba44
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/lib.rs
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+use binder::{Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode, Strong};
+use binder_rpc_test_aidl::aidl::IBinderRpcCallback::IBinderRpcCallback;
+use binder_rpc_test_aidl::aidl::IBinderRpcSession::IBinderRpcSession;
+use binder_rpc_test_aidl::aidl::IBinderRpcTest::IBinderRpcTest;
+use std::sync::Mutex;
+
+static G_NUM: Mutex<i32> = Mutex::new(0);
+
+#[derive(Debug, Default)]
+pub struct MyBinderRpcSession {
+ name: String,
+}
+
+impl MyBinderRpcSession {
+ pub fn new(name: &str) -> Self {
+ Self::increment_instance_count();
+ Self { name: name.to_string() }
+ }
+
+ pub fn get_instance_count() -> i32 {
+ *G_NUM.lock().unwrap()
+ }
+
+ fn increment_instance_count() {
+ *G_NUM.lock().unwrap() += 1;
+ }
+
+ fn decrement_instance_count() {
+ *G_NUM.lock().unwrap() -= 1;
+ }
+}
+
+impl Drop for MyBinderRpcSession {
+ fn drop(&mut self) {
+ MyBinderRpcSession::decrement_instance_count();
+ }
+}
+
+impl Interface for MyBinderRpcSession {}
+
+impl IBinderRpcSession for MyBinderRpcSession {
+ fn getName(&self) -> Result<String, Status> {
+ Ok(self.name.clone())
+ }
+}
+
+impl IBinderRpcTest for MyBinderRpcSession {
+ fn sendString(&self, _: &str) -> Result<(), Status> {
+ todo!()
+ }
+ fn doubleString(&self, _s: &str) -> Result<String, Status> {
+ todo!()
+ }
+ fn getClientPort(&self) -> Result<i32, Status> {
+ todo!()
+ }
+ fn countBinders(&self) -> Result<Vec<i32>, Status> {
+ todo!()
+ }
+ fn getNullBinder(&self) -> Result<SpIBinder, Status> {
+ todo!()
+ }
+ fn pingMe(&self, _binder: &SpIBinder) -> Result<i32, Status> {
+ todo!()
+ }
+ fn repeatBinder(&self, _binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> {
+ todo!()
+ }
+ fn holdBinder(&self, _binder: Option<&SpIBinder>) -> Result<(), Status> {
+ todo!()
+ }
+ fn getHeldBinder(&self) -> Result<Option<SpIBinder>, Status> {
+ todo!()
+ }
+ fn nestMe(
+ &self,
+ binder: &Strong<(dyn IBinderRpcTest + 'static)>,
+ count: i32,
+ ) -> Result<(), Status> {
+ if count < 0 {
+ Ok(())
+ } else {
+ binder.nestMe(binder, count - 1)
+ }
+ }
+ fn alwaysGiveMeTheSameBinder(&self) -> Result<SpIBinder, Status> {
+ todo!()
+ }
+ fn openSession(
+ &self,
+ _name: &str,
+ ) -> Result<Strong<(dyn IBinderRpcSession + 'static)>, Status> {
+ todo!()
+ }
+ fn getNumOpenSessions(&self) -> Result<i32, Status> {
+ todo!()
+ }
+ fn lock(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn unlockInMsAsync(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn lockUnlock(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn sleepMs(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn sleepMsAsync(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn doCallback(
+ &self,
+ _: &Strong<(dyn IBinderRpcCallback + 'static)>,
+ _: bool,
+ _: bool,
+ _: &str,
+ ) -> Result<(), Status> {
+ todo!()
+ }
+ fn doCallbackAsync(
+ &self,
+ _: &Strong<(dyn IBinderRpcCallback + 'static)>,
+ _: bool,
+ _: bool,
+ _: &str,
+ ) -> Result<(), Status> {
+ todo!()
+ }
+ fn die(&self, _: bool) -> Result<(), Status> {
+ Err(Status::from(StatusCode::UNKNOWN_TRANSACTION))
+ }
+ fn scheduleShutdown(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn useKernelBinderCallingId(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn echoAsFile(&self, _: &str) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn concatFiles(&self, _: &[ParcelFileDescriptor]) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn blockingSendFdOneway(&self, _: &ParcelFileDescriptor) -> Result<(), Status> {
+ todo!()
+ }
+ fn blockingRecvFd(&self) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn blockingSendIntOneway(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn blockingRecvInt(&self) -> Result<i32, Status> {
+ todo!()
+ }
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk
new file mode 100644
index 0000000..ae26355
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/binder_rpc_test_session/rules.mk
@@ -0,0 +1,32 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/lib.rs
+
+MODULE_CRATE_NAME := binder_rpc_test_session
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/rpcbinder \
+ $(LOCAL_DIR)/../aidl \
+ $(call FIND_CRATE,log) \
+ trusty/user/base/lib/trusty-std \
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/binder_rpc_test/main.rs b/libs/binder/trusty/rust/binder_rpc_test/main.rs
new file mode 100644
index 0000000..baea5a8
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/main.rs
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+#![cfg(test)]
+
+use binder::{BinderFeatures, IBinder, Status, StatusCode, Strong};
+use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession};
+use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest};
+use binder_rpc_test_session::MyBinderRpcSession;
+use libc::{clock_gettime, CLOCK_REALTIME};
+use rpcbinder::RpcSession;
+use trusty_std::ffi::{CString, FallibleCString};
+
+test::init!();
+
+const SERVICE_PORT: &str = "com.android.trusty.binderRpcTestService.V1";
+const RUST_SERVICE_PORT: &str = "com.android.trusty.rust.binderRpcTestService.V1";
+
+macro_rules! service_test {
+ ($c_name:ident, $rust_name:ident, $body:expr) => {
+ #[test]
+ fn $c_name() {
+ $body(get_service(SERVICE_PORT))
+ }
+ #[test]
+ fn $rust_name() {
+ $body(get_service(RUST_SERVICE_PORT))
+ }
+ };
+}
+
+fn get_service(port: &str) -> Strong<dyn IBinderRpcTest> {
+ let port = CString::try_new(port).expect("Failed to allocate port name");
+ RpcSession::new().setup_trusty_client(port.as_c_str()).expect("Failed to create session")
+}
+
+fn expect_sessions(expected: i32, srv: &Strong<dyn IBinderRpcTest>) {
+ let count = srv.getNumOpenSessions();
+ assert!(count.is_ok());
+ assert_eq!(expected, count.unwrap());
+}
+
+fn get_time_ns() -> u64 {
+ let mut ts = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+
+ // Safety: Passing valid pointer to variable ts which lives past end of call
+ assert_eq!(unsafe { clock_gettime(CLOCK_REALTIME, &mut ts) }, 0);
+
+ ts.tv_sec as u64 * 1_000_000_000u64 + ts.tv_nsec as u64
+}
+
+fn get_time_ms() -> u64 {
+ get_time_ns() / 1_000_000u64
+}
+
+// ----------
+
+service_test! {ping, ping_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ assert_eq!(srv.as_binder().ping_binder(), Ok(()));
+}}
+
+service_test! {send_something_oneway, send_something_oneway_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ assert_eq!(srv.sendString("Foo"), Ok(()));
+}}
+
+service_test! {send_and_get_result_back, send_and_get_result_back_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ assert_eq!(srv.doubleString("Foo"), Ok(String::from("FooFoo")));
+}}
+
+service_test! {send_and_get_result_back_big, send_and_get_result_back_big_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let single_len = 512;
+ let single = "a".repeat(single_len);
+ assert_eq!(srv.doubleString(&single), Ok(String::from(single.clone() + &single)));
+}}
+
+service_test! {invalid_null_binder_return, invalid_null_binder_return_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let binder = srv.getNullBinder();
+ assert!(binder == Err(Status::from(StatusCode::UNEXPECTED_NULL)) || binder == Err(Status::from(StatusCode::UNKNOWN_TRANSACTION)));
+}}
+
+service_test! {call_me_back, call_me_back_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let binder =
+ BnBinderRpcSession::new_binder(MyBinderRpcSession::new("Foo"), BinderFeatures::default())
+ .as_binder();
+ let result = srv.pingMe(&binder);
+ assert_eq!(result, Ok(0));
+}}
+
+service_test! {repeat_binder, repeat_binder_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let in_binder =
+ BnBinderRpcSession::new_binder(MyBinderRpcSession::new("Foo"), BinderFeatures::default())
+ .as_binder();
+ let result = srv.repeatBinder(Some(&in_binder));
+ assert_eq!(result.unwrap().unwrap(), in_binder);
+}}
+
+service_test! {repeat_their_binder, repeat_their_binder_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let session = srv.openSession("Test");
+ assert!(session.is_ok());
+
+ let in_binder = session.unwrap().as_binder();
+ let out_binder = srv.repeatBinder(Some(&in_binder));
+ assert_eq!(out_binder.unwrap().unwrap(), in_binder);
+}}
+
+service_test! {hold_binder, hold_binder_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let name = "Foo";
+
+ let binder =
+ BnBinderRpcSession::new_binder(MyBinderRpcSession::new(name), BinderFeatures::default())
+ .as_binder();
+ assert!(srv.holdBinder(Some(&binder)).is_ok());
+
+ let held = srv.getHeldBinder();
+ assert!(held.is_ok());
+ let held = held.unwrap();
+ assert!(held.is_some());
+ let held = held.unwrap();
+ assert_eq!(binder, held);
+
+ let session = held.into_interface::<dyn IBinderRpcSession>();
+ assert!(session.is_ok());
+
+ let session_name = session.unwrap().getName();
+ assert!(session_name.is_ok());
+ let session_name = session_name.unwrap();
+ assert_eq!(session_name, name);
+
+ assert!(srv.holdBinder(None).is_ok());
+}}
+
+service_test! {nested_transactions, nested_transactions_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let binder =
+ BnBinderRpcTest::new_binder(MyBinderRpcSession::new("Nest"), BinderFeatures::default());
+ assert!(srv.nestMe(&binder, 10).is_ok());
+}}
+
+service_test! {same_binder_equality, same_binder_equality_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let a = srv.alwaysGiveMeTheSameBinder();
+ assert!(a.is_ok());
+
+ let b = srv.alwaysGiveMeTheSameBinder();
+ assert!(b.is_ok());
+
+ assert_eq!(a.unwrap(), b.unwrap());
+}}
+
+service_test! {single_session, single_session_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let session = srv.openSession("aoeu");
+ assert!(session.is_ok());
+ let session = session.unwrap();
+ let name = session.getName();
+ assert!(name.is_ok());
+ assert_eq!(name.unwrap(), "aoeu");
+
+ let count = srv.getNumOpenSessions();
+ assert!(count.is_ok());
+ assert_eq!(count.unwrap(), 1);
+
+ drop(session);
+ let count = srv.getNumOpenSessions();
+ assert!(count.is_ok());
+ assert_eq!(count.unwrap(), 0);
+}}
+
+service_test! {many_session, many_session_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let mut sessions = Vec::new();
+
+ for i in 0..15 {
+ expect_sessions(i, &srv);
+
+ let session = srv.openSession(&(i.to_string()));
+ assert!(session.is_ok());
+ sessions.push(session.unwrap());
+ }
+
+ expect_sessions(sessions.len() as i32, &srv);
+
+ for i in 0..sessions.len() {
+ let name = sessions[i].getName();
+ assert!(name.is_ok());
+ assert_eq!(name.unwrap(), i.to_string());
+ }
+
+ expect_sessions(sessions.len() as i32, &srv);
+
+ while !sessions.is_empty() {
+ sessions.pop();
+
+ expect_sessions(sessions.len() as i32, &srv);
+ }
+
+ expect_sessions(0, &srv);
+}}
+
+service_test! {one_way_call_does_not_wait, one_way_call_does_not_wait_rust, |srv: Strong<dyn IBinderRpcTest>| {
+ let really_long_time_ms = 100;
+ let sleep_ms = really_long_time_ms * 5;
+
+ let before = get_time_ms();
+ let _ = srv.sleepMsAsync(sleep_ms);
+ let after = get_time_ms();
+
+ assert!(after < before + really_long_time_ms as u64);
+}}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/manifest.json b/libs/binder/trusty/rust/binder_rpc_test/manifest.json
new file mode 100644
index 0000000..384ed44
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/manifest.json
@@ -0,0 +1,9 @@
+{
+ "uuid": "91eed949-8a9e-4569-9c83-5935fb624025",
+ "app_name": "rust_binder_rpc_test",
+ "min_heap": 32768,
+ "min_stack": 16384,
+ "mgmt_flags": {
+ "non_critical_app": true
+ }
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/rules.mk
new file mode 100644
index 0000000..8347a35
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/rules.mk
@@ -0,0 +1,37 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/main.rs
+
+MODULE_CRATE_NAME := binder_rpc_test
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/rpcbinder \
+ $(LOCAL_DIR)/aidl \
+ $(LOCAL_DIR)/binder_rpc_test_session \
+ $(call FIND_CRATE,log) \
+ trusty/user/base/lib/trusty-std \
+
+MODULE_RUST_TESTS := true
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+include make/library.mk
diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/main.rs b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
new file mode 100644
index 0000000..c4a758a
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/service/main.rs
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ */
+use binder::{
+ BinderFeatures, IBinder, Interface, ParcelFileDescriptor, SpIBinder, Status, StatusCode, Strong,
+};
+use binder_rpc_test_aidl::aidl::IBinderRpcCallback::IBinderRpcCallback;
+use binder_rpc_test_aidl::aidl::IBinderRpcSession::{BnBinderRpcSession, IBinderRpcSession};
+use binder_rpc_test_aidl::aidl::IBinderRpcTest::{BnBinderRpcTest, IBinderRpcTest};
+use binder_rpc_test_session::MyBinderRpcSession;
+use libc::{c_long, nanosleep, timespec};
+use rpcbinder::RpcServer;
+use std::rc::Rc;
+use std::sync::Mutex;
+use tipc::{service_dispatcher, wrap_service, Manager, PortCfg};
+
+const RUST_SERVICE_PORT: &str = "com.android.trusty.rust.binderRpcTestService.V1";
+
+// -----------------------------------------------------------------------------
+
+static SESSION_COUNT: Mutex<i32> = Mutex::new(0);
+static HOLD_BINDER: Mutex<Option<SpIBinder>> = Mutex::new(None);
+static SAME_BINDER: Mutex<Option<SpIBinder>> = Mutex::new(None);
+
+#[derive(Debug, Default)]
+struct TestService {
+ port: i32,
+ name: String,
+}
+
+#[allow(dead_code)]
+impl TestService {
+ fn new(name: &str) -> Self {
+ *SESSION_COUNT.lock().unwrap() += 1;
+ Self { name: name.to_string(), ..Default::default() }
+ }
+
+ fn get_instance_count() -> i32 {
+ *SESSION_COUNT.lock().unwrap()
+ }
+}
+
+impl Drop for TestService {
+ fn drop(&mut self) {
+ *SESSION_COUNT.lock().unwrap() -= 1;
+ }
+}
+
+impl Interface for TestService {}
+
+impl IBinderRpcSession for TestService {
+ fn getName(&self) -> Result<String, Status> {
+ Ok(self.name.clone())
+ }
+}
+
+impl IBinderRpcTest for TestService {
+ fn sendString(&self, _: &str) -> Result<(), Status> {
+ // This is a oneway function, so caller returned immediately and gives back an Ok(()) regardless of what this returns
+ Ok(())
+ }
+ fn doubleString(&self, s: &str) -> Result<String, Status> {
+ let ss = [s, s].concat();
+ Ok(ss)
+ }
+ fn getClientPort(&self) -> Result<i32, Status> {
+ Ok(self.port)
+ }
+ fn countBinders(&self) -> Result<Vec<i32>, Status> {
+ todo!()
+ }
+ fn getNullBinder(&self) -> Result<SpIBinder, Status> {
+ Err(Status::from(StatusCode::UNKNOWN_TRANSACTION))
+ }
+ fn pingMe(&self, binder: &SpIBinder) -> Result<i32, Status> {
+ match binder.clone().ping_binder() {
+ Ok(()) => Ok(StatusCode::OK as i32),
+ Err(e) => Err(Status::from(e)),
+ }
+ }
+ fn repeatBinder(&self, binder: Option<&SpIBinder>) -> Result<Option<SpIBinder>, Status> {
+ match binder {
+ Some(x) => Ok(Some(x.clone())),
+ None => Err(Status::from(StatusCode::BAD_VALUE)),
+ }
+ }
+ fn holdBinder(&self, binder: Option<&SpIBinder>) -> Result<(), Status> {
+ *HOLD_BINDER.lock().unwrap() = binder.cloned();
+ Ok(())
+ }
+ fn getHeldBinder(&self) -> Result<Option<SpIBinder>, Status> {
+ Ok((*HOLD_BINDER.lock().unwrap()).clone())
+ }
+ fn nestMe(
+ &self,
+ binder: &Strong<(dyn IBinderRpcTest + 'static)>,
+ count: i32,
+ ) -> Result<(), Status> {
+ if count < 0 {
+ Ok(())
+ } else {
+ binder.nestMe(binder, count - 1)
+ }
+ }
+ fn alwaysGiveMeTheSameBinder(&self) -> Result<SpIBinder, Status> {
+ let mut locked = SAME_BINDER.lock().unwrap();
+ Ok((*locked)
+ .get_or_insert_with(|| {
+ BnBinderRpcTest::new_binder(TestService::default(), BinderFeatures::default())
+ .as_binder()
+ })
+ .clone())
+ }
+ fn openSession(&self, name: &str) -> Result<Strong<(dyn IBinderRpcSession + 'static)>, Status> {
+ let s = BnBinderRpcSession::new_binder(
+ MyBinderRpcSession::new(name),
+ BinderFeatures::default(),
+ );
+ Ok(s)
+ }
+ fn getNumOpenSessions(&self) -> Result<i32, Status> {
+ let count = MyBinderRpcSession::get_instance_count();
+ Ok(count)
+ }
+ fn lock(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn unlockInMsAsync(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn lockUnlock(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn sleepMs(&self, ms: i32) -> Result<(), Status> {
+ let ts = timespec {
+ tv_sec: (ms / 1000) as c_long,
+ tv_nsec: (ms % 1000) as c_long * 1_000_000 as c_long,
+ };
+
+ let mut rem = timespec { tv_sec: 0, tv_nsec: 0 };
+
+ // Safety: Passing valid pointers to variables ts & rem which live past end of call
+ assert_eq!(unsafe { nanosleep(&ts, &mut rem) }, 0);
+
+ Ok(())
+ }
+ fn sleepMsAsync(&self, ms: i32) -> Result<(), Status> {
+ self.sleepMs(ms)
+ }
+ fn doCallback(
+ &self,
+ _: &Strong<(dyn IBinderRpcCallback + 'static)>,
+ _: bool,
+ _: bool,
+ _: &str,
+ ) -> Result<(), Status> {
+ todo!()
+ }
+ fn doCallbackAsync(
+ &self,
+ _: &Strong<(dyn IBinderRpcCallback + 'static)>,
+ _: bool,
+ _: bool,
+ _: &str,
+ ) -> Result<(), Status> {
+ todo!()
+ }
+ fn die(&self, _: bool) -> Result<(), Status> {
+ Err(Status::from(StatusCode::UNKNOWN_TRANSACTION))
+ }
+ fn scheduleShutdown(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn useKernelBinderCallingId(&self) -> Result<(), Status> {
+ todo!()
+ }
+ fn echoAsFile(&self, _: &str) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn concatFiles(&self, _: &[ParcelFileDescriptor]) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn blockingSendFdOneway(&self, _: &ParcelFileDescriptor) -> Result<(), Status> {
+ todo!()
+ }
+ fn blockingRecvFd(&self) -> Result<ParcelFileDescriptor, Status> {
+ todo!()
+ }
+ fn blockingSendIntOneway(&self, _: i32) -> Result<(), Status> {
+ todo!()
+ }
+ fn blockingRecvInt(&self) -> Result<i32, Status> {
+ todo!()
+ }
+}
+
+wrap_service!(TestRpcServer(RpcServer: UnbufferedService));
+
+service_dispatcher! {
+ enum TestDispatcher {
+ TestRpcServer,
+ }
+}
+
+fn main() {
+ let mut dispatcher = TestDispatcher::<1>::new().expect("Could not create test dispatcher");
+
+ let service = BnBinderRpcTest::new_binder(TestService::default(), BinderFeatures::default());
+ let rpc_server =
+ TestRpcServer::new(RpcServer::new_per_session(move |_uuid| Some(service.as_binder())));
+
+ let cfg = PortCfg::new(RUST_SERVICE_PORT)
+ .expect("Could not create port config")
+ .allow_ta_connect()
+ .allow_ns_connect();
+ dispatcher.add_service(Rc::new(rpc_server), cfg).expect("Could not add service to dispatcher");
+
+ Manager::<_, _, 1, 4>::new_with_dispatcher(dispatcher, [])
+ .expect("Could not create service manager")
+ .run_event_loop()
+ .expect("Test event loop failed");
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json b/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json
new file mode 100644
index 0000000..121ba11
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/service/manifest.json
@@ -0,0 +1,10 @@
+{
+ "uuid": "4741fc65-8b65-4893-ba55-b182c003c8b7",
+ "app_name": "rust_binder_rpc_test_service",
+ "min_heap": 16384,
+ "min_stack": 16384,
+ "mgmt_flags": {
+ "non_critical_app": true,
+ "restart_on_exit": true
+ }
+}
diff --git a/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk b/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk
new file mode 100644
index 0000000..f71ee9b
--- /dev/null
+++ b/libs/binder/trusty/rust/binder_rpc_test/service/rules.mk
@@ -0,0 +1,36 @@
+# 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.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+LIBBINDER_DIR := $(LOCAL_DIR)/../../../..
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := $(LOCAL_DIR)/main.rs
+
+MODULE_CRATE_NAME := binder_rpc_test_service
+
+MODULE_LIBRARY_DEPS += \
+ $(LIBBINDER_DIR)/trusty/rust \
+ $(LIBBINDER_DIR)/trusty/rust/rpcbinder \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_server \
+ $(LOCAL_DIR)/../aidl \
+ $(LOCAL_DIR)/../binder_rpc_test_session \
+ $(LOCAL_DIR)/.. \
+ trusty/user/base/lib/tipc/rust \
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+include make/trusted_app.mk
diff --git a/libs/binder/trusty/rust/rpcbinder/rules.mk b/libs/binder/trusty/rust/rpcbinder/rules.mk
index 76f3b94..97f5c03 100644
--- a/libs/binder/trusty/rust/rpcbinder/rules.mk
+++ b/libs/binder/trusty/rust/rpcbinder/rules.mk
@@ -28,6 +28,8 @@
$(LIBBINDER_DIR)/trusty/rust \
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
+ $(LIBBINDER_DIR)/trusty/rust/binder_rpc_server_bindgen \
+ external/rust/crates/cfg-if \
external/rust/crates/foreign-types \
trusty/user/base/lib/tipc/rust \
trusty/user/base/lib/trusty-sys \
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index d343f14..36bd3a2 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -28,10 +28,12 @@
$(LIBBINDER_DIR)/trusty/rust/binder_ndk_sys \
$(LIBBINDER_DIR)/trusty/rust/binder_rpc_unstable_bindgen \
external/rust/crates/downcast-rs \
+ external/rust/crates/libc \
trusty/user/base/lib/trusty-sys \
MODULE_RUSTFLAGS += \
--cfg 'android_vendor' \
+ --cfg 'trusty' \
# Trusty does not have `ProcessState`, so there are a few
# doc links in `IBinder` that are still broken.
diff --git a/libs/binder/trusty/usertests-inc.mk b/libs/binder/trusty/usertests-inc.mk
index 1300121..833d209 100644
--- a/libs/binder/trusty/usertests-inc.mk
+++ b/libs/binder/trusty/usertests-inc.mk
@@ -16,4 +16,8 @@
TRUSTY_USER_TESTS += \
frameworks/native/libs/binder/trusty/binderRpcTest \
frameworks/native/libs/binder/trusty/binderRpcTest/service \
+ frameworks/native/libs/binder/trusty/rust/binder_rpc_test/service \
+
+TRUSTY_RUST_USER_TESTS += \
+ frameworks/native/libs/binder/trusty/rust/binder_rpc_test \
diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp
index a8f2cbf..19f3aad 100644
--- a/libs/binderdebug/BinderDebug.cpp
+++ b/libs/binderdebug/BinderDebug.cpp
@@ -199,4 +199,31 @@
return ret;
}
+status_t getBinderTransactions(pid_t pid, std::string& transactionsOutput) {
+ std::ifstream ifs("/dev/binderfs/binder_logs/transactions");
+ if (!ifs.is_open()) {
+ ifs.open("/d/binder/transactions");
+ if (!ifs.is_open()) {
+ LOG(ERROR) << "Could not open /dev/binderfs/binder_logs/transactions. "
+ << "Likely a permissions issue. errno: " << errno;
+ return -errno;
+ }
+ }
+
+ std::string line;
+ while (getline(ifs, line)) {
+ // The section for this pid ends with another "proc <pid>" for another
+ // process. There is only one entry per pid so we can stop looking after
+ // we've grabbed the whole section
+ if (base::StartsWith(line, "proc " + std::to_string(pid))) {
+ do {
+ transactionsOutput += line + '\n';
+ } while (getline(ifs, line) && !base::StartsWith(line, "proc "));
+ return OK;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
} // namespace android
diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h
index 6ce8edf..018393c 100644
--- a/libs/binderdebug/include/binderdebug/BinderDebug.h
+++ b/libs/binderdebug/include/binderdebug/BinderDebug.h
@@ -44,4 +44,12 @@
status_t getBinderClientPids(BinderDebugContext context, pid_t pid, pid_t servicePid,
int32_t handle, std::vector<pid_t>* pids);
+/**
+ * Get the transactions for a given process from /dev/binderfs/binder_logs/transactions
+ * Return: OK if the file was found and the pid was found in the file.
+ * -errno if there was an issue opening the file
+ * NAME_NOT_FOUND if the pid wasn't found in the file
+ */
+status_t getBinderTransactions(pid_t pid, std::string& transactionOutput);
+
} // namespace android
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
index b5c4010..e888b0a 100644
--- a/libs/binderthreadstate/test.cpp
+++ b/libs/binderthreadstate/test.cpp
@@ -22,6 +22,7 @@
#include <binderthreadstateutilstest/1.0/IHidlStuff.h>
#include <gtest/gtest.h>
#include <hidl/HidlTransportSupport.h>
+#include <hidl/ServiceManagement.h>
#include <hwbinder/IPCThreadState.h>
#include <thread>
@@ -37,6 +38,7 @@
using android::sp;
using android::String16;
using android::binder::Status;
+using android::hardware::isHidlSupported;
using android::hardware::Return;
using binderthreadstateutilstest::V1_0::IHidlStuff;
@@ -67,6 +69,7 @@
// complicated calls are possible, but this should do here.
static void callHidl(size_t id, int32_t idx) {
+ CHECK_EQ(true, isHidlSupported()) << "We shouldn't be calling HIDL if it's not supported";
auto stuff = IHidlStuff::getService(id2name(id));
CHECK(stuff->call(idx).isOk());
}
@@ -174,6 +177,7 @@
}
TEST(BindThreadState, RemoteHidlCall) {
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
auto stuff = IHidlStuff::getService(id2name(kP1Id));
ASSERT_NE(nullptr, stuff);
ASSERT_TRUE(stuff->call(0).isOk());
@@ -186,11 +190,14 @@
}
TEST(BindThreadState, RemoteNestedStartHidlCall) {
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
auto stuff = IHidlStuff::getService(id2name(kP1Id));
ASSERT_NE(nullptr, stuff);
ASSERT_TRUE(stuff->call(100).isOk());
}
TEST(BindThreadState, RemoteNestedStartAidlCall) {
+ // this test case is trying ot nest a HIDL call which requires HIDL support
+ if (!isHidlSupported()) GTEST_SKIP() << "No HIDL support on device";
sp<IAidlStuff> stuff;
ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
ASSERT_NE(nullptr, stuff);
@@ -205,11 +212,15 @@
defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
android::ProcessState::self()->startThreadPool();
- // HIDL
- android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
- sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
- CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str()));
- android::hardware::joinRpcThreadpool();
+ if (isHidlSupported()) {
+ // HIDL
+ android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
+ sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
+ CHECK_EQ(OK, hidlServer->registerAsService(id2name(thisId).c_str()));
+ android::hardware::joinRpcThreadpool();
+ } else {
+ android::IPCThreadState::self()->joinThreadPool(true);
+ }
return EXIT_FAILURE;
}
@@ -227,9 +238,15 @@
}
android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str()));
- android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str());
+ if (isHidlSupported()) {
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor,
+ id2name(kP1Id).c_str());
+ }
android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str()));
- android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str());
+ if (isHidlSupported()) {
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor,
+ id2name(kP2Id).c_str());
+ }
return RUN_ALL_TESTS();
}
diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp
index 3c8c41f..196161b 100644
--- a/libs/bufferqueueconverter/Android.bp
+++ b/libs/bufferqueueconverter/Android.bp
@@ -17,9 +17,6 @@
cc_library {
name: "libbufferqueueconverter",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
srcs: [
diff --git a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
index 73a15be..82f528e 100644
--- a/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
+++ b/libs/bufferstreams/rust/src/publishers/buffer_pool_publisher.rs
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//!
-
use crate::{
buffers::BufferPool, subscriptions::SharedBufferSubscription, BufferPublisher,
BufferSubscriber, Frame, StreamConfig,
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 6ccc6ca..81f6a58 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -40,6 +40,9 @@
static constexpr uint64_t NSEC_PER_SEC = 1000000000;
static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
+// Declare busy loop variable globally to prevent removal during optimization
+static long sum __attribute__((used)) = 0;
+
using std::vector;
class TimeInStateTest : public testing::Test {
@@ -576,7 +579,7 @@
// Keeps CPU busy with some number crunching
void useCpu() {
- long sum = 0;
+ sum = 0;
for (int i = 0; i < 100000; i++) {
sum *= i;
}
diff --git a/libs/debugstore/OWNERS b/libs/debugstore/OWNERS
new file mode 100644
index 0000000..428a1a2
--- /dev/null
+++ b/libs/debugstore/OWNERS
@@ -0,0 +1,3 @@
+benmiles@google.com
+gaillard@google.com
+mohamadmahmoud@google.com
diff --git a/libs/debugstore/rust/Android.bp b/libs/debugstore/rust/Android.bp
new file mode 100644
index 0000000..55ba3c3
--- /dev/null
+++ b/libs/debugstore/rust/Android.bp
@@ -0,0 +1,71 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_android_telemetry_infra",
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+rust_defaults {
+ name: "libdebugstore_defaults",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libcrossbeam_queue",
+ "libparking_lot",
+ "libonce_cell",
+ "libcxx",
+ ],
+ shared_libs: ["libutils"],
+ edition: "2021",
+}
+
+rust_ffi_static {
+ name: "libdebugstore_rust_ffi",
+ crate_name: "debugstore",
+ defaults: ["libdebugstore_defaults"],
+}
+
+cc_library {
+ name: "libdebugstore_cxx",
+ generated_headers: ["libdebugstore_cxx_bridge_header"],
+ generated_sources: ["libdebugstore_cxx_bridge_code"],
+ export_generated_headers: ["libdebugstore_cxx_bridge_header"],
+ shared_libs: ["libutils"],
+ whole_static_libs: ["libdebugstore_rust_ffi"],
+}
+
+rust_test {
+ name: "libdebugstore_tests",
+ defaults: ["libdebugstore_defaults"],
+ test_options: {
+ unit_test: true,
+ },
+ shared_libs: ["libdebugstore_cxx"],
+}
+
+genrule {
+ name: "libdebugstore_cxx_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["debugstore/debugstore_cxx_bridge.rs.h"],
+}
+
+genrule {
+ name: "libdebugstore_cxx_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["src/lib.rs"],
+ out: ["debugstore/debugstore_cxx_bridge.rs.cpp"],
+}
diff --git a/libs/debugstore/rust/Cargo.toml b/libs/debugstore/rust/Cargo.toml
new file mode 100644
index 0000000..23a8d24
--- /dev/null
+++ b/libs/debugstore/rust/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "debugstore"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib", "rlib"]
+
+[dependencies]
\ No newline at end of file
diff --git a/libs/debugstore/rust/src/core.rs b/libs/debugstore/rust/src/core.rs
new file mode 100644
index 0000000..1dfa512
--- /dev/null
+++ b/libs/debugstore/rust/src/core.rs
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+use super::event::Event;
+use super::event_type::EventType;
+use super::storage::Storage;
+use crate::cxxffi::uptimeMillis;
+use once_cell::sync::Lazy;
+use std::fmt;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+// Lazily initialized static instance of DebugStore.
+static INSTANCE: Lazy<DebugStore> = Lazy::new(DebugStore::new);
+
+/// The `DebugStore` struct is responsible for managing debug events and data.
+pub struct DebugStore {
+ /// Atomic counter for generating unique event IDs.
+ id_generator: AtomicU64,
+ /// Non-blocking storage for debug events.
+ event_store: Storage<Event, { DebugStore::DEFAULT_EVENT_LIMIT }>,
+}
+
+impl DebugStore {
+ /// The default limit for the number of events that can be stored.
+ ///
+ /// This limit is used to initialize the storage for debug events.
+ const DEFAULT_EVENT_LIMIT: usize = 16;
+ /// A designated identifier used for events that cannot be closed.
+ ///
+ /// This ID is used for point/instantaneous events, or events do not have
+ /// a distinct end.
+ const NON_CLOSABLE_ID: u64 = 0;
+ /// The version number for the encoding of debug store data.
+ ///
+ /// This constant is used as a part of the debug store's data format,
+ /// allowing for version tracking and compatibility checks.
+ const ENCODE_VERSION: u32 = 1;
+
+ /// Creates a new instance of `DebugStore` with specified event limit and maximum delay.
+ fn new() -> Self {
+ Self { id_generator: AtomicU64::new(1), event_store: Storage::new() }
+ }
+
+ /// Returns a shared instance of `DebugStore`.
+ ///
+ /// This method provides a singleton pattern access to `DebugStore`.
+ pub fn get_instance() -> &'static DebugStore {
+ &INSTANCE
+ }
+
+ /// Begins a new debug event with the given name and data.
+ ///
+ /// This method logs the start of a debug event, assigning it a unique ID and marking its start time.
+ /// - `name`: The name of the debug event.
+ /// - `data`: Associated data as key-value pairs.
+ /// - Returns: A unique ID for the debug event.
+ pub fn begin(&self, name: String, data: Vec<(String, String)>) -> u64 {
+ let id = self.generate_id();
+ self.event_store.insert(Event::new(
+ id,
+ Some(name),
+ uptimeMillis(),
+ EventType::DurationStart,
+ data,
+ ));
+ id
+ }
+
+ /// Records a debug event without a specific duration, with the given name and data.
+ ///
+ /// This method logs an instantaneous debug event, useful for events that don't have a duration but are significant.
+ /// - `name`: The name of the debug event.
+ /// - `data`: Associated data as key-value pairs.
+ pub fn record(&self, name: String, data: Vec<(String, String)>) {
+ self.event_store.insert(Event::new(
+ Self::NON_CLOSABLE_ID,
+ Some(name),
+ uptimeMillis(),
+ EventType::Point,
+ data,
+ ));
+ }
+
+ /// Ends a debug event that was previously started with the given ID.
+ ///
+ /// This method marks the end of a debug event, completing its lifecycle.
+ /// - `id`: The unique ID of the debug event to end.
+ /// - `data`: Additional data to log at the end of the event.
+ pub fn end(&self, id: u64, data: Vec<(String, String)>) {
+ if id != Self::NON_CLOSABLE_ID {
+ self.event_store.insert(Event::new(
+ id,
+ None,
+ uptimeMillis(),
+ EventType::DurationEnd,
+ data,
+ ));
+ }
+ }
+
+ fn generate_id(&self) -> u64 {
+ let mut id = self.id_generator.fetch_add(1, Ordering::Relaxed);
+ while id == Self::NON_CLOSABLE_ID {
+ id = self.id_generator.fetch_add(1, Ordering::Relaxed);
+ }
+ id
+ }
+}
+
+impl fmt::Display for DebugStore {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let uptime_now = uptimeMillis();
+ write!(f, "{},{},{}::", Self::ENCODE_VERSION, self.event_store.len(), uptime_now)?;
+
+ write!(
+ f,
+ "{}",
+ self.event_store.fold(String::new(), |mut acc, event| {
+ if !acc.is_empty() {
+ acc.push_str("||");
+ }
+ acc.push_str(&event.to_string());
+ acc
+ })
+ )
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_begin_event() {
+ let debug_store = DebugStore::new();
+ let _event_id = debug_store.begin("test_event".to_string(), vec![]);
+ let output = debug_store.to_string();
+ assert!(
+ output.contains("test_event"),
+ "The output should contain the event name 'test_event'"
+ );
+ }
+
+ #[test]
+ fn test_unique_event_ids() {
+ let debug_store = DebugStore::new();
+ let event_id1 = debug_store.begin("event1".to_string(), vec![]);
+ let event_id2 = debug_store.begin("event2".to_string(), vec![]);
+ assert_ne!(event_id1, event_id2, "Event IDs should be unique");
+ }
+
+ #[test]
+ fn test_end_event() {
+ let debug_store = DebugStore::new();
+ let event_id = debug_store.begin("test_event".to_string(), vec![]);
+ debug_store.end(event_id, vec![]);
+ let output = debug_store.to_string();
+
+ let id_pattern = format!("ID:{},", event_id);
+ assert!(
+ output.contains("test_event"),
+ "The output should contain the event name 'test_event'"
+ );
+ assert_eq!(
+ output.matches(&id_pattern).count(),
+ 2,
+ "The output should contain two events (start and end) associated with the given ID"
+ );
+ }
+
+ #[test]
+ fn test_event_data_handling() {
+ let debug_store = DebugStore::new();
+ debug_store
+ .record("data_event".to_string(), vec![("key".to_string(), "value".to_string())]);
+ let output = debug_store.to_string();
+ assert!(
+ output.contains("data_event"),
+ "The output should contain the event name 'data_event'"
+ );
+ assert!(
+ output.contains("key=value"),
+ "The output should contain the event data 'key=value'"
+ );
+ }
+}
diff --git a/libs/debugstore/rust/src/event.rs b/libs/debugstore/rust/src/event.rs
new file mode 100644
index 0000000..0c16665
--- /dev/null
+++ b/libs/debugstore/rust/src/event.rs
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use super::event_type::EventType;
+use std::fmt;
+
+/// Represents a single debug event within the Debug Store system.
+///
+/// It contains all the necessary information for a debug event.
+#[derive(Clone)]
+pub struct Event {
+ /// The unique identifier for this event.
+ pub id: u64,
+ /// The optional name of the event.
+ pub name: Option<String>,
+ /// The system uptime when the event occurred.
+ pub timestamp: i64,
+ /// The type of the event.
+ pub event_type: EventType,
+ /// Additional data associated with the event, stored in the given order as key-value pairs.
+ data: Vec<(String, String)>,
+}
+
+impl Event {
+ /// Constructs a new `Event`.
+ ///
+ /// - `id`: The unique identifier for the event.
+ /// - `name`: An optional name for the event.
+ /// - `timestamp`: The system uptime when the event occurred.
+ /// - `event_type`: The type of the event.
+ /// - `data`: Additional data for the event, represented as ordered key-value pairs.
+ pub fn new(
+ id: u64,
+ name: Option<String>,
+ timestamp: i64,
+ event_type: EventType,
+ data: Vec<(String, String)>,
+ ) -> Self {
+ Self { id, name, timestamp, event_type, data }
+ }
+}
+
+impl fmt::Display for Event {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ID:{},C:{},T:{}", self.id, self.event_type, self.timestamp)?;
+
+ if let Some(ref name) = self.name {
+ write!(f, ",N:{}", name)?;
+ }
+
+ if !self.data.is_empty() {
+ let data_str =
+ self.data.iter().map(|(k, v)| format!("{}={}", k, v)).collect::<Vec<_>>().join(";");
+ write!(f, ",D:{}", data_str)?;
+ }
+
+ Ok(())
+ }
+}
diff --git a/libs/debugstore/rust/src/event_type.rs b/libs/debugstore/rust/src/event_type.rs
new file mode 100644
index 0000000..6f4bafb
--- /dev/null
+++ b/libs/debugstore/rust/src/event_type.rs
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+use std::fmt;
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum EventType {
+ /// Marks the an unknown or invalid event, for convenient mapping to a protobuf enum.
+ Invalid,
+ /// Marks the beginning of a duration-based event, indicating the start of a timed operation.
+ DurationStart,
+ /// Marks the end of a duration-based event, indicating the end of a timed operation.
+ DurationEnd,
+ /// Represents a single, instantaneous event with no duration.
+ Point,
+}
+
+impl fmt::Display for EventType {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{}",
+ match self {
+ EventType::Invalid => "I",
+ EventType::DurationStart => "S",
+ EventType::DurationEnd => "E",
+ EventType::Point => "P",
+ }
+ )
+ }
+}
diff --git a/libs/debugstore/rust/src/lib.rs b/libs/debugstore/rust/src/lib.rs
new file mode 100644
index 0000000..f2195c0
--- /dev/null
+++ b/libs/debugstore/rust/src/lib.rs
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! # Debug Store Crate
+/// The Debug Store Crate provides functionalities for storing debug events.
+/// It allows logging and retrieval of debug events, and associated data.
+mod core;
+mod event;
+mod event_type;
+mod storage;
+
+pub use core::*;
+pub use event::*;
+
+use cxx::{CxxString, CxxVector};
+
+#[cxx::bridge(namespace = "android::debugstore")]
+#[allow(unsafe_op_in_unsafe_fn)]
+mod cxxffi {
+ extern "Rust" {
+ fn debug_store_to_string() -> String;
+ fn debug_store_record(name: &CxxString, data: &CxxVector<CxxString>);
+ fn debug_store_begin(name: &CxxString, data: &CxxVector<CxxString>) -> u64;
+ fn debug_store_end(id: u64, data: &CxxVector<CxxString>);
+ }
+
+ #[namespace = "android"]
+ unsafe extern "C++" {
+ include!("utils/SystemClock.h");
+ fn uptimeMillis() -> i64;
+ }
+}
+
+fn debug_store_to_string() -> String {
+ DebugStore::get_instance().to_string()
+}
+
+fn debug_store_record(name: &CxxString, data: &CxxVector<CxxString>) {
+ DebugStore::get_instance().record(name.to_string_lossy().into_owned(), cxx_vec_to_pairs(data));
+}
+
+fn debug_store_begin(name: &CxxString, data: &CxxVector<CxxString>) -> u64 {
+ DebugStore::get_instance().begin(name.to_string_lossy().into_owned(), cxx_vec_to_pairs(data))
+}
+
+fn debug_store_end(id: u64, data: &CxxVector<CxxString>) {
+ DebugStore::get_instance().end(id, cxx_vec_to_pairs(data));
+}
+
+fn cxx_vec_to_pairs(vec: &CxxVector<CxxString>) -> Vec<(String, String)> {
+ vec.iter()
+ .map(|s| s.to_string_lossy().into_owned())
+ .collect::<Vec<_>>()
+ .chunks(2)
+ .filter_map(|chunk| match chunk {
+ [k, v] => Some((k.clone(), v.clone())),
+ _ => None,
+ })
+ .collect()
+}
diff --git a/libs/debugstore/rust/src/storage.rs b/libs/debugstore/rust/src/storage.rs
new file mode 100644
index 0000000..2ad7f4e
--- /dev/null
+++ b/libs/debugstore/rust/src/storage.rs
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crossbeam_queue::ArrayQueue;
+
+/// A thread-safe storage that allows non-blocking attempts to store and visit elements.
+pub struct Storage<T, const N: usize> {
+ insertion_buffer: ArrayQueue<T>,
+}
+
+impl<T, const N: usize> Storage<T, N> {
+ /// Creates a new Storage with the specified size.
+ pub fn new() -> Self {
+ Self { insertion_buffer: ArrayQueue::new(N) }
+ }
+
+ /// Inserts a value into the storage, returning an error if the lock cannot be acquired.
+ pub fn insert(&self, value: T) {
+ self.insertion_buffer.force_push(value);
+ }
+
+ /// Folds over the elements in the storage using the provided function.
+ pub fn fold<U, F>(&self, init: U, mut func: F) -> U
+ where
+ F: FnMut(U, &T) -> U,
+ {
+ let mut acc = init;
+ while let Some(value) = self.insertion_buffer.pop() {
+ acc = func(acc, &value);
+ }
+ acc
+ }
+
+ /// Returns the number of elements that have been inserted into the storage.
+ pub fn len(&self) -> usize {
+ self.insertion_buffer.len()
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_insert_and_retrieve() {
+ let storage = Storage::<i32, 10>::new();
+ storage.insert(7);
+
+ let sum = storage.fold(0, |acc, &x| acc + x);
+ assert_eq!(sum, 7, "The sum of the elements should be equal to the inserted value.");
+ }
+
+ #[test]
+ fn test_fold_functionality() {
+ let storage = Storage::<i32, 5>::new();
+ storage.insert(1);
+ storage.insert(2);
+ storage.insert(3);
+
+ let sum = storage.fold(0, |acc, &x| acc + x);
+ assert_eq!(
+ sum, 6,
+ "The sum of the elements should be equal to the sum of inserted values."
+ );
+ }
+
+ #[test]
+ fn test_insert_and_retrieve_multiple_values() {
+ let storage = Storage::<i32, 10>::new();
+ storage.insert(1);
+ storage.insert(2);
+ storage.insert(5);
+
+ let first_sum = storage.fold(0, |acc, &x| acc + x);
+ assert_eq!(first_sum, 8, "The sum of the elements should be equal to the inserted values.");
+
+ storage.insert(30);
+ storage.insert(22);
+
+ let second_sum = storage.fold(0, |acc, &x| acc + x);
+ assert_eq!(
+ second_sum, 52,
+ "The sum of the elements should be equal to the inserted values."
+ );
+ }
+
+ #[test]
+ fn test_storage_limit() {
+ let storage = Storage::<i32, 1>::new();
+ storage.insert(1);
+ // This value should overwrite the previously inserted value (1).
+ storage.insert(4);
+ let sum = storage.fold(0, |acc, &x| acc + x);
+ assert_eq!(sum, 4, "The sum of the elements should be equal to the inserted values.");
+ }
+
+ #[test]
+ fn test_concurrent_insertions() {
+ use std::sync::Arc;
+ use std::thread;
+
+ let storage = Arc::new(Storage::<i32, 100>::new());
+ let threads: Vec<_> = (0..10)
+ .map(|_| {
+ let storage_clone = Arc::clone(&storage);
+ thread::spawn(move || {
+ for i in 0..10 {
+ storage_clone.insert(i);
+ }
+ })
+ })
+ .collect();
+
+ for thread in threads {
+ thread.join().expect("Thread should finish without panicking");
+ }
+
+ let count = storage.fold(0, |acc, _| acc + 1);
+ assert_eq!(count, 100, "Storage should be filled to its limit with concurrent insertions.");
+ }
+}
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp
index 32b2b68..4b41152 100644
--- a/libs/ftl/Android.bp
+++ b/libs/ftl/Android.bp
@@ -24,6 +24,7 @@
"flags_test.cpp",
"function_test.cpp",
"future_test.cpp",
+ "hash_test.cpp",
"match_test.cpp",
"mixins_test.cpp",
"non_null_test.cpp",
diff --git a/libs/ftl/algorithm_test.cpp b/libs/ftl/algorithm_test.cpp
index 487b1b8..11569f2 100644
--- a/libs/ftl/algorithm_test.cpp
+++ b/libs/ftl/algorithm_test.cpp
@@ -24,6 +24,17 @@
namespace android::test {
// Keep in sync with example usage in header file.
+TEST(Algorithm, Contains) {
+ const ftl::StaticVector vector = {1, 2, 3};
+ EXPECT_TRUE(ftl::contains(vector, 1));
+
+ EXPECT_FALSE(ftl::contains(vector, 0));
+ EXPECT_TRUE(ftl::contains(vector, 2));
+ EXPECT_TRUE(ftl::contains(vector, 3));
+ EXPECT_FALSE(ftl::contains(vector, 4));
+}
+
+// Keep in sync with example usage in header file.
TEST(Algorithm, FindIf) {
using namespace std::string_view_literals;
diff --git a/libs/ftl/hash_test.cpp b/libs/ftl/hash_test.cpp
new file mode 100644
index 0000000..9c7b8c2
--- /dev/null
+++ b/libs/ftl/hash_test.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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/hash.h>
+#include <gtest/gtest.h>
+
+#include <numeric>
+#include <string>
+
+namespace android::test {
+
+TEST(Hash, StableHash) {
+ EXPECT_EQ(11160318154034397263ull, (ftl::stable_hash({})));
+
+ std::string string(64, '?');
+ std::iota(string.begin(), string.end(), 'A');
+
+ // Maximum length is 64 characters.
+ EXPECT_FALSE(ftl::stable_hash(string + '\n'));
+
+ EXPECT_EQ(6278090252846864564ull, ftl::stable_hash(std::string_view(string).substr(0, 8)));
+ EXPECT_EQ(1883356980931444616ull, ftl::stable_hash(std::string_view(string).substr(0, 16)));
+ EXPECT_EQ(8073093283835059304ull, ftl::stable_hash(std::string_view(string).substr(0, 32)));
+ EXPECT_EQ(18197365392429149980ull, ftl::stable_hash(string));
+}
+
+} // namespace android::test
diff --git a/libs/ftl/non_null_test.cpp b/libs/ftl/non_null_test.cpp
index bd0462b..367b398 100644
--- a/libs/ftl/non_null_test.cpp
+++ b/libs/ftl/non_null_test.cpp
@@ -14,12 +14,17 @@
* limitations under the License.
*/
+#include <ftl/algorithm.h>
#include <ftl/non_null.h>
#include <gtest/gtest.h>
#include <memory>
+#include <set>
#include <string>
#include <string_view>
+#include <type_traits>
+#include <unordered_set>
+#include <vector>
namespace android::test {
namespace {
@@ -47,7 +52,7 @@
// Keep in sync with example usage in header file.
TEST(NonNull, Example) {
const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
- std::size_t size;
+ std::size_t size{};
get_length(string_ptr, ftl::as_non_null(&size));
EXPECT_EQ(size, 7u);
@@ -71,5 +76,84 @@
static_assert(longest(kApplePtr, kOrangePtr) == kOrangePtr);
+static_assert(static_cast<bool>(kApplePtr));
+
+static_assert(std::is_same_v<decltype(ftl::as_non_null(std::declval<const int* const>())),
+ ftl::NonNull<const int*>>);
+
} // namespace
+
+TEST(NonNull, SwapRawPtr) {
+ int i1 = 123;
+ int i2 = 456;
+ auto ptr1 = ftl::as_non_null(&i1);
+ auto ptr2 = ftl::as_non_null(&i2);
+
+ std::swap(ptr1, ptr2);
+
+ EXPECT_EQ(*ptr1, 456);
+ EXPECT_EQ(*ptr2, 123);
+}
+
+TEST(NonNull, SwapSmartPtr) {
+ auto ptr1 = ftl::as_non_null(std::make_shared<int>(123));
+ auto ptr2 = ftl::as_non_null(std::make_shared<int>(456));
+
+ std::swap(ptr1, ptr2);
+
+ EXPECT_EQ(*ptr1, 456);
+ EXPECT_EQ(*ptr2, 123);
+}
+
+TEST(NonNull, VectorOfRawPtr) {
+ int i = 1;
+ std::vector<ftl::NonNull<int*>> vpi;
+ vpi.push_back(ftl::as_non_null(&i));
+ EXPECT_FALSE(ftl::contains(vpi, nullptr));
+ EXPECT_TRUE(ftl::contains(vpi, &i));
+ EXPECT_TRUE(ftl::contains(vpi, vpi.front()));
+}
+
+TEST(NonNull, VectorOfSmartPtr) {
+ std::vector<ftl::NonNull<std::shared_ptr<int>>> vpi;
+ vpi.push_back(ftl::as_non_null(std::make_shared<int>(2)));
+ EXPECT_FALSE(ftl::contains(vpi, nullptr));
+ EXPECT_TRUE(ftl::contains(vpi, vpi.front().get()));
+ EXPECT_TRUE(ftl::contains(vpi, vpi.front()));
+}
+
+TEST(NonNull, SetOfRawPtr) {
+ int i = 1;
+ std::set<ftl::NonNull<int*>> spi;
+ spi.insert(ftl::as_non_null(&i));
+ EXPECT_FALSE(ftl::contains(spi, nullptr));
+ EXPECT_TRUE(ftl::contains(spi, &i));
+ EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
+}
+
+TEST(NonNull, SetOfSmartPtr) {
+ std::set<ftl::NonNull<std::shared_ptr<int>>> spi;
+ spi.insert(ftl::as_non_null(std::make_shared<int>(2)));
+ EXPECT_FALSE(ftl::contains(spi, nullptr));
+ EXPECT_TRUE(ftl::contains(spi, spi.begin()->get()));
+ EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
+}
+
+TEST(NonNull, UnorderedSetOfRawPtr) {
+ int i = 1;
+ std::unordered_set<ftl::NonNull<int*>> spi;
+ spi.insert(ftl::as_non_null(&i));
+ EXPECT_FALSE(ftl::contains(spi, nullptr));
+ EXPECT_TRUE(ftl::contains(spi, &i));
+ EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
+}
+
+TEST(NonNull, UnorderedSetOfSmartPtr) {
+ std::unordered_set<ftl::NonNull<std::shared_ptr<int>>> spi;
+ spi.insert(ftl::as_non_null(std::make_shared<int>(2)));
+ EXPECT_FALSE(ftl::contains(spi, nullptr));
+ EXPECT_TRUE(ftl::contains(spi, spi.begin()->get()));
+ EXPECT_TRUE(ftl::contains(spi, *spi.begin()));
+}
+
} // namespace android::test
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index f300da5..8dabc2c 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -38,10 +38,7 @@
},
vendor_available: true,
- vndk: {
- enabled: true,
- support_system_process: true,
- },
+ double_loadable: true,
apex_available: [
"//apex_available:platform",
"com.android.media.swcodec",
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index 7b74214..33cebe3 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -96,6 +96,7 @@
if ((status = parcel->writeUint64(vulkanDeviceFeaturesEnabled)) != OK) return status;
if ((status = parcel->writeInt32Vector(vulkanInstanceExtensions)) != OK) return status;
if ((status = parcel->writeInt32Vector(vulkanDeviceExtensions)) != OK) return status;
+ if ((status = parcel->writeUtf8VectorAsUtf16Vector(vulkanEngineNames)) != OK) return status;
return OK;
}
@@ -118,6 +119,7 @@
if ((status = parcel->readUint64(&vulkanDeviceFeaturesEnabled)) != OK) return status;
if ((status = parcel->readInt32Vector(&vulkanInstanceExtensions)) != OK) return status;
if ((status = parcel->readInt32Vector(&vulkanDeviceExtensions)) != OK) return status;
+ if ((status = parcel->readUtf8VectorFromUtf16Vector(&vulkanEngineNames)) != OK) return status;
return OK;
}
@@ -161,6 +163,11 @@
StringAppendF(&result, " 0x%x", extension);
}
result.append("\n");
+ result.append("vulkanEngineNames:");
+ for (const std::string& engineName : vulkanEngineNames) {
+ StringAppendF(&result, " %s,", engineName.c_str());
+ }
+ result.append("\n");
return result;
}
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index 50c05f4..52383ac 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -445,6 +445,21 @@
extensionHashes, numStats);
}
+void GraphicsEnv::addVulkanEngineName(const char* engineName) {
+ ATRACE_CALL();
+ if (engineName == nullptr) {
+ return;
+ }
+ std::lock_guard<std::mutex> lock(mStatsLock);
+ if (!readyToSendGpuStatsLocked()) return;
+
+ const sp<IGpuService> gpuService = getGpuService();
+ if (gpuService) {
+ gpuService->addVulkanEngineName(mGpuStats.appPackageName, mGpuStats.driverVersionCode,
+ engineName);
+ }
+}
+
bool GraphicsEnv::readyToSendGpuStatsLocked() {
// Only send stats for processes having at least one activity launched and that process doesn't
// skip the GraphicsEnvironment setup.
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 5dc195c..42e7c37 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -77,6 +77,19 @@
IBinder::FLAG_ONEWAY);
}
+ void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const char* engineName) override {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
+
+ data.writeUtf8AsUtf16(appPackageName);
+ data.writeUint64(driverVersionCode);
+ data.writeCString(engineName);
+
+ remote()->transact(BnGpuService::ADD_VULKAN_ENGINE_NAME, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ }
+
void setUpdatableDriverPath(const std::string& driverPath) override {
Parcel data, reply;
data.writeInterfaceToken(IGpuService::getInterfaceDescriptor());
@@ -197,6 +210,21 @@
return OK;
}
+ case ADD_VULKAN_ENGINE_NAME: {
+ CHECK_INTERFACE(IGpuService, data, reply);
+
+ std::string appPackageName;
+ if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status;
+
+ uint64_t driverVersionCode;
+ if ((status = data.readUint64(&driverVersionCode)) != OK) return status;
+
+ const char* engineName;
+ if ((engineName = data.readCString()) == nullptr) return BAD_VALUE;
+
+ addVulkanEngineName(appPackageName, driverVersionCode, engineName);
+ return OK;
+ }
case SET_UPDATABLE_DRIVER_PATH: {
CHECK_INTERFACE(IGpuService, data, reply);
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 9ebaf16..23f583b 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -60,6 +60,10 @@
public:
// This limits the worst case number of extensions to be tracked.
static const uint32_t MAX_NUM_EXTENSIONS = 100;
+ // Max number of vulkan engine names for a single GpuStatsAppInfo
+ static const uint32_t MAX_VULKAN_ENGINE_NAMES = 16;
+ // Max length of a vulkan engine name string
+ static const size_t MAX_VULKAN_ENGINE_NAME_LENGTH = 50;
GpuStatsAppInfo() = default;
GpuStatsAppInfo(const GpuStatsAppInfo&) = default;
@@ -84,6 +88,7 @@
uint64_t vulkanDeviceFeaturesEnabled = 0;
std::vector<int32_t> vulkanInstanceExtensions = {};
std::vector<int32_t> vulkanDeviceExtensions = {};
+ std::vector<std::string> vulkanEngineNames = {};
std::chrono::time_point<std::chrono::system_clock> lastAccessTime;
};
diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
index 6cce3f6..b0ab0b9 100644
--- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
+++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h
@@ -89,6 +89,8 @@
// Set which device extensions are enabled for the app.
void setVulkanDeviceExtensions(uint32_t enabledExtensionCount,
const char* const* ppEnabledExtensionNames);
+ // Add the engine name passed in VkApplicationInfo during CreateInstance
+ void addVulkanEngineName(const char* engineName);
/*
* Api for Vk/GL layer injection. Presently, drivers enable certain
diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h
index 45f05d6..a0d6e37 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -46,6 +46,8 @@
const uint64_t driverVersionCode,
const GpuStatsInfo::Stats stats, const uint64_t* values,
const uint32_t valueCount) = 0;
+ virtual void addVulkanEngineName(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const char* engineName) = 0;
// setter and getter for updatable driver path.
virtual void setUpdatableDriverPath(const std::string& driverPath) = 0;
@@ -64,6 +66,7 @@
GET_UPDATABLE_DRIVER_PATH,
TOGGLE_ANGLE_AS_SYSTEM_DRIVER,
SET_TARGET_STATS_ARRAY,
+ ADD_VULKAN_ENGINE_NAME,
// Always append new enum to the end.
};
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 70cb36b..6c45746 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -155,9 +155,9 @@
},
}
-aidl_library {
- name: "libgui_aidl_hdrs",
- hdrs: [
+filegroup {
+ name: "libgui_extra_aidl_files",
+ srcs: [
"android/gui/DisplayInfo.aidl",
"android/gui/FocusRequest.aidl",
"android/gui/InputApplicationInfo.aidl",
@@ -170,11 +170,34 @@
],
}
+filegroup {
+ name: "libgui_extra_unstructured_aidl_files",
+ srcs: [
+ "android/gui/DisplayInfo.aidl",
+ "android/gui/InputApplicationInfo.aidl",
+ "android/gui/WindowInfo.aidl",
+ "android/gui/WindowInfosUpdate.aidl",
+ ],
+}
+
+aidl_library {
+ name: "libgui_aidl_hdrs",
+ hdrs: [":libgui_extra_aidl_files"],
+}
+
+aidl_library {
+ name: "libgui_extra_unstructured_aidl_hdrs",
+ hdrs: [":libgui_extra_unstructured_aidl_files"],
+}
+
aidl_library {
name: "libgui_aidl",
srcs: ["aidl/**/*.aidl"],
strip_import_prefix: "aidl",
- deps: ["libgui_aidl_hdrs"],
+ deps: [
+ "libgui_aidl_hdrs",
+ "libgui_extra_unstructured_aidl_hdrs",
+ ],
}
filegroup {
@@ -241,7 +264,6 @@
"IProducerListener.cpp",
"ISurfaceComposer.cpp",
"ITransactionCompletedListener.cpp",
- "LayerDebugInfo.cpp",
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
@@ -283,10 +305,6 @@
cc_library_shared {
name: "libgui",
vendor_available: true,
- vndk: {
- enabled: true,
- private: true,
- },
double_loadable: true,
defaults: [
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index fb69fda..69345a9 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -423,6 +423,11 @@
sp<IConsumerListener> listener;
bool callOnFrameDequeued = false;
uint64_t bufferId = 0; // Only used if callOnFrameDequeued == true
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ std::vector<gui::AdditionalOptions> allocOptions;
+ uint32_t allocOptionsGenId = 0;
+#endif
+
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
@@ -486,11 +491,17 @@
}
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
- if (mCore->mSharedBufferSlot == found &&
- buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
- BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
- "buffer");
+ bool needsReallocation = buffer == nullptr ||
+ buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage);
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ needsReallocation |= mSlots[found].mAdditionalOptionsGenerationId !=
+ mCore->mAdditionalOptionsGenerationId;
+#endif
+
+ if (mCore->mSharedBufferSlot == found && needsReallocation) {
+ BQ_LOGE("dequeueBuffer: cannot re-allocate a shared buffer");
return BAD_VALUE;
}
@@ -505,9 +516,7 @@
mSlots[found].mBufferState.dequeue();
- if ((buffer == nullptr) ||
- buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
- {
+ if (needsReallocation) {
if (CC_UNLIKELY(ATRACE_ENABLED())) {
if (buffer == nullptr) {
ATRACE_FORMAT_INSTANT("%s buffer reallocation: null", mConsumerName.c_str());
@@ -530,6 +539,10 @@
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
mCore->mIsAllocating = true;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ allocOptions = mCore->mAdditionalOptions;
+ allocOptionsGenId = mCore->mAdditionalOptionsGenerationId;
+#endif
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
@@ -575,9 +588,29 @@
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ std::vector<GraphicBufferAllocator::AdditionalOptions> tempOptions;
+ tempOptions.reserve(allocOptions.size());
+ for (const auto& it : allocOptions) {
+ tempOptions.emplace_back(it.name.c_str(), it.value);
+ }
+ const GraphicBufferAllocator::AllocationRequest allocRequest = {
+ .importBuffer = true,
+ .width = width,
+ .height = height,
+ .format = format,
+ .layerCount = BQ_LAYER_COUNT,
+ .usage = usage,
+ .requestorName = {mConsumerName.c_str(), mConsumerName.size()},
+ .extras = std::move(tempOptions),
+ };
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+#else
sp<GraphicBuffer> graphicBuffer =
new GraphicBuffer(width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.c_str(), mConsumerName.size()});
+#endif
status_t error = graphicBuffer->initCheck();
@@ -587,6 +620,9 @@
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ mSlots[*outSlot].mAdditionalOptionsGenerationId = allocOptionsGenId;
+#endif
callOnFrameDequeued = true;
bufferId = mSlots[*outSlot].mGraphicBuffer->getId();
}
@@ -1342,6 +1378,9 @@
}
mCore->mAllowAllocation = true;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ mCore->mAdditionalOptions.clear();
+#endif
VALIDATE_CONSISTENCY();
return status;
}
@@ -1410,6 +1449,9 @@
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.notify_all();
mCore->mAutoPrerotation = false;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ mCore->mAdditionalOptions.clear();
+#endif
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
BQ_LOGE("disconnect: not connected (req=%d)", api);
@@ -1462,6 +1504,10 @@
PixelFormat allocFormat = PIXEL_FORMAT_UNKNOWN;
uint64_t allocUsage = 0;
std::string allocName;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ std::vector<gui::AdditionalOptions> allocOptions;
+ uint32_t allocOptionsGenId = 0;
+#endif
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked(lock);
@@ -1490,14 +1536,42 @@
allocUsage = usage | mCore->mConsumerUsageBits;
allocName.assign(mCore->mConsumerName.c_str(), mCore->mConsumerName.size());
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ allocOptions = mCore->mAdditionalOptions;
+ allocOptionsGenId = mCore->mAdditionalOptionsGenerationId;
+#endif
+
mCore->mIsAllocating = true;
+
} // Autolock scope
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ std::vector<GraphicBufferAllocator::AdditionalOptions> tempOptions;
+ tempOptions.reserve(allocOptions.size());
+ for (const auto& it : allocOptions) {
+ tempOptions.emplace_back(it.name.c_str(), it.value);
+ }
+ const GraphicBufferAllocator::AllocationRequest allocRequest = {
+ .importBuffer = true,
+ .width = allocWidth,
+ .height = allocHeight,
+ .format = allocFormat,
+ .layerCount = BQ_LAYER_COUNT,
+ .usage = allocUsage,
+ .requestorName = allocName,
+ .extras = std::move(tempOptions),
+ };
+#endif
+
Vector<sp<GraphicBuffer>> buffers;
for (size_t i = 0; i < newBufferCount; ++i) {
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(allocRequest);
+#else
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
allocUsage, allocName);
+#endif
status_t result = graphicBuffer->initCheck();
@@ -1524,8 +1598,12 @@
PixelFormat checkFormat = format != 0 ?
format : mCore->mDefaultBufferFormat;
uint64_t checkUsage = usage | mCore->mConsumerUsageBits;
+ bool allocOptionsChanged = false;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ allocOptionsChanged = allocOptionsGenId != mCore->mAdditionalOptionsGenerationId;
+#endif
if (checkWidth != allocWidth || checkHeight != allocHeight ||
- checkFormat != allocFormat || checkUsage != allocUsage) {
+ checkFormat != allocFormat || checkUsage != allocUsage || allocOptionsChanged) {
// Something changed while we released the lock. Retry.
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
mCore->mIsAllocating = false;
@@ -1543,6 +1621,9 @@
mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
mSlots[*slot].mGraphicBuffer = buffers[i];
mSlots[*slot].mFence = Fence::NO_FENCE;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ mSlots[*slot].mAdditionalOptionsGenerationId = allocOptionsGenId;
+#endif
// freeBufferLocked puts this slot on the free slots list. Since
// we then attached a buffer, move the slot to free buffer list.
@@ -1778,4 +1859,29 @@
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+status_t BufferQueueProducer::setAdditionalOptions(
+ const std::vector<gui::AdditionalOptions>& options) {
+ ATRACE_CALL();
+ BQ_LOGV("setAdditionalOptions, size = %zu", options.size());
+
+ if (!GraphicBufferAllocator::get().supportsAdditionalOptions()) {
+ return INVALID_OPERATION;
+ }
+
+ std::lock_guard<std::mutex> lock(mCore->mMutex);
+
+ if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
+ BQ_LOGE("setAdditionalOptions: BufferQueue not connected, cannot set additional options");
+ return NO_INIT;
+ }
+
+ if (mCore->mAdditionalOptions != options) {
+ mCore->mAdditionalOptions = options;
+ mCore->mAdditionalOptionsGenerationId++;
+ }
+ return NO_ERROR;
+}
+#endif
+
} // namespace android
diff --git a/libs/gui/DisplayInfo.cpp b/libs/gui/DisplayInfo.cpp
index bd640df..47cec07 100644
--- a/libs/gui/DisplayInfo.cpp
+++ b/libs/gui/DisplayInfo.cpp
@@ -37,8 +37,9 @@
return BAD_VALUE;
}
+ int32_t displayIdInt;
float dsdx, dtdx, tx, dtdy, dsdy, ty;
- SAFE_PARCEL(parcel->readInt32, &displayId);
+ SAFE_PARCEL(parcel->readInt32, &displayIdInt);
SAFE_PARCEL(parcel->readInt32, &logicalWidth);
SAFE_PARCEL(parcel->readInt32, &logicalHeight);
SAFE_PARCEL(parcel->readFloat, &dsdx);
@@ -48,6 +49,7 @@
SAFE_PARCEL(parcel->readFloat, &dsdy);
SAFE_PARCEL(parcel->readFloat, &ty);
+ displayId = ui::LogicalDisplayId{displayIdInt};
transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1});
return OK;
@@ -59,7 +61,7 @@
return BAD_VALUE;
}
- SAFE_PARCEL(parcel->writeInt32, displayId);
+ SAFE_PARCEL(parcel->writeInt32, displayId.val());
SAFE_PARCEL(parcel->writeInt32, logicalWidth);
SAFE_PARCEL(parcel->writeInt32, logicalHeight);
SAFE_PARCEL(parcel->writeFloat, transform.dsdx());
@@ -76,7 +78,7 @@
using android::base::StringAppendF;
out += prefix;
- StringAppendF(&out, "DisplayViewport[id=%" PRId32 "]\n", displayId);
+ StringAppendF(&out, "DisplayViewport[id=%s]\n", displayId.toString().c_str());
out += prefix;
StringAppendF(&out, INDENT "Width=%" PRId32 ", Height=%" PRId32 "\n", logicalWidth,
logicalHeight);
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index e81c098..0914480 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -80,6 +80,7 @@
QUERY_MULTIPLE,
GET_LAST_QUEUED_BUFFER2,
SET_FRAME_RATE,
+ SET_ADDITIONAL_OPTIONS,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -778,6 +779,25 @@
return result;
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ virtual status_t setAdditionalOptions(const std::vector<gui::AdditionalOptions>& options) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
+ if (options.size() > 100) {
+ return BAD_VALUE;
+ }
+ data.writeInt32(options.size());
+ for (const auto& it : options) {
+ data.writeCString(it.name.c_str());
+ data.writeInt64(it.value);
+ }
+ status_t result = remote()->transact(SET_ADDITIONAL_OPTIONS, data, &reply);
+ if (result == NO_ERROR) {
+ result = reply.readInt32();
+ }
+ return result;
+ }
+#endif
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -981,6 +1001,13 @@
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+status_t IGraphicBufferProducer::setAdditionalOptions(const std::vector<gui::AdditionalOptions>&) {
+ // No-op for IGBP other than BufferQueue.
+ return INVALID_OPERATION;
+}
+#endif
+
status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
status_t res = OK;
res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -1533,6 +1560,28 @@
return NO_ERROR;
}
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ case SET_ADDITIONAL_OPTIONS: {
+ CHECK_INTERFACE(IGraphicBuffer, data, reply);
+ int optionCount = data.readInt32();
+ if (optionCount < 0 || optionCount > 100) {
+ return BAD_VALUE;
+ }
+ std::vector<gui::AdditionalOptions> opts;
+ opts.reserve(optionCount);
+ for (int i = 0; i < optionCount; i++) {
+ const char* name = data.readCString();
+ int64_t value = 0;
+ if (name == nullptr || data.readInt64(&value) != NO_ERROR) {
+ return BAD_VALUE;
+ }
+ opts.emplace_back(name, value);
+ }
+ status_t result = setAdditionalOptions(opts);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
+#endif
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/LayerDebugInfo.cpp b/libs/gui/LayerDebugInfo.cpp
deleted file mode 100644
index 15b2221..0000000
--- a/libs/gui/LayerDebugInfo.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/LayerDebugInfo.h>
-
-#include <android-base/stringprintf.h>
-
-#include <ui/DebugUtils.h>
-
-#include <binder/Parcel.h>
-
-using namespace android;
-using android::base::StringAppendF;
-
-#define RETURN_ON_ERROR(X) do {status_t res = (X); if (res != NO_ERROR) return res;} while(false)
-
-namespace android::gui {
-
-status_t LayerDebugInfo::writeToParcel(Parcel* parcel) const {
- RETURN_ON_ERROR(parcel->writeCString(mName.c_str()));
- RETURN_ON_ERROR(parcel->writeCString(mParentName.c_str()));
- RETURN_ON_ERROR(parcel->writeCString(mType.c_str()));
- RETURN_ON_ERROR(parcel->write(mTransparentRegion));
- RETURN_ON_ERROR(parcel->write(mVisibleRegion));
- RETURN_ON_ERROR(parcel->write(mSurfaceDamageRegion));
- RETURN_ON_ERROR(parcel->writeUint32(mLayerStack));
- RETURN_ON_ERROR(parcel->writeFloat(mX));
- RETURN_ON_ERROR(parcel->writeFloat(mY));
- RETURN_ON_ERROR(parcel->writeUint32(mZ));
- RETURN_ON_ERROR(parcel->writeInt32(mWidth));
- RETURN_ON_ERROR(parcel->writeInt32(mHeight));
- RETURN_ON_ERROR(parcel->write(mCrop));
- RETURN_ON_ERROR(parcel->writeFloat(mColor.r));
- RETURN_ON_ERROR(parcel->writeFloat(mColor.g));
- RETURN_ON_ERROR(parcel->writeFloat(mColor.b));
- RETURN_ON_ERROR(parcel->writeFloat(mColor.a));
- RETURN_ON_ERROR(parcel->writeUint32(mFlags));
- RETURN_ON_ERROR(parcel->writeInt32(mPixelFormat));
- RETURN_ON_ERROR(parcel->writeUint32(static_cast<uint32_t>(mDataSpace)));
- for (size_t index = 0; index < 4; index++) {
- RETURN_ON_ERROR(parcel->writeFloat(mMatrix[index / 2][index % 2]));
- }
- RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferWidth));
- RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferHeight));
- RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferStride));
- RETURN_ON_ERROR(parcel->writeInt32(mActiveBufferFormat));
- RETURN_ON_ERROR(parcel->writeInt32(mNumQueuedFrames));
- RETURN_ON_ERROR(parcel->writeBool(mIsOpaque));
- RETURN_ON_ERROR(parcel->writeBool(mContentDirty));
- RETURN_ON_ERROR(parcel->write(mStretchEffect));
- return NO_ERROR;
-}
-
-status_t LayerDebugInfo::readFromParcel(const Parcel* parcel) {
- mName = parcel->readCString();
- RETURN_ON_ERROR(parcel->errorCheck());
- mParentName = parcel->readCString();
- RETURN_ON_ERROR(parcel->errorCheck());
- mType = parcel->readCString();
- RETURN_ON_ERROR(parcel->errorCheck());
- RETURN_ON_ERROR(parcel->read(mTransparentRegion));
- RETURN_ON_ERROR(parcel->read(mVisibleRegion));
- RETURN_ON_ERROR(parcel->read(mSurfaceDamageRegion));
- RETURN_ON_ERROR(parcel->readUint32(&mLayerStack));
- RETURN_ON_ERROR(parcel->readFloat(&mX));
- RETURN_ON_ERROR(parcel->readFloat(&mY));
- RETURN_ON_ERROR(parcel->readUint32(&mZ));
- RETURN_ON_ERROR(parcel->readInt32(&mWidth));
- RETURN_ON_ERROR(parcel->readInt32(&mHeight));
- RETURN_ON_ERROR(parcel->read(mCrop));
- mColor.r = parcel->readFloat();
- RETURN_ON_ERROR(parcel->errorCheck());
- mColor.g = parcel->readFloat();
- RETURN_ON_ERROR(parcel->errorCheck());
- mColor.b = parcel->readFloat();
- RETURN_ON_ERROR(parcel->errorCheck());
- mColor.a = parcel->readFloat();
- RETURN_ON_ERROR(parcel->errorCheck());
- RETURN_ON_ERROR(parcel->readUint32(&mFlags));
- RETURN_ON_ERROR(parcel->readInt32(&mPixelFormat));
- // \todo [2017-07-25 kraita]: Static casting mDataSpace pointer to an uint32 does work. Better ways?
- mDataSpace = static_cast<android_dataspace>(parcel->readUint32());
- RETURN_ON_ERROR(parcel->errorCheck());
- for (size_t index = 0; index < 4; index++) {
- RETURN_ON_ERROR(parcel->readFloat(&mMatrix[index / 2][index % 2]));
- }
- RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferWidth));
- RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferHeight));
- RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferStride));
- RETURN_ON_ERROR(parcel->readInt32(&mActiveBufferFormat));
- RETURN_ON_ERROR(parcel->readInt32(&mNumQueuedFrames));
- RETURN_ON_ERROR(parcel->readBool(&mIsOpaque));
- RETURN_ON_ERROR(parcel->readBool(&mContentDirty));
- RETURN_ON_ERROR(parcel->read(mStretchEffect));
- return NO_ERROR;
-}
-
-std::string to_string(const LayerDebugInfo& info) {
- std::string result;
-
- StringAppendF(&result, "+ %s (%s)\n", info.mType.c_str(), info.mName.c_str());
- info.mTransparentRegion.dump(result, "TransparentRegion");
- info.mVisibleRegion.dump(result, "VisibleRegion");
- info.mSurfaceDamageRegion.dump(result, "SurfaceDamageRegion");
- if (info.mStretchEffect.hasEffect()) {
- const auto& se = info.mStretchEffect;
- StringAppendF(&result,
- " StretchEffect width = %f, height = %f vec=(%f, %f) "
- "maxAmount=(%f, %f)\n",
- se.width, se.height,
- se.vectorX, se.vectorY, se.maxAmountX, se.maxAmountY);
- }
-
- StringAppendF(&result, " layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
- info.mLayerStack, info.mZ, static_cast<double>(info.mX),
- static_cast<double>(info.mY), info.mWidth, info.mHeight);
-
- StringAppendF(&result, "crop=%s, ", to_string(info.mCrop).c_str());
- StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", info.mIsOpaque, info.mContentDirty);
- StringAppendF(&result, "dataspace=%s, ", dataspaceDetails(info.mDataSpace).c_str());
- StringAppendF(&result, "pixelformat=%s, ", decodePixelFormat(info.mPixelFormat).c_str());
- StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
- static_cast<double>(info.mColor.r), static_cast<double>(info.mColor.g),
- static_cast<double>(info.mColor.b), static_cast<double>(info.mColor.a),
- info.mFlags);
- StringAppendF(&result, "tr=[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(info.mMatrix[0][0]),
- static_cast<double>(info.mMatrix[0][1]), static_cast<double>(info.mMatrix[1][0]),
- static_cast<double>(info.mMatrix[1][1]));
- result.append("\n");
- StringAppendF(&result, " parent=%s\n", info.mParentName.c_str());
- StringAppendF(&result, " activeBuffer=[%4ux%4u:%4u,%s],", info.mActiveBufferWidth,
- info.mActiveBufferHeight, info.mActiveBufferStride,
- decodePixelFormat(info.mActiveBufferFormat).c_str());
- StringAppendF(&result, " queued-frames=%d", info.mNumQueuedFrames);
- result.append("\n");
- return result;
-}
-
-} // namespace android::gui
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 1e0aacd..0a28799 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -90,7 +90,6 @@
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
isTrustedOverlay(false),
- borderEnabled(false),
bufferCrop(Rect::INVALID_RECT),
destinationFrame(Rect::INVALID_RECT),
dropInputMode(gui::DropInputMode::NONE) {
@@ -122,12 +121,6 @@
SAFE_PARCEL(output.write, transparentRegion);
SAFE_PARCEL(output.writeUint32, bufferTransform);
SAFE_PARCEL(output.writeBool, transformToDisplayInverse);
- SAFE_PARCEL(output.writeBool, borderEnabled);
- SAFE_PARCEL(output.writeFloat, borderWidth);
- SAFE_PARCEL(output.writeFloat, borderColor.r);
- SAFE_PARCEL(output.writeFloat, borderColor.g);
- SAFE_PARCEL(output.writeFloat, borderColor.b);
- SAFE_PARCEL(output.writeFloat, borderColor.a);
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dataspace));
SAFE_PARCEL(output.write, hdrMetadata);
SAFE_PARCEL(output.write, surfaceDamageRegion);
@@ -238,17 +231,6 @@
SAFE_PARCEL(input.read, transparentRegion);
SAFE_PARCEL(input.readUint32, &bufferTransform);
SAFE_PARCEL(input.readBool, &transformToDisplayInverse);
- SAFE_PARCEL(input.readBool, &borderEnabled);
- SAFE_PARCEL(input.readFloat, &tmpFloat);
- borderWidth = tmpFloat;
- SAFE_PARCEL(input.readFloat, &tmpFloat);
- borderColor.r = tmpFloat;
- SAFE_PARCEL(input.readFloat, &tmpFloat);
- borderColor.g = tmpFloat;
- SAFE_PARCEL(input.readFloat, &tmpFloat);
- borderColor.b = tmpFloat;
- SAFE_PARCEL(input.readFloat, &tmpFloat);
- borderColor.a = tmpFloat;
uint32_t tmpUint32 = 0;
SAFE_PARCEL(input.readUint32, &tmpUint32);
@@ -659,12 +641,6 @@
what |= eShadowRadiusChanged;
shadowRadius = other.shadowRadius;
}
- if (other.what & eRenderBorderChanged) {
- what |= eRenderBorderChanged;
- borderEnabled = other.borderEnabled;
- borderWidth = other.borderWidth;
- borderColor = other.borderColor;
- }
if (other.what & eDefaultFrameRateCompatibilityChanged) {
what |= eDefaultFrameRateCompatibilityChanged;
defaultFrameRateCompatibility = other.defaultFrameRateCompatibility;
@@ -794,7 +770,6 @@
CHECK_DIFF2(diff, eBackgroundColorChanged, other, bgColor, bgColorDataspace);
if (other.what & eMetadataChanged) diff |= eMetadataChanged;
CHECK_DIFF(diff, eShadowRadiusChanged, other, shadowRadius);
- CHECK_DIFF3(diff, eRenderBorderChanged, other, borderEnabled, borderWidth, borderColor);
CHECK_DIFF(diff, eDefaultFrameRateCompatibilityChanged, other, defaultFrameRateCompatibility);
CHECK_DIFF(diff, eFrameRateSelectionPriority, other, frameRateSelectionPriority);
CHECK_DIFF3(diff, eFrameRateChanged, other, frameRate, frameRateCompatibility,
diff --git a/libs/gui/LayerStatePermissions.cpp b/libs/gui/LayerStatePermissions.cpp
index 28697ca..c467cfd 100644
--- a/libs/gui/LayerStatePermissions.cpp
+++ b/libs/gui/LayerStatePermissions.cpp
@@ -23,31 +23,31 @@
#include <gui/LayerState.h>
namespace android {
-std::unordered_map<std::string, int> LayerStatePermissions::mPermissionMap = {
+std::vector<std::pair<String16, int>> LayerStatePermissions::mPermissionMap = {
// If caller has ACCESS_SURFACE_FLINGER, they automatically get ROTATE_SURFACE_FLINGER
// permission, as well
- {"android.permission.ACCESS_SURFACE_FLINGER",
+ {String16("android.permission.ACCESS_SURFACE_FLINGER"),
layer_state_t::Permission::ACCESS_SURFACE_FLINGER |
layer_state_t::Permission::ROTATE_SURFACE_FLINGER},
- {"android.permission.ROTATE_SURFACE_FLINGER",
+ {String16("android.permission.ROTATE_SURFACE_FLINGER"),
layer_state_t::Permission::ROTATE_SURFACE_FLINGER},
- {"android.permission.INTERNAL_SYSTEM_WINDOW",
+ {String16("android.permission.INTERNAL_SYSTEM_WINDOW"),
layer_state_t::Permission::INTERNAL_SYSTEM_WINDOW},
};
-static bool callingThreadHasPermission(const std::string& permission __attribute__((unused)),
+static bool callingThreadHasPermission(const String16& permission __attribute__((unused)),
int pid __attribute__((unused)),
int uid __attribute__((unused))) {
#ifndef __ANDROID_VNDK__
return uid == AID_GRAPHICS || uid == AID_SYSTEM ||
- PermissionCache::checkPermission(String16(permission.c_str()), pid, uid);
+ PermissionCache::checkPermission(permission, pid, uid);
#endif // __ANDROID_VNDK__
return false;
}
uint32_t LayerStatePermissions::getTransactionPermissions(int pid, int uid) {
uint32_t permissions = 0;
- for (auto [permissionName, permissionVal] : mPermissionMap) {
+ for (const auto& [permissionName, permissionVal] : mPermissionMap) {
if (callingThreadHasPermission(permissionName, pid, uid)) {
permissions |= permissionVal;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 086544e..87fd448 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1475,6 +1475,9 @@
case NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO:
res = dispatchSetFrameTimelineInfo(args);
break;
+ case NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS:
+ res = dispatchSetAdditionalOptions(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -1833,6 +1836,24 @@
return setFrameTimelineInfo(nativeWindowFtlInfo.frameNumber, ftlInfo);
}
+int Surface::dispatchSetAdditionalOptions(va_list args) {
+ ATRACE_CALL();
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ const AHardwareBufferLongOptions* opts = va_arg(args, const AHardwareBufferLongOptions*);
+ const size_t optsSize = va_arg(args, size_t);
+ std::vector<gui::AdditionalOptions> convertedOpts;
+ convertedOpts.reserve(optsSize);
+ for (size_t i = 0; i < optsSize; i++) {
+ convertedOpts.emplace_back(opts[i].name, opts[i].value);
+ }
+ return setAdditionalOptions(convertedOpts);
+#else
+ (void)args;
+ return INVALID_OPERATION;
+#endif
+}
+
bool Surface::transformToDisplayInverse() const {
return (mTransform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) ==
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
@@ -2619,6 +2640,17 @@
return BAD_VALUE;
}
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+status_t Surface::setAdditionalOptions(const std::vector<gui::AdditionalOptions>& options) {
+ if (!GraphicBufferAllocator::get().supportsAdditionalOptions()) {
+ return INVALID_OPERATION;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ return mGraphicBufferProducer->setAdditionalOptions(options);
+}
+#endif
+
sp<IBinder> Surface::getSurfaceControlHandle() const {
Mutex::Autolock lock(mMutex);
return mSurfaceControlHandle;
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 07664ca..7aaaebb 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -708,6 +708,7 @@
SurfaceComposerClient::Transaction::Transaction() {
mId = generateId();
+ mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
@@ -725,6 +726,7 @@
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
mListenerCallbacks = other.mListenerCallbacks;
+ mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
@@ -1002,8 +1004,8 @@
// register all surface controls for all callbackIds for this listener that is merging
for (const auto& surfaceControl : currentProcessCallbackInfo.surfaceControls) {
- TransactionCompletedListener::getInstance()
- ->addSurfaceControlToCallbacks(currentProcessCallbackInfo, surfaceControl);
+ mTransactionCompletedListener->addSurfaceControlToCallbacks(currentProcessCallbackInfo,
+ surfaceControl);
}
}
@@ -1355,7 +1357,7 @@
auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
callbackInfo.surfaceControls.insert(sc);
- TransactionCompletedListener::getInstance()->addSurfaceControlToCallbacks(callbackInfo, sc);
+ mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
@@ -1673,7 +1675,7 @@
std::shared_ptr<BufferData> bufferData = std::move(s->bufferData);
- TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
+ mTransactionCompletedListener->removeReleaseBufferCallback(
bufferData->generateReleaseCallbackId());
s->what &= ~layer_state_t::eBufferChanged;
s->bufferData = nullptr;
@@ -1716,8 +1718,7 @@
bufferData->acquireFence = *fence;
bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
}
- bufferData->releaseBufferEndpoint =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ bufferData->releaseBufferEndpoint = IInterface::asBinder(mTransactionCompletedListener);
setReleaseBufferCallback(bufferData.get(), callback);
}
@@ -1775,9 +1776,10 @@
return;
}
- bufferData->releaseBufferListener = TransactionCompletedListener::getIInstance();
- auto listener = TransactionCompletedListener::getInstance();
- listener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(), callback);
+ bufferData->releaseBufferListener =
+ static_cast<sp<ITransactionCompletedListener>>(mTransactionCompletedListener);
+ mTransactionCompletedListener->setReleaseBufferCallback(bufferData->generateReleaseCallbackId(),
+ callback);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDataspace(
@@ -1933,18 +1935,15 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCallback(
TransactionCompletedCallbackTakesContext callback, void* callbackContext,
CallbackId::Type callbackType) {
- auto listener = TransactionCompletedListener::getInstance();
-
auto callbackWithContext = std::bind(callback, callbackContext, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
- const auto& surfaceControls =
- mListenerCallbacks[TransactionCompletedListener::getIInstance()].surfaceControls;
+ const auto& surfaceControls = mListenerCallbacks[mTransactionCompletedListener].surfaceControls;
CallbackId callbackId =
- listener->addCallbackFunction(callbackWithContext, surfaceControls, callbackType);
+ mTransactionCompletedListener->addCallbackFunction(callbackWithContext, surfaceControls,
+ callbackType);
- mListenerCallbacks[TransactionCompletedListener::getIInstance()].callbackIds.emplace(
- callbackId);
+ mListenerCallbacks[mTransactionCompletedListener].callbackIds.emplace(callbackId);
return *this;
}
@@ -2251,23 +2250,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::enableBorder(
- const sp<SurfaceControl>& sc, bool shouldEnable, float width, const half4& color) {
- layer_state_t* s = getLayerState(sc);
- if (!s) {
- mStatus = BAD_INDEX;
- return *this;
- }
-
- s->what |= layer_state_t::eRenderBorderChanged;
- s->borderEnabled = shouldEnable;
- s->borderWidth = width;
- s->borderColor = color;
-
- registerSurfaceControlForCallback(sc);
- return *this;
-}
-
// ---------------------------------------------------------------------------
DisplayState& SurfaceComposerClient::Transaction::getDisplayState(const sp<IBinder>& token) {
@@ -2351,8 +2333,9 @@
const sp<SurfaceControl>& sc, TrustedPresentationCallback cb,
const TrustedPresentationThresholds& thresholds, void* context,
sp<SurfaceComposerClient::PresentationCallbackRAII>& outCallbackRef) {
- auto listener = TransactionCompletedListener::getInstance();
- outCallbackRef = listener->addTrustedPresentationCallback(cb, sc->getLayerId(), context);
+ outCallbackRef =
+ mTransactionCompletedListener->addTrustedPresentationCallback(cb, sc->getLayerId(),
+ context);
layer_state_t* s = getLayerState(sc);
if (!s) {
@@ -2369,8 +2352,7 @@
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::clearTrustedPresentationCallback(const sp<SurfaceControl>& sc) {
- auto listener = TransactionCompletedListener::getInstance();
- listener->clearTrustedPresentationCallback(sc->getLayerId());
+ mTransactionCompletedListener->clearTrustedPresentationCallback(sc->getLayerId());
layer_state_t* s = getLayerState(sc);
if (!s) {
@@ -2461,7 +2443,6 @@
const sp<IBinder>& parentHandle,
LayerMetadata metadata,
uint32_t* outTransformHint) {
- sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index ad0d99d..82d2554 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -146,7 +146,7 @@
parcel->writeInt32(ownerUid.val()) ?:
parcel->writeUtf8AsUtf16(packageName) ?:
parcel->writeInt32(inputConfig.get()) ?:
- parcel->writeInt32(displayId) ?:
+ parcel->writeInt32(displayId.val()) ?:
applicationInfo.writeToParcel(parcel) ?:
parcel->write(touchableRegion) ?:
parcel->writeBool(replaceTouchableRegionWithCrop) ?:
@@ -175,7 +175,8 @@
}
float dsdx, dtdx, tx, dtdy, dsdy, ty;
- int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt;
+ int32_t lpFlags, lpType, touchOcclusionModeInt, inputConfigInt, ownerPidInt, ownerUidInt,
+ displayIdInt;
sp<IBinder> touchableRegionCropHandleSp;
// clang-format off
@@ -198,7 +199,7 @@
parcel->readInt32(&ownerUidInt) ?:
parcel->readUtf8FromUtf16(&packageName) ?:
parcel->readInt32(&inputConfigInt) ?:
- parcel->readInt32(&displayId) ?:
+ parcel->readInt32(&displayIdInt) ?:
applicationInfo.readFromParcel(parcel) ?:
parcel->read(touchableRegion) ?:
parcel->readBool(&replaceTouchableRegionWithCrop) ?:
@@ -221,6 +222,7 @@
ownerPid = Pid{ownerPidInt};
ownerUid = Uid{static_cast<uid_t>(ownerUidInt)};
touchableRegionCropHandle = touchableRegionCropHandleSp;
+ displayId = ui::LogicalDisplayId{displayIdInt};
return OK;
}
diff --git a/libs/gui/aidl/Android.bp b/libs/gui/aidl/Android.bp
new file mode 100644
index 0000000..8ed08c2
--- /dev/null
+++ b/libs/gui/aidl/Android.bp
@@ -0,0 +1,85 @@
+// 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.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+ default_team: "trendy_team_android_core_graphics_stack",
+}
+
+filegroup {
+ name: "libgui_unstructured_aidl_files",
+ srcs: [
+ ":libgui_extra_unstructured_aidl_files",
+
+ "android/gui/BitTube.aidl",
+ "android/gui/CaptureArgs.aidl",
+ "android/gui/DisplayCaptureArgs.aidl",
+ "android/gui/LayerCaptureArgs.aidl",
+ "android/gui/LayerMetadata.aidl",
+ "android/gui/ParcelableVsyncEventData.aidl",
+ "android/gui/ScreenCaptureResults.aidl",
+ ],
+}
+
+aidl_library {
+ name: "libgui_unstructured_aidl",
+ hdrs: [":libgui_unstructured_aidl_files"],
+}
+
+filegroup {
+ name: "libgui_interface_aidl_files",
+ srcs: [
+ ":libgui_extra_aidl_files",
+ "**/*.aidl",
+ ],
+ exclude_srcs: [":libgui_unstructured_aidl_files"],
+}
+
+aidl_interface {
+ name: "android.gui",
+ unstable: true,
+ srcs: [
+ ":libgui_interface_aidl_files",
+ ],
+ include_dirs: [
+ "frameworks/native/libs/gui",
+ "frameworks/native/libs/gui/aidl",
+ ],
+ headers: [
+ "libgui_aidl_hdrs",
+ "libgui_extra_unstructured_aidl_hdrs",
+ ],
+ backend: {
+ rust: {
+ enabled: true,
+ additional_rustlibs: [
+ "libgui_aidl_types_rs",
+ ],
+ },
+ java: {
+ enabled: false,
+ },
+ cpp: {
+ enabled: false,
+ },
+ ndk: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/gui/aidl/android/gui/BitTube.aidl b/libs/gui/aidl/android/gui/BitTube.aidl
index 6b0595e..eb231c1 100644
--- a/libs/gui/aidl/android/gui/BitTube.aidl
+++ b/libs/gui/aidl/android/gui/BitTube.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable BitTube cpp_header "private/gui/BitTube.h";
+parcelable BitTube cpp_header "private/gui/BitTube.h" rust_type "gui_aidl_types_rs::BitTube";
diff --git a/libs/gui/aidl/android/gui/CaptureArgs.aidl b/libs/gui/aidl/android/gui/CaptureArgs.aidl
index 920d949..9f198ca 100644
--- a/libs/gui/aidl/android/gui/CaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/CaptureArgs.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
+parcelable CaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::CaptureArgs";
diff --git a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
index 2caa2b9..fc97dbf 100644
--- a/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/DisplayCaptureArgs.aidl
@@ -16,4 +16,5 @@
package android.gui;
-parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h";
+parcelable DisplayCaptureArgs cpp_header "gui/DisplayCaptureArgs.h" rust_type "gui_aidl_types_rs::DisplayCaptureArgs";
+
diff --git a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
index af138c7..13962fe 100644
--- a/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
+++ b/libs/gui/aidl/android/gui/DisplayModeSpecs.aidl
@@ -42,6 +42,17 @@
}
/**
+ * Refers to the time after which the idle screen's refresh rate is to be reduced
+ */
+ parcelable IdleScreenRefreshRateConfig {
+
+ /**
+ * The timeout value in milli seconds
+ */
+ int timeoutMillis;
+ }
+
+ /**
* Base mode ID. This is what system defaults to for all other settings, or
* if the refresh rate range is not available.
*/
@@ -72,4 +83,13 @@
* never smaller.
*/
RefreshRateRanges appRequestRanges;
+
+ /**
+ * The config to represent the maximum time (in ms) for which the display can remain in an idle
+ * state before reducing the refresh rate to conserve power.
+ * Null value refers that the device is not configured to dynamically reduce the refresh rate
+ * based on external conditions.
+ * -1 refers to the current conditions requires no timeout
+ */
+ @nullable IdleScreenRefreshRateConfig idleScreenRefreshRateConfig;
}
diff --git a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
index cf6752f..c6e7197 100644
--- a/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
+++ b/libs/gui/aidl/android/gui/ISurfaceComposer.aidl
@@ -43,7 +43,6 @@
import android.gui.IWindowInfosListener;
import android.gui.IWindowInfosPublisher;
import android.gui.LayerCaptureArgs;
-import android.gui.LayerDebugInfo;
import android.gui.OverlayProperties;
import android.gui.PullAtomData;
import android.gui.ScreenCaptureResults;
@@ -291,13 +290,6 @@
PullAtomData onPullAtom(int atomId);
/**
- * Gets the list of active layers in Z order for debugging purposes
- *
- * Requires the ACCESS_SURFACE_FLINGER permission.
- */
- List<LayerDebugInfo> getLayerDebugInfo();
-
- /**
* Gets the composition preference of the default data space and default pixel format,
* as well as the wide color gamut data space and wide color gamut pixel format.
* If the wide color gamut data space is V0_SRGB, then it implies that the platform
diff --git a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
index f0def50..18d293f 100644
--- a/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
+++ b/libs/gui/aidl/android/gui/LayerCaptureArgs.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h";
+parcelable LayerCaptureArgs cpp_header "gui/LayerCaptureArgs.h" rust_type "gui_aidl_types_rs::LayerCaptureArgs";
diff --git a/libs/gui/aidl/android/gui/LayerMetadata.aidl b/libs/gui/aidl/android/gui/LayerMetadata.aidl
index 1368ac5..d8121be 100644
--- a/libs/gui/aidl/android/gui/LayerMetadata.aidl
+++ b/libs/gui/aidl/android/gui/LayerMetadata.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable LayerMetadata cpp_header "gui/LayerMetadata.h";
+parcelable LayerMetadata cpp_header "gui/LayerMetadata.h" rust_type "gui_aidl_types_rs::LayerMetadata";
diff --git a/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
index ba76671..53f443a 100644
--- a/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
+++ b/libs/gui/aidl/android/gui/ParcelableVsyncEventData.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable ParcelableVsyncEventData cpp_header "gui/VsyncEventData.h";
+parcelable ParcelableVsyncEventData cpp_header "gui/VsyncEventData.h" rust_type "gui_aidl_types_rs::VsyncEventData";
diff --git a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
index 9908edd..97a9035 100644
--- a/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
+++ b/libs/gui/aidl/android/gui/ScreenCaptureResults.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h";
\ No newline at end of file
+parcelable ScreenCaptureResults cpp_header "gui/ScreenCaptureResults.h" rust_type "gui_aidl_types_rs::ScreenCaptureResults";
\ No newline at end of file
diff --git a/libs/gui/android/gui/DisplayInfo.aidl b/libs/gui/android/gui/DisplayInfo.aidl
index 30c0885..3b16724 100644
--- a/libs/gui/android/gui/DisplayInfo.aidl
+++ b/libs/gui/android/gui/DisplayInfo.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable DisplayInfo cpp_header "gui/DisplayInfo.h";
+parcelable DisplayInfo cpp_header "gui/DisplayInfo.h" rust_type "gui_aidl_types_rs::DisplayInfo";
diff --git a/libs/gui/android/gui/WindowInfo.aidl b/libs/gui/android/gui/WindowInfo.aidl
index 2c85d15..b9d5ccf 100644
--- a/libs/gui/android/gui/WindowInfo.aidl
+++ b/libs/gui/android/gui/WindowInfo.aidl
@@ -16,4 +16,4 @@
package android.gui;
-parcelable WindowInfo cpp_header "gui/WindowInfo.h";
+parcelable WindowInfo cpp_header "gui/WindowInfo.h" rust_type "gui_aidl_types_rs::WindowInfo";
diff --git a/libs/gui/android/gui/WindowInfosUpdate.aidl b/libs/gui/android/gui/WindowInfosUpdate.aidl
index 0c6109d..5c23e08 100644
--- a/libs/gui/android/gui/WindowInfosUpdate.aidl
+++ b/libs/gui/android/gui/WindowInfosUpdate.aidl
@@ -19,4 +19,4 @@
import android.gui.DisplayInfo;
import android.gui.WindowInfo;
-parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h";
+parcelable WindowInfosUpdate cpp_header "gui/WindowInfosUpdate.h" rust_type "gui_aidl_types_rs::WindowInfosUpdate";
diff --git a/libs/gui/include/gui/AdditionalOptions.h b/libs/gui/include/gui/AdditionalOptions.h
new file mode 100644
index 0000000..87cb913
--- /dev/null
+++ b/libs/gui/include/gui/AdditionalOptions.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <string>
+
+namespace android::gui {
+// Additional options to pass to AHardwareBuffer_allocateWithOptions.
+// See also allocator-v2's BufferDescriptorInfo.aidl
+struct AdditionalOptions {
+ std::string name;
+ int64_t value;
+
+ bool operator==(const AdditionalOptions& other) const {
+ return value == other.value && name == other.name;
+ }
+};
+} // namespace android::gui
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 22c2be7..bb52c8e 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -17,6 +17,9 @@
#ifndef ANDROID_GUI_BUFFERQUEUECORE_H
#define ANDROID_GUI_BUFFERQUEUECORE_H
+#include <com_android_graphics_libgui_flags.h>
+
+#include <gui/AdditionalOptions.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueueDefs.h>
#include <gui/BufferSlot.h>
@@ -357,6 +360,14 @@
// This allows the consumer to acquire an additional buffer if that buffer is not droppable and
// will eventually be released or acquired by the consumer.
bool mAllowExtraAcquire = false;
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ // Additional options to pass when allocating GraphicBuffers.
+ // GenerationID changes when the options change, indicating reallocation is required
+ uint32_t mAdditionalOptionsGenerationId = 0;
+ std::vector<gui::AdditionalOptions> mAdditionalOptions;
+#endif
+
}; // class BufferQueueCore
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index de47483..37a9607 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_GUI_BUFFERQUEUEPRODUCER_H
#define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
+#include <gui/AdditionalOptions.h>
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferProducer.h>
@@ -208,6 +209,10 @@
int8_t changeFrameRateStrategy) override;
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ status_t setAdditionalOptions(const std::vector<gui::AdditionalOptions>& options) override;
+#endif
+
protected:
// see IGraphicsBufferProducer::setMaxDequeuedBufferCount, but with the ability to retrieve the
// total maximum buffer count for the buffer queue (dequeued AND acquired)
diff --git a/libs/gui/include/gui/BufferSlot.h b/libs/gui/include/gui/BufferSlot.h
index 57704b1..5b32710 100644
--- a/libs/gui/include/gui/BufferSlot.h
+++ b/libs/gui/include/gui/BufferSlot.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_GUI_BUFFERSLOT_H
#define ANDROID_GUI_BUFFERSLOT_H
+#include <com_android_graphics_libgui_flags.h>
+
#include <ui/Fence.h>
#include <ui/GraphicBuffer.h>
@@ -230,6 +232,11 @@
// producer. If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when
// dequeued to prevent the producer from using a stale cached buffer.
bool mNeedsReallocation;
+
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ // The generation id of the additional options that mGraphicBuffer was allocated with
+ uint32_t mAdditionalOptionsGenerationId = 0;
+#endif
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index 8c1103b..4dbf9e1 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -119,6 +119,7 @@
HdcpLevelsChange hdcpLevelsChange;
};
};
+ static_assert(sizeof(Event) == 216);
public:
/*
diff --git a/libs/gui/include/gui/DisplayInfo.h b/libs/gui/include/gui/DisplayInfo.h
index 42b62c7..7282b80 100644
--- a/libs/gui/include/gui/DisplayInfo.h
+++ b/libs/gui/include/gui/DisplayInfo.h
@@ -18,7 +18,7 @@
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
-#include <gui/constants.h>
+#include <ui/LogicalDisplayId.h>
#include <ui/Transform.h>
namespace android::gui {
@@ -29,7 +29,7 @@
* This should only be used by InputFlinger to support raw coordinates in logical display space.
*/
struct DisplayInfo : public Parcelable {
- int32_t displayId = ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE;
// Logical display dimensions.
int32_t logicalWidth = 0;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 7639e70..8fca946 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -31,6 +31,7 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include <gui/AdditionalOptions.h>
#include <gui/FrameTimestamps.h>
#include <gui/HdrMetadata.h>
@@ -684,6 +685,10 @@
int8_t changeFrameRateStrategy);
#endif
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ virtual status_t setAdditionalOptions(const std::vector<gui::AdditionalOptions>& options);
+#endif
+
struct RequestBufferOutput : public Flattenable<RequestBufferOutput> {
RequestBufferOutput() = default;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index a836f46..738c73a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -74,7 +74,6 @@
struct DisplayCaptureArgs;
struct LayerCaptureArgs;
-class LayerDebugInfo;
} // namespace gui
diff --git a/libs/gui/include/gui/LayerDebugInfo.h b/libs/gui/include/gui/LayerDebugInfo.h
deleted file mode 100644
index dbb80e5..0000000
--- a/libs/gui/include/gui/LayerDebugInfo.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/Parcelable.h>
-
-#include <ui/PixelFormat.h>
-#include <ui/Region.h>
-#include <ui/StretchEffect.h>
-
-#include <string>
-#include <math/vec4.h>
-
-namespace android::gui {
-
-/* Class for transporting debug info from SurfaceFlinger to authorized
- * recipients. The class is intended to be a data container. There are
- * no getters or setters.
- */
-class LayerDebugInfo : public Parcelable {
-public:
- LayerDebugInfo() = default;
- LayerDebugInfo(const LayerDebugInfo&) = default;
- virtual ~LayerDebugInfo() = default;
-
- virtual status_t writeToParcel(Parcel* parcel) const;
- virtual status_t readFromParcel(const Parcel* parcel);
-
- std::string mName = std::string("NOT FILLED");
- std::string mParentName = std::string("NOT FILLED");
- std::string mType = std::string("NOT FILLED");
- Region mTransparentRegion = Region::INVALID_REGION;
- Region mVisibleRegion = Region::INVALID_REGION;
- Region mSurfaceDamageRegion = Region::INVALID_REGION;
- uint32_t mLayerStack = 0;
- float mX = 0.f;
- float mY = 0.f;
- uint32_t mZ = 0 ;
- int32_t mWidth = -1;
- int32_t mHeight = -1;
- android::Rect mCrop = android::Rect::INVALID_RECT;
- half4 mColor = half4(1.0_hf, 1.0_hf, 1.0_hf, 0.0_hf);
- uint32_t mFlags = 0;
- PixelFormat mPixelFormat = PIXEL_FORMAT_NONE;
- android_dataspace mDataSpace = HAL_DATASPACE_UNKNOWN;
- // Row-major transform matrix (SurfaceControl::setMatrix())
- float mMatrix[2][2] = {{0.f, 0.f}, {0.f, 0.f}};
- int32_t mActiveBufferWidth = -1;
- int32_t mActiveBufferHeight = -1;
- int32_t mActiveBufferStride = 0;
- PixelFormat mActiveBufferFormat = PIXEL_FORMAT_NONE;
- int32_t mNumQueuedFrames = -1;
- bool mIsOpaque = false;
- bool mContentDirty = false;
- StretchEffect mStretchEffect = {};
-};
-
-std::string to_string(const LayerDebugInfo& info);
-
-} // namespace android::gui
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 0fedea7..ca7acf9 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -179,7 +179,6 @@
eCachingHintChanged = 0x00000200,
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- eRenderBorderChanged = 0x00001000,
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -258,8 +257,8 @@
layer_state_t::eBlurRegionsChanged | layer_state_t::eColorChanged |
layer_state_t::eColorSpaceAgnosticChanged | layer_state_t::eColorTransformChanged |
layer_state_t::eCornerRadiusChanged | layer_state_t::eDimmingEnabledChanged |
- layer_state_t::eHdrMetadataChanged | layer_state_t::eRenderBorderChanged |
- layer_state_t::eShadowRadiusChanged | layer_state_t::eStretchChanged;
+ layer_state_t::eHdrMetadataChanged | layer_state_t::eShadowRadiusChanged |
+ layer_state_t::eStretchChanged;
// Changes which invalidates the layer's visible region in CE.
static constexpr uint64_t CONTENT_DIRTY = layer_state_t::CONTENT_CHANGES |
@@ -388,11 +387,6 @@
// should be trusted for input occlusion detection purposes
bool isTrustedOverlay;
- // Flag to indicate if border needs to be enabled on the layer
- bool borderEnabled;
- float borderWidth;
- half4 borderColor;
-
// Stretch effect to be applied to this layer
StretchEffect stretchEffect;
diff --git a/libs/gui/include/gui/LayerStatePermissions.h b/libs/gui/include/gui/LayerStatePermissions.h
index a90f30c..b6588a2 100644
--- a/libs/gui/include/gui/LayerStatePermissions.h
+++ b/libs/gui/include/gui/LayerStatePermissions.h
@@ -15,15 +15,14 @@
*/
#include <stdint.h>
-#include <string>
-#include <unordered_map>
-
+#include <utils/String16.h>
+#include <vector>
namespace android {
class LayerStatePermissions {
public:
static uint32_t getTransactionPermissions(int pid, int uid);
private:
- static std::unordered_map<std::string, int> mPermissionMap;
+ static std::vector<std::pair<String16, int>> mPermissionMap;
};
} // namespace android
\ No newline at end of file
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 39a59e4..bdcaaf2 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -215,6 +215,16 @@
int8_t changeFrameRateStrategy);
virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ /**
+ * Set additional options to be passed when allocating a buffer. Only valid if IAllocator-V2
+ * or newer is available, otherwise will return INVALID_OPERATION. Only allowed to be called
+ * after connect and options are cleared when disconnect happens. Returns NO_INIT if not
+ * connected
+ */
+ status_t setAdditionalOptions(const std::vector<gui::AdditionalOptions>& options);
+#endif
+
protected:
virtual ~Surface();
@@ -302,6 +312,7 @@
int dispatchGetLastQueuedBuffer(va_list args);
int dispatchGetLastQueuedBuffer2(va_list args);
int dispatchSetFrameTimelineInfo(va_list args);
+ int dispatchSetAdditionalOptions(va_list args);
std::mutex mNameMutex;
std::string mName;
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 82090fa..987efe0 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -430,6 +430,8 @@
static std::mutex sApplyTokenMutex;
void releaseBufferIfOverwriting(const layer_state_t& state);
static void mergeFrameTimelineInfo(FrameTimelineInfo& t, const FrameTimelineInfo& other);
+ // Tracks registered callbacks
+ sp<TransactionCompletedListener> mTransactionCompletedListener = nullptr;
protected:
std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates;
@@ -743,9 +745,6 @@
const Rect& destinationFrame);
Transaction& setDropInputMode(const sp<SurfaceControl>& sc, gui::DropInputMode mode);
- Transaction& enableBorder(const sp<SurfaceControl>& sc, bool shouldEnable, float width,
- const half4& color);
-
status_t setDisplaySurface(const sp<IBinder>& token,
const sp<IGraphicBufferProducer>& bufferProducer);
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 2d1b51a..3ea3f67 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -23,7 +23,7 @@
#include <ftl/flags.h>
#include <ftl/mixins.h>
#include <gui/PidUid.h>
-#include <gui/constants.h>
+#include <ui/LogicalDisplayId.h>
#include <ui/Rect.h>
#include <ui/Region.h>
#include <ui/Size.h>
@@ -178,6 +178,8 @@
static_cast<uint32_t>(os::InputConfig::CLONE),
GLOBAL_STYLUS_BLOCKS_TOUCH =
static_cast<uint32_t>(os::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH),
+ SENSITIVE_FOR_PRIVACY =
+ static_cast<uint32_t>(os::InputConfig::SENSITIVE_FOR_PRIVACY),
// clang-format on
};
@@ -232,7 +234,7 @@
Uid ownerUid = Uid::INVALID;
std::string packageName;
ftl::Flags<InputConfig> inputConfig;
- int32_t displayId = ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE;
InputApplicationInfo applicationInfo;
bool replaceTouchableRegionWithCrop = false;
wp<IBinder> touchableRegionCropHandle;
diff --git a/libs/gui/include/gui/constants.h b/libs/gui/include/gui/constants.h
deleted file mode 100644
index 8eab378..0000000
--- a/libs/gui/include/gui/constants.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-namespace android {
-
-/**
- * Invalid value for display size. Used when display size isn't available.
- */
-constexpr int32_t INVALID_DISPLAY_SIZE = 0;
-
-enum {
- /* Used when an event is not associated with any display.
- * Typically used for non-pointer events. */
- ADISPLAY_ID_NONE = -1,
-
- /* The default display id. */
- ADISPLAY_ID_DEFAULT = 0,
-};
-
-} // namespace android
\ No newline at end of file
diff --git a/libs/gui/libgui_flags.aconfig b/libs/gui/libgui_flags.aconfig
index 3864699..a902a8c 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -16,3 +16,11 @@
bug: "310927247"
is_fixed_read_only: true
}
+
+flag {
+ name: "bq_extendedallocate"
+ namespace: "core_graphics"
+ description: "Add BQ support for allocate with extended options"
+ bug: "268382490"
+ is_fixed_read_only: true
+}
diff --git a/libs/gui/rust/aidl_types/Android.bp b/libs/gui/rust/aidl_types/Android.bp
new file mode 100644
index 0000000..794f69e
--- /dev/null
+++ b/libs/gui/rust/aidl_types/Android.bp
@@ -0,0 +1,23 @@
+rust_defaults {
+ name: "libgui_aidl_types_defaults",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ ],
+}
+
+rust_library {
+ name: "libgui_aidl_types_rs",
+ crate_name: "gui_aidl_types_rs",
+ defaults: ["libgui_aidl_types_defaults"],
+
+ // Currently necessary for host builds
+ // TODO(b/31559095): bionic on host should define this
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+ min_sdk_version: "VanillaIceCream",
+ vendor_available: true,
+}
diff --git a/libs/gui/rust/aidl_types/src/lib.rs b/libs/gui/rust/aidl_types/src/lib.rs
new file mode 100644
index 0000000..fead018
--- /dev/null
+++ b/libs/gui/rust/aidl_types/src/lib.rs
@@ -0,0 +1,55 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Rust wrapper for libgui AIDL types.
+
+use binder::{
+ binder_impl::{BorrowedParcel, UnstructuredParcelable},
+ impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
+ StatusCode,
+};
+
+macro_rules! stub_unstructured_parcelable {
+ ($name:ident) => {
+ /// Unimplemented stub parcelable.
+ #[derive(Debug, Default)]
+ pub struct $name(());
+
+ impl UnstructuredParcelable for $name {
+ fn write_to_parcel(&self, _parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
+ todo!()
+ }
+
+ fn from_parcel(_parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
+ todo!()
+ }
+ }
+
+ impl_deserialize_for_unstructured_parcelable!($name);
+ impl_serialize_for_unstructured_parcelable!($name);
+ };
+}
+
+stub_unstructured_parcelable!(BitTube);
+stub_unstructured_parcelable!(CaptureArgs);
+stub_unstructured_parcelable!(DisplayCaptureArgs);
+stub_unstructured_parcelable!(DisplayInfo);
+stub_unstructured_parcelable!(LayerCaptureArgs);
+stub_unstructured_parcelable!(LayerDebugInfo);
+stub_unstructured_parcelable!(LayerMetadata);
+stub_unstructured_parcelable!(ParcelableVsyncEventData);
+stub_unstructured_parcelable!(ScreenCaptureResults);
+stub_unstructured_parcelable!(VsyncEventData);
+stub_unstructured_parcelable!(WindowInfo);
+stub_unstructured_parcelable!(WindowInfosUpdate);
diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp
index 0f16f71..ea8acbb 100644
--- a/libs/gui/tests/Android.bp
+++ b/libs/gui/tests/Android.bp
@@ -21,8 +21,10 @@
cppflags: [
"-Wall",
"-Werror",
- "-Wno-extra",
+ "-Wextra",
+ "-Wthread-safety",
"-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true",
+ "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_EXTENDEDALLOCATE=true",
],
srcs: [
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index ea7078d..946ff05 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -18,6 +18,7 @@
#include <gui/BLASTBufferQueue.h>
+#include <android-base/thread_annotations.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <gui/AidlStatusUtil.h>
#include <gui/BufferQueueCore.h>
@@ -61,7 +62,8 @@
}
void waitOnNumberReleased(int32_t expectedNumReleased) {
- std::unique_lock<std::mutex> lock(mMutex);
+ std::unique_lock lock{mMutex};
+ base::ScopedLockAssertion assumeLocked(mMutex);
while (mNumReleased < expectedNumReleased) {
ASSERT_NE(mReleaseCallback.wait_for(lock, std::chrono::seconds(3)),
std::cv_status::timeout)
@@ -134,11 +136,18 @@
void clearSyncTransaction() { mBlastBufferQueueAdapter->clearSyncTransaction(); }
- int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
+ int getWidth() {
+ std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex);
+ return mBlastBufferQueueAdapter->mSize.width;
+ }
- int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
+ int getHeight() {
+ std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex);
+ return mBlastBufferQueueAdapter->mSize.height;
+ }
std::function<void(Transaction*)> getTransactionReadyCallback() {
+ std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex);
return mBlastBufferQueueAdapter->mTransactionReadyCallback;
}
@@ -147,6 +156,7 @@
}
const sp<SurfaceControl> getSurfaceControl() {
+ std::scoped_lock lock(mBlastBufferQueueAdapter->mMutex);
return mBlastBufferQueueAdapter->mSurfaceControl;
}
@@ -156,6 +166,7 @@
void waitForCallbacks() {
std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ base::ScopedLockAssertion assumeLocked(mBlastBufferQueueAdapter->mMutex);
// Wait until all but one of the submitted buffers have been released.
while (mBlastBufferQueueAdapter->mSubmitted.size() > 1) {
mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
@@ -166,8 +177,8 @@
mBlastBufferQueueAdapter->waitForCallback(frameNumber);
}
- void validateNumFramesSubmitted(int64_t numFramesSubmitted) {
- std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ void validateNumFramesSubmitted(size_t numFramesSubmitted) {
+ std::scoped_lock lock{mBlastBufferQueueAdapter->mMutex};
ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
}
@@ -201,7 +212,7 @@
mDisplayWidth = resolution.getWidth();
mDisplayHeight = resolution.getHeight();
ALOGD("Display: %dx%d orientation:%d", mDisplayWidth, mDisplayHeight,
- displayState.orientation);
+ static_cast<int32_t>(displayState.orientation));
mRootSurfaceControl = mClient->createSurface(String8("RootTestSurface"), mDisplayWidth,
mDisplayHeight, PIXEL_FORMAT_RGBA_8888,
@@ -240,8 +251,8 @@
void fillBuffer(uint32_t* bufData, Rect rect, uint32_t stride, uint8_t r, uint8_t g,
uint8_t b) {
- for (uint32_t row = rect.top; row < rect.bottom; row++) {
- for (uint32_t col = rect.left; col < rect.right; col++) {
+ for (int32_t row = rect.top; row < rect.bottom; row++) {
+ for (int32_t col = rect.left; col < rect.right; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
*pixel = r;
*(pixel + 1) = g;
@@ -271,16 +282,16 @@
bool outsideRegion = false) {
sp<GraphicBuffer>& captureBuf = mCaptureResults.buffer;
const auto epsilon = 3;
- const auto width = captureBuf->getWidth();
- const auto height = captureBuf->getHeight();
+ const int32_t width = static_cast<int32_t>(captureBuf->getWidth());
+ const int32_t height = static_cast<int32_t>(captureBuf->getHeight());
const auto stride = captureBuf->getStride();
uint32_t* bufData;
captureBuf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_READ_OFTEN),
reinterpret_cast<void**>(&bufData));
- for (uint32_t row = 0; row < height; row++) {
- for (uint32_t col = 0; col < width; col++) {
+ for (int32_t row = 0; row < height; row++) {
+ for (int32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
ASSERT_NE(nullptr, pixel);
bool inRegion;
@@ -352,8 +363,8 @@
// create BLASTBufferQueue adapter associated with this surface
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
- ASSERT_EQ(mDisplayWidth, adapter.getWidth());
- ASSERT_EQ(mDisplayHeight, adapter.getHeight());
+ ASSERT_EQ(static_cast<int32_t>(mDisplayWidth), adapter.getWidth());
+ ASSERT_EQ(static_cast<int32_t>(mDisplayHeight), adapter.getHeight());
ASSERT_EQ(nullptr, adapter.getTransactionReadyCallback());
}
@@ -371,10 +382,10 @@
int32_t width;
igbProducer->query(NATIVE_WINDOW_WIDTH, &width);
- ASSERT_EQ(mDisplayWidth / 2, width);
+ ASSERT_EQ(static_cast<int32_t>(mDisplayWidth) / 2, width);
int32_t height;
igbProducer->query(NATIVE_WINDOW_HEIGHT, &height);
- ASSERT_EQ(mDisplayHeight / 2, height);
+ ASSERT_EQ(static_cast<int32_t>(mDisplayHeight) / 2, height);
}
TEST_F(BLASTBufferQueueTest, SyncNextTransaction) {
@@ -476,7 +487,7 @@
ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
allocated.push_back({slot, fence});
}
- for (int i = 0; i < allocated.size(); i++) {
+ for (size_t i = 0; i < allocated.size(); i++) {
igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
}
@@ -1313,14 +1324,14 @@
// Before connecting to the surface, we do not get a valid transform hint
int transformHint;
surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- ASSERT_EQ(ui::Transform::ROT_0, transformHint);
+ ASSERT_EQ(ui::Transform::ROT_0, static_cast<ui::Transform::RotationFlags>(transformHint));
ASSERT_EQ(NO_ERROR,
surface->connect(NATIVE_WINDOW_API_CPU, new TestProducerListener(igbProducer)));
// After connecting to the surface, we should get the correct hint.
surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- ASSERT_EQ(ui::Transform::ROT_90, transformHint);
+ ASSERT_EQ(ui::Transform::ROT_90, static_cast<ui::Transform::RotationFlags>(transformHint));
ANativeWindow_Buffer buffer;
surface->lock(&buffer, nullptr /* inOutDirtyBounds */);
@@ -1331,13 +1342,13 @@
// The hint does not change and matches the value used when dequeueing the buffer.
surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- ASSERT_EQ(ui::Transform::ROT_90, transformHint);
+ ASSERT_EQ(ui::Transform::ROT_90, static_cast<ui::Transform::RotationFlags>(transformHint));
surface->unlockAndPost();
// After queuing the buffer, we get the updated transform hint
surface->query(NATIVE_WINDOW_TRANSFORM_HINT, &transformHint);
- ASSERT_EQ(ui::Transform::ROT_0, transformHint);
+ ASSERT_EQ(ui::Transform::ROT_0, static_cast<ui::Transform::RotationFlags>(transformHint));
adapter.waitForCallbacks();
}
@@ -1573,7 +1584,7 @@
FrameEvents* events = nullptr;
events = history.getFrame(1);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1591,7 +1602,7 @@
ASSERT_NE(nullptr, events);
// frame number, requestedPresentTime, and postTime should not have changed
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1606,7 +1617,7 @@
// we should also have gotten the initial values for the next frame
events = history.getFrame(2);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(2u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
@@ -1622,7 +1633,7 @@
// Check the first frame...
events = history.getFrame(1);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
ASSERT_GE(events->latchTime, postedTimeA);
@@ -1636,7 +1647,7 @@
// ...and the second
events = history.getFrame(2);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(2u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
ASSERT_GE(events->latchTime, postedTimeB);
@@ -1650,7 +1661,7 @@
// ...and finally the third!
events = history.getFrame(3);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(3, events->frameNumber);
+ ASSERT_EQ(3u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeC, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeC);
@@ -1677,7 +1688,7 @@
FrameEvents* events = nullptr;
events = history.getFrame(1);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1692,7 +1703,7 @@
ASSERT_NE(nullptr, events);
// frame number, requestedPresentTime, and postTime should not have changed
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1715,7 +1726,7 @@
adapter.waitForCallback(3);
// frame number, requestedPresentTime, and postTime should not have changed
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1729,7 +1740,7 @@
// we should also have gotten values for the presented frame
events = history.getFrame(2);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(2u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
ASSERT_GE(events->latchTime, postedTimeB);
@@ -1751,7 +1762,7 @@
// frame number, requestedPresentTime, and postTime should not have changed
events = history.getFrame(1);
- ASSERT_EQ(1, events->frameNumber);
+ ASSERT_EQ(1u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeA, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeA);
@@ -1765,7 +1776,7 @@
// we should also have gotten values for the presented frame
events = history.getFrame(2);
ASSERT_NE(nullptr, events);
- ASSERT_EQ(2, events->frameNumber);
+ ASSERT_EQ(2u, events->frameNumber);
ASSERT_EQ(requestedPresentTimeB, events->requestedPresentTime);
ASSERT_GE(events->postedTime, postedTimeB);
ASSERT_GE(events->latchTime, postedTimeB);
diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp
index df7739c..272c5ed 100644
--- a/libs/gui/tests/BufferQueue_test.cpp
+++ b/libs/gui/tests/BufferQueue_test.cpp
@@ -28,6 +28,8 @@
#include <ui/GraphicBuffer.h>
+#include <android-base/properties.h>
+
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
@@ -47,6 +49,10 @@
using namespace std::chrono_literals;
+static bool IsCuttlefish() {
+ return ::android::base::GetProperty("ro.product.board", "") == "cutf";
+}
+
namespace android {
using namespace com::android::graphics::libgui;
@@ -119,8 +125,7 @@
}
sp<IServiceManager> serviceManager = defaultServiceManager();
- sp<IBinder> binderProducer =
- serviceManager->getService(PRODUCER_NAME);
+ sp<IBinder> binderProducer = serviceManager->waitForService(PRODUCER_NAME);
mProducer = interface_cast<IGraphicBufferProducer>(binderProducer);
EXPECT_TRUE(mProducer != nullptr);
sp<IBinder> binderConsumer =
@@ -1114,7 +1119,7 @@
// Check onBuffersDiscarded is called with correct slots
auto buffersDiscarded = pl->getDiscardedSlots();
- ASSERT_EQ(buffersDiscarded.size(), 1);
+ ASSERT_EQ(buffersDiscarded.size(), 1u);
ASSERT_EQ(buffersDiscarded[0], releasedSlot);
// Check no free buffers in dump
@@ -1239,7 +1244,7 @@
ASSERT_EQ(OK, mConsumer->detachBuffer(item.mSlot));
// Check whether the slot from IProducerListener is same to the detached slot.
- ASSERT_EQ(pl->getDetachedSlots().size(), 1);
+ ASSERT_EQ(pl->getDetachedSlots().size(), 1u);
ASSERT_EQ(pl->getDetachedSlots()[0], slots[1]);
// Dequeue another buffer.
@@ -1440,4 +1445,55 @@
EXPECT_EQ(nullptr, bufferConsumer.get());
}
+TEST_F(BufferQueueTest, TestAdditionalOptions) {
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer);
+
+ sp<BufferItemConsumer> bufferConsumer =
+ sp<BufferItemConsumer>::make(consumer, GRALLOC_USAGE_SW_READ_OFTEN, 2);
+ ASSERT_NE(nullptr, bufferConsumer.get());
+ sp<Surface> surface = sp<Surface>::make(producer);
+ native_window_set_buffers_format(surface.get(), PIXEL_FORMAT_RGBA_8888);
+ native_window_set_buffers_dimensions(surface.get(), 100, 100);
+
+ std::array<AHardwareBufferLongOptions, 1> extras = {{
+ {.name = "android.hardware.graphics.common.Dataspace", ADATASPACE_DISPLAY_P3},
+ }};
+
+ ASSERT_EQ(NO_INIT,
+ native_window_set_buffers_additional_options(surface.get(), extras.data(),
+ extras.size()));
+
+ if (!IsCuttlefish()) {
+ GTEST_SKIP() << "Not cuttlefish";
+ }
+
+ ASSERT_EQ(OK, native_window_api_connect(surface.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(OK,
+ native_window_set_buffers_additional_options(surface.get(), extras.data(),
+ extras.size()));
+
+ ANativeWindowBuffer* windowBuffer = nullptr;
+ int fence = -1;
+ ASSERT_EQ(OK, ANativeWindow_dequeueBuffer(surface.get(), &windowBuffer, &fence));
+
+ AHardwareBuffer* buffer = ANativeWindowBuffer_getHardwareBuffer(windowBuffer);
+ ASSERT_TRUE(buffer);
+ ADataSpace dataSpace = AHardwareBuffer_getDataSpace(buffer);
+ EXPECT_EQ(ADATASPACE_DISPLAY_P3, dataSpace);
+
+ ANativeWindow_cancelBuffer(surface.get(), windowBuffer, -1);
+
+ // Check that reconnecting properly clears the options
+ ASSERT_EQ(OK, native_window_api_disconnect(surface.get(), NATIVE_WINDOW_API_CPU));
+ ASSERT_EQ(OK, native_window_api_connect(surface.get(), NATIVE_WINDOW_API_CPU));
+
+ ASSERT_EQ(OK, ANativeWindow_dequeueBuffer(surface.get(), &windowBuffer, &fence));
+ buffer = ANativeWindowBuffer_getHardwareBuffer(windowBuffer);
+ ASSERT_TRUE(buffer);
+ dataSpace = AHardwareBuffer_getDataSpace(buffer);
+ EXPECT_EQ(ADATASPACE_UNKNOWN, dataSpace);
+}
+
} // namespace android
diff --git a/libs/gui/tests/DisplayInfo_test.cpp b/libs/gui/tests/DisplayInfo_test.cpp
index df3329c..4df76b1 100644
--- a/libs/gui/tests/DisplayInfo_test.cpp
+++ b/libs/gui/tests/DisplayInfo_test.cpp
@@ -28,7 +28,7 @@
TEST(DisplayInfo, Parcelling) {
DisplayInfo info;
- info.displayId = 42;
+ info.displayId = ui::LogicalDisplayId{42};
info.logicalWidth = 99;
info.logicalHeight = 78;
info.transform.set({0.4, -1, 100, 0.5, 0, 40, 0, 0, 1});
diff --git a/libs/gui/tests/DisplayedContentSampling_test.cpp b/libs/gui/tests/DisplayedContentSampling_test.cpp
index 0a2750a..bffb3f0 100644
--- a/libs/gui/tests/DisplayedContentSampling_test.cpp
+++ b/libs/gui/tests/DisplayedContentSampling_test.cpp
@@ -116,10 +116,10 @@
EXPECT_EQ(OK, status);
if (stats.numFrames <= 0) return;
- if (componentMask & (0x1 << 0)) EXPECT_NE(0, stats.component_0_sample.size());
- if (componentMask & (0x1 << 1)) EXPECT_NE(0, stats.component_1_sample.size());
- if (componentMask & (0x1 << 2)) EXPECT_NE(0, stats.component_2_sample.size());
- if (componentMask & (0x1 << 3)) EXPECT_NE(0, stats.component_3_sample.size());
+ if (componentMask & (0x1 << 0)) EXPECT_NE(0u, stats.component_0_sample.size());
+ if (componentMask & (0x1 << 1)) EXPECT_NE(0u, stats.component_1_sample.size());
+ if (componentMask & (0x1 << 2)) EXPECT_NE(0u, stats.component_2_sample.size());
+ if (componentMask & (0x1 << 3)) EXPECT_NE(0u, stats.component_3_sample.size());
}
} // namespace android
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index 9791212..c0e7965 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -24,6 +24,7 @@
#include <memory>
+#include <android-base/thread_annotations.h>
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/keycodes.h>
#include <android/native_window.h>
@@ -58,8 +59,16 @@
using android::gui::InputApplicationInfo;
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
+using android::ui::ADISPLAY_ID_DEFAULT;
+using android::ui::ADISPLAY_ID_NONE;
-namespace android::test {
+namespace android {
+namespace {
+ui::LogicalDisplayId toDisplayId(ui::LayerStack layerStack) {
+ return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)};
+}
+} // namespace
+namespace test {
using Transaction = SurfaceComposerClient::Transaction;
@@ -67,7 +76,9 @@
sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger")));
if (input == nullptr) {
ALOGE("Failed to link to input service");
- } else { ALOGE("Linked to input"); }
+ } else {
+ ALOGE("Linked to input");
+ }
return interface_cast<IInputFlinger>(input);
}
@@ -78,26 +89,27 @@
class SynchronousWindowInfosReportedListener : public gui::BnWindowInfosReportedListener {
public:
binder::Status onWindowInfosReported() override {
- std::lock_guard<std::mutex> lock{mMutex};
+ std::scoped_lock lock{mLock};
mWindowInfosReported = true;
mConditionVariable.notify_one();
return binder::Status::ok();
}
void wait() {
- std::unique_lock<std::mutex> lock{mMutex};
- mConditionVariable.wait(lock, [&] { return mWindowInfosReported; });
+ std::unique_lock lock{mLock};
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ mConditionVariable.wait(lock, [&]() REQUIRES(mLock) { return mWindowInfosReported; });
}
private:
- std::mutex mMutex;
+ std::mutex mLock;
std::condition_variable mConditionVariable;
- bool mWindowInfosReported{false};
+ bool mWindowInfosReported GUARDED_BY(mLock){false};
};
class InputSurface {
public:
- InputSurface(const sp<SurfaceControl> &sc, int width, int height, bool noInputChannel = false) {
+ InputSurface(const sp<SurfaceControl>& sc, int width, int height, bool noInputChannel = false) {
mSurfaceControl = sc;
mInputFlinger = getInputFlinger();
@@ -128,7 +140,7 @@
mInputInfo.applicationInfo = aInfo;
}
- static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient> &scc,
+ static std::unique_ptr<InputSurface> makeColorInputSurface(const sp<SurfaceComposerClient>& scc,
int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Surface"), 0 /* bufHeight */, 0 /* bufWidth */,
@@ -138,7 +150,7 @@
}
static std::unique_ptr<InputSurface> makeBufferInputSurface(
- const sp<SurfaceComposerClient> &scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Buffer Surface"), width, height,
PIXEL_FORMAT_RGBA_8888, 0 /* flags */);
@@ -146,7 +158,7 @@
}
static std::unique_ptr<InputSurface> makeContainerInputSurface(
- const sp<SurfaceComposerClient> &scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Container Surface"), 0 /* bufHeight */,
0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
@@ -155,7 +167,7 @@
}
static std::unique_ptr<InputSurface> makeContainerInputSurfaceNoInputChannel(
- const sp<SurfaceComposerClient> &scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Container Surface"), 100 /* height */,
100 /* width */, PIXEL_FORMAT_RGBA_8888,
@@ -165,7 +177,7 @@
}
static std::unique_ptr<InputSurface> makeCursorInputSurface(
- const sp<SurfaceComposerClient> &scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height) {
sp<SurfaceControl> surfaceControl =
scc->createSurface(String8("Test Cursor Surface"), 0 /* bufHeight */,
0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
@@ -176,7 +188,7 @@
InputEvent* consumeEvent(std::chrono::milliseconds timeout = 3000ms) {
mClientChannel->waitForMessage(timeout);
- InputEvent *ev;
+ InputEvent* ev;
uint32_t seqId;
status_t consumed = mInputConsumer->consume(&mInputEventFactory, true, -1, &seqId, &ev);
if (consumed != OK) {
@@ -188,14 +200,14 @@
}
void assertFocusChange(bool hasFocus) {
- InputEvent *ev = consumeEvent();
+ InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::FOCUS, ev->getType());
- FocusEvent *focusEvent = static_cast<FocusEvent *>(ev);
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(ev);
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
}
- void expectTap(int x, int y) {
+ void expectTap(float x, float y) {
InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::MOTION, ev->getType());
@@ -214,10 +226,10 @@
}
void expectTapWithFlag(int x, int y, int32_t flags) {
- InputEvent *ev = consumeEvent();
+ InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::MOTION, ev->getType());
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ MotionEvent* mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
EXPECT_EQ(x, mev->getX(0));
EXPECT_EQ(y, mev->getY(0));
@@ -226,18 +238,18 @@
ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::MOTION, ev->getType());
- mev = static_cast<MotionEvent *>(ev);
+ mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
EXPECT_EQ(flags, mev->getFlags() & flags);
}
void expectTapInDisplayCoordinates(int displayX, int displayY) {
- InputEvent *ev = consumeEvent();
+ InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::MOTION, ev->getType());
- MotionEvent *mev = static_cast<MotionEvent *>(ev);
+ MotionEvent* mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, mev->getAction());
- const PointerCoords &coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
+ const PointerCoords& coords = *mev->getRawPointerCoords(0 /*pointerIndex*/);
EXPECT_EQ(displayX, coords.getX());
EXPECT_EQ(displayY, coords.getY());
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
@@ -245,16 +257,16 @@
ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::MOTION, ev->getType());
- mev = static_cast<MotionEvent *>(ev);
+ mev = static_cast<MotionEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, mev->getAction());
EXPECT_EQ(0, mev->getFlags() & VERIFIED_MOTION_EVENT_FLAGS);
}
- void expectKey(uint32_t keycode) {
- InputEvent *ev = consumeEvent();
+ void expectKey(int32_t keycode) {
+ InputEvent* ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::KEY, ev->getType());
- KeyEvent *keyEvent = static_cast<KeyEvent *>(ev);
+ KeyEvent* keyEvent = static_cast<KeyEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, keyEvent->getAction());
EXPECT_EQ(keycode, keyEvent->getKeyCode());
EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
@@ -262,12 +274,17 @@
ev = consumeEvent();
ASSERT_NE(ev, nullptr);
ASSERT_EQ(InputEventType::KEY, ev->getType());
- keyEvent = static_cast<KeyEvent *>(ev);
+ keyEvent = static_cast<KeyEvent*>(ev);
EXPECT_EQ(AMOTION_EVENT_ACTION_UP, keyEvent->getAction());
EXPECT_EQ(keycode, keyEvent->getKeyCode());
EXPECT_EQ(0, keyEvent->getFlags() & VERIFIED_KEY_EVENT_FLAGS);
}
+ void assertNoEvent() {
+ InputEvent* event = consumeEvent(/*timeout=*/100ms);
+ ASSERT_EQ(event, nullptr) << "Expected no event, but got " << *event;
+ }
+
virtual ~InputSurface() {
if (mClientChannel) {
mInputFlinger->removeInputChannel(mClientChannel->getConnectionToken());
@@ -275,7 +292,7 @@
}
virtual void doTransaction(
- std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ std::function<void(SurfaceComposerClient::Transaction&, const sp<SurfaceControl>&)>
transactionBody) {
SurfaceComposerClient::Transaction t;
transactionBody(t, mSurfaceControl);
@@ -296,13 +313,13 @@
reportedListener->wait();
}
- void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
+ void requestFocus(ui::LogicalDisplayId displayId = ADISPLAY_ID_DEFAULT) {
SurfaceComposerClient::Transaction t;
FocusRequest request;
request.token = mInputInfo.token;
request.windowName = mInputInfo.name;
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- request.displayId = displayId;
+ request.displayId = displayId.val();
t.setFocusedWindow(request);
t.apply(true);
}
@@ -320,7 +337,7 @@
class BlastInputSurface : public InputSurface {
public:
- BlastInputSurface(const sp<SurfaceControl> &sc, const sp<SurfaceControl> &parentSc, int width,
+ BlastInputSurface(const sp<SurfaceControl>& sc, const sp<SurfaceControl>& parentSc, int width,
int height)
: InputSurface(sc, width, height) {
mParentSurfaceControl = parentSc;
@@ -329,7 +346,7 @@
~BlastInputSurface() = default;
static std::unique_ptr<BlastInputSurface> makeBlastInputSurface(
- const sp<SurfaceComposerClient> &scc, int width, int height) {
+ const sp<SurfaceComposerClient>& scc, int width, int height) {
sp<SurfaceControl> parentSc =
scc->createSurface(String8("Test Parent Surface"), 0 /* bufHeight */,
0 /* bufWidth */, PIXEL_FORMAT_RGBA_8888,
@@ -344,7 +361,7 @@
}
void doTransaction(
- std::function<void(SurfaceComposerClient::Transaction &, const sp<SurfaceControl> &)>
+ std::function<void(SurfaceComposerClient::Transaction&, const sp<SurfaceControl>&)>
transactionBody) override {
SurfaceComposerClient::Transaction t;
transactionBody(t, mParentSurfaceControl);
@@ -371,9 +388,7 @@
class InputSurfacesTest : public ::testing::Test {
public:
- InputSurfacesTest() {
- ProcessState::self()->startThreadPool();
- }
+ InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
void SetUp() {
mComposerClient = new SurfaceComposerClient;
@@ -393,15 +408,13 @@
mBufferPostDelay = static_cast<int32_t>(1e6 / mode.peakRefreshRate) * 3;
}
- void TearDown() {
- mComposerClient->dispose();
- }
+ void TearDown() { mComposerClient->dispose(); }
std::unique_ptr<InputSurface> makeSurface(int width, int height) {
return InputSurface::makeColorInputSurface(mComposerClient, width, height);
}
- void postBuffer(const sp<SurfaceControl> &layer, int32_t w, int32_t h) {
+ void postBuffer(const sp<SurfaceControl>& layer, int32_t w, int32_t h) {
int64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
sp<GraphicBuffer> buffer =
@@ -414,11 +427,11 @@
int32_t mBufferPostDelay;
};
-void injectTapOnDisplay(int x, int y, int displayId) {
+void injectTapOnDisplay(int x, int y, ui::LogicalDisplayId displayId) {
char *buf1, *buf2, *bufDisplayId;
asprintf(&buf1, "%d", x);
asprintf(&buf2, "%d", y);
- asprintf(&bufDisplayId, "%d", displayId);
+ asprintf(&bufDisplayId, "%d", displayId.val());
if (fork() == 0) {
execlp("input", "input", "-d", bufDisplayId, "tap", buf1, buf2, NULL);
}
@@ -428,10 +441,10 @@
injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
}
-void injectKeyOnDisplay(uint32_t keycode, int displayId) {
+void injectKeyOnDisplay(uint32_t keycode, ui::LogicalDisplayId displayId) {
char *buf1, *bufDisplayId;
asprintf(&buf1, "%d", keycode);
- asprintf(&bufDisplayId, "%d", displayId);
+ asprintf(&bufDisplayId, "%d", displayId.val());
if (fork() == 0) {
execlp("input", "input", "-d", bufDisplayId, "keyevent", buf1, NULL);
}
@@ -469,12 +482,8 @@
injectTap(101, 101);
surface->expectTap(1, 1);
- surface2->doTransaction([](auto &t, auto &sc) {
- t.setPosition(sc, 100, 100);
- });
- surface->doTransaction([](auto &t, auto &sc) {
- t.setPosition(sc, 200, 200);
- });
+ surface2->doTransaction([](auto& t, auto& sc) { t.setPosition(sc, 100, 100); });
+ surface->doTransaction([](auto& t, auto& sc) { t.setPosition(sc, 200, 200); });
injectTap(101, 101);
surface2->expectTap(1, 1);
@@ -490,23 +499,17 @@
surface->showAt(10, 10);
surface2->showAt(10, 10);
- surface->doTransaction([](auto &t, auto &sc) {
- t.setLayer(sc, LAYER_BASE + 1);
- });
+ surface->doTransaction([](auto& t, auto& sc) { t.setLayer(sc, LAYER_BASE + 1); });
injectTap(11, 11);
surface->expectTap(1, 1);
- surface2->doTransaction([](auto &t, auto &sc) {
- t.setLayer(sc, LAYER_BASE + 1);
- });
+ surface2->doTransaction([](auto& t, auto& sc) { t.setLayer(sc, LAYER_BASE + 1); });
injectTap(11, 11);
surface2->expectTap(1, 1);
- surface2->doTransaction([](auto &t, auto &sc) {
- t.hide(sc);
- });
+ surface2->doTransaction([](auto& t, auto& sc) { t.hide(sc); });
injectTap(11, 11);
surface->expectTap(1, 1);
@@ -555,7 +558,7 @@
childSurface->mInputInfo.surfaceInset = 10;
childSurface->showAt(100, 100);
- childSurface->doTransaction([&](auto &t, auto &sc) {
+ childSurface->doTransaction([&](auto& t, auto& sc) {
t.setPosition(sc, -5, -5);
t.reparent(sc, parentSurface->mSurfaceControl);
});
@@ -576,7 +579,7 @@
fgSurface->mInputInfo.surfaceInset = 5;
fgSurface->showAt(100, 100);
- fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
+ fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 4.0); });
// expect = touch / scale - inset
injectTap(112, 124);
@@ -595,7 +598,7 @@
fgSurface->mInputInfo.surfaceInset = INT32_MAX;
fgSurface->showAt(100, 100);
- fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
+ fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
// expect no crash for overflow, and inset size to be clamped to surface size
injectTap(112, 124);
@@ -644,7 +647,7 @@
fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
fgSurface->showAt(0, 0);
- fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
+ fgSurface->doTransaction([&](auto& t, auto& sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });
// Expect no crash for overflow.
injectTap(12, 24);
@@ -654,7 +657,7 @@
// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
Region transparentRegion(Rect(0, 0, 10, 10));
t.setTransparentRegionHint(sc, transparentRegion);
});
@@ -695,7 +698,7 @@
injectTap(11, 11);
bufferSurface->expectTap(1, 1);
- bufferSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+ bufferSurface->doTransaction([](auto& t, auto& sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -711,7 +714,7 @@
injectTap(11, 11);
fgSurface->expectTap(1, 1);
- fgSurface->doTransaction([](auto &t, auto &sc) { t.setAlpha(sc, 0.0); });
+ fgSurface->doTransaction([](auto& t, auto& sc) { t.setAlpha(sc, 0.0); });
injectTap(11, 11);
fgSurface->expectTap(1, 1);
@@ -728,7 +731,7 @@
injectTap(11, 11);
containerSurface->expectTap(1, 1);
- containerSurface->doTransaction([](auto &t, auto &sc) { t.hide(sc); });
+ containerSurface->doTransaction([](auto& t, auto& sc) { t.hide(sc); });
injectTap(11, 11);
bgSurface->expectTap(1, 1);
@@ -768,19 +771,19 @@
TEST_F(InputSurfacesTest, rotate_surface) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(10, 10);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, 1, -1, 0); // 90 degrees
});
injectTap(8, 11);
surface->expectTap(1, 2);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, -1, 0, 0, -1); // 180 degrees
});
injectTap(9, 8);
surface->expectTap(1, 2);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, -1, 1, 0); // 270 degrees
});
injectTap(12, 9);
@@ -790,19 +793,19 @@
TEST_F(InputSurfacesTest, rotate_surface_with_scale) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(10, 10);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
});
injectTap(2, 12);
surface->expectTap(1, 2);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
});
injectTap(8, 2);
surface->expectTap(1, 2);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
});
injectTap(18, 8);
@@ -814,19 +817,19 @@
surface->mInputInfo.surfaceInset = 5;
surface->showAt(100, 100);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, 2, -4, 0); // 90 degrees
});
injectTap(40, 120);
surface->expectTap(5, 10);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, -2, 0, 0, -4); // 180 degrees
});
injectTap(80, 40);
surface->expectTap(5, 10);
- surface->doTransaction([](auto &t, auto &sc) {
+ surface->doTransaction([](auto& t, auto& sc) {
t.setMatrix(sc, 0, -2, 4, 0); // 270 degrees
});
injectTap(160, 80);
@@ -867,7 +870,7 @@
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(100, 100);
- nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ nonTouchableSurface->doTransaction([&](auto& t, auto& sc) {
t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
@@ -891,7 +894,7 @@
nonTouchableSurface->showAt(0, 0);
parentSurface->showAt(50, 50);
- nonTouchableSurface->doTransaction([&](auto &t, auto &sc) {
+ nonTouchableSurface->doTransaction([&](auto& t, auto& sc) {
t.setCrop(parentSurface->mSurfaceControl, Rect(0, 0, 50, 50));
t.reparent(sc, parentSurface->mSurfaceControl);
});
@@ -933,13 +936,11 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_unobscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction(
- [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
injectTap(101, 101);
-
- EXPECT_NE(surface->consumeEvent(), nullptr);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ surface->expectTap(1, 1);
surface->requestFocus();
surface->assertFocusChange(true);
@@ -949,16 +950,14 @@
TEST_F(InputSurfacesTest, strict_unobscured_input_scaled_without_crop_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) {
+ surface->doTransaction([&](auto& t, auto& sc) {
t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
t.setMatrix(sc, 2.0, 0, 0, 2.0);
});
surface->showAt(100, 100);
injectTap(101, 101);
-
- EXPECT_NE(surface->consumeEvent(), nullptr);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ surface->expectTap(.5, .5);
surface->requestFocus();
surface->assertFocusChange(true);
@@ -970,26 +969,26 @@
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
- [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
obscuringSurface->mInputInfo.ownerUid = gui::Uid{22222};
obscuringSurface->showAt(100, 100);
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
}
TEST_F(InputSurfacesTest, strict_unobscured_input_partially_obscured_window) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->mInputInfo.ownerUid = gui::Uid{11111};
surface->doTransaction(
- [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
+ [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::OBSCURED); });
surface->showAt(100, 100);
std::unique_ptr<InputSurface> obscuringSurface = makeSurface(100, 100);
obscuringSurface->mInputInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, true);
@@ -998,12 +997,12 @@
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
}
TEST_F(InputSurfacesTest, strict_unobscured_input_alpha_window) {
@@ -1012,7 +1011,7 @@
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->showAt(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) {
+ surface->doTransaction([&](auto& t, auto& sc) {
t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
t.reparent(sc, parentSurface->mSurfaceControl);
t.setAlpha(parentSurface->mSurfaceControl, 0.9f);
@@ -1020,12 +1019,12 @@
injectTap(101, 101);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
}
TEST_F(InputSurfacesTest, strict_unobscured_input_cropped_window) {
@@ -1033,7 +1032,7 @@
parentSurface->showAt(0, 0, Rect(0, 0, 300, 300));
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) {
+ surface->doTransaction([&](auto& t, auto& sc) {
t.setDropInputMode(sc, gui::DropInputMode::OBSCURED);
t.reparent(sc, parentSurface->mSurfaceControl);
t.setCrop(parentSurface->mSurfaceControl, Rect(10, 10, 100, 100));
@@ -1042,12 +1041,12 @@
injectTap(111, 111);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
}
TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) {
@@ -1067,17 +1066,16 @@
TEST_F(InputSurfacesTest, drop_input_policy) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction(
- [&](auto &t, auto &sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); });
+ [&](auto& t, auto& sc) { t.setDropInputMode(sc, gui::DropInputMode::ALL); });
surface->showAt(100, 100);
injectTap(101, 101);
-
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
surface->requestFocus();
surface->assertFocusChange(true);
injectKey(AKEYCODE_V);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
}
TEST_F(InputSurfacesTest, layer_with_valid_crop_can_be_focused) {
@@ -1100,7 +1098,7 @@
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
- [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
+ [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
@@ -1112,7 +1110,7 @@
// Does not receive events outside its crop
injectTap(26, 26);
- EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
+ containerSurface->assertNoEvent();
}
/**
@@ -1125,7 +1123,7 @@
std::unique_ptr<InputSurface> containerSurface =
InputSurface::makeContainerInputSurface(mComposerClient, 100, 100);
containerSurface->doTransaction(
- [&](auto &t, auto &sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
+ [&](auto& t, auto& sc) { t.reparent(sc, parentContainer->mSurfaceControl); });
containerSurface->mInputInfo.replaceTouchableRegionWithCrop = true;
containerSurface->mInputInfo.touchableRegionCropHandle = nullptr;
parentContainer->showAt(10, 10, Rect(0, 0, 20, 20));
@@ -1137,7 +1135,7 @@
// Does not receive events outside parent bounds
injectTap(31, 31);
- EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
+ containerSurface->assertNoEvent();
}
/**
@@ -1163,7 +1161,7 @@
// Does not receive events outside crop layer bounds
injectTap(21, 21);
injectTap(71, 71);
- EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
+ containerSurface->assertNoEvent();
}
TEST_F(InputSurfacesTest, child_container_with_no_input_channel_blocks_parent) {
@@ -1177,17 +1175,18 @@
InputSurface::makeContainerInputSurfaceNoInputChannel(mComposerClient, 100, 100);
childContainerSurface->showAt(0, 0);
childContainerSurface->doTransaction(
- [&](auto &t, auto &sc) { t.reparent(sc, parent->mSurfaceControl); });
+ [&](auto& t, auto& sc) { t.reparent(sc, parent->mSurfaceControl); });
injectTap(101, 101);
- EXPECT_EQ(parent->consumeEvent(/*timeout=*/100ms), nullptr);
+ parent->assertNoEvent();
}
class MultiDisplayTests : public InputSurfacesTest {
public:
MultiDisplayTests() : InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
+
void TearDown() override {
- for (auto &token : mVirtualDisplays) {
+ for (auto& token : mVirtualDisplays) {
SurfaceComposerClient::destroyDisplay(token);
}
InputSurfacesTest::TearDown();
@@ -1224,18 +1223,18 @@
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
// Do not create a display associated with the LayerStack.
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+ surface->doTransaction([&](auto& t, auto& sc) { t.setLayerStack(sc, layerStack); });
surface->showAt(100, 100);
// Touches should be dropped if the layer is on an invalid display.
- injectTapOnDisplay(101, 101, layerStack.id);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ injectTapOnDisplay(101, 101, toDisplayId(layerStack));
+ surface->assertNoEvent();
// However, we still let the window be focused and receive keys.
- surface->requestFocus(layerStack.id);
+ surface->requestFocus(toDisplayId(layerStack));
surface->assertFocusChange(true);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ injectKeyOnDisplay(AKEYCODE_V, toDisplayId(layerStack));
surface->expectKey(AKEYCODE_V);
}
@@ -1243,15 +1242,15 @@
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
createDisplay(1000, 1000, false /*isSecure*/, layerStack);
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); });
+ surface->doTransaction([&](auto& t, auto& sc) { t.setLayerStack(sc, layerStack); });
surface->showAt(100, 100);
- injectTapOnDisplay(101, 101, layerStack.id);
+ injectTapOnDisplay(101, 101, toDisplayId(layerStack));
surface->expectTap(1, 1);
- surface->requestFocus(layerStack.id);
+ surface->requestFocus(toDisplayId(layerStack));
surface->assertFocusChange(true);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ injectKeyOnDisplay(AKEYCODE_V, toDisplayId(layerStack));
surface->expectKey(AKEYCODE_V);
}
@@ -1259,20 +1258,20 @@
ui::LayerStack layerStack = ui::LayerStack::fromValue(42);
createDisplay(1000, 1000, false /*isSecure*/, layerStack);
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) {
+ surface->doTransaction([&](auto& t, auto& sc) {
t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
t.setLayerStack(sc, layerStack);
});
surface->showAt(100, 100);
- injectTapOnDisplay(101, 101, layerStack.id);
+ injectTapOnDisplay(101, 101, toDisplayId(layerStack));
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ surface->assertNoEvent();
- surface->requestFocus(layerStack.id);
+ surface->requestFocus(toDisplayId(layerStack));
surface->assertFocusChange(true);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
- EXPECT_EQ(surface->consumeEvent(/*timeout=*/100ms), nullptr);
+ injectKeyOnDisplay(AKEYCODE_V, toDisplayId(layerStack));
+ surface->assertNoEvent();
}
TEST_F(MultiDisplayTests, dont_drop_input_for_secure_layer_on_secure_display) {
@@ -1285,21 +1284,21 @@
seteuid(AID_ROOT);
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
- surface->doTransaction([&](auto &t, auto &sc) {
+ surface->doTransaction([&](auto& t, auto& sc) {
t.setFlags(sc, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
t.setLayerStack(sc, layerStack);
});
surface->showAt(100, 100);
- injectTapOnDisplay(101, 101, layerStack.id);
- EXPECT_NE(surface->consumeEvent(), nullptr);
- EXPECT_NE(surface->consumeEvent(), nullptr);
+ injectTapOnDisplay(101, 101, toDisplayId(layerStack));
+ surface->expectTap(1, 1);
- surface->requestFocus(layerStack.id);
+ surface->requestFocus(toDisplayId(layerStack));
surface->assertFocusChange(true);
- injectKeyOnDisplay(AKEYCODE_V, layerStack.id);
+ injectKeyOnDisplay(AKEYCODE_V, toDisplayId(layerStack));
surface->expectKey(AKEYCODE_V);
}
-} // namespace android::test
+} // namespace test
+} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 8a7ecab..eee4fb9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -173,7 +173,7 @@
// Acquire and free 1+extraDiscardedBuffers buffer, check onBufferReleased is called.
std::vector<BufferItem> releasedItems;
releasedItems.resize(1+extraDiscardedBuffers);
- for (int i = 0; i < releasedItems.size(); i++) {
+ for (size_t i = 0; i < releasedItems.size(); i++) {
ASSERT_EQ(NO_ERROR, consumer->acquireBuffer(&releasedItems[i], 0));
ASSERT_EQ(NO_ERROR, consumer->releaseBuffer(releasedItems[i].mSlot,
releasedItems[i].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR,
@@ -197,7 +197,7 @@
// Check onBufferDiscarded is called with correct buffer
auto discardedBuffers = listener->getDiscardedBuffers();
ASSERT_EQ(discardedBuffers.size(), releasedItems.size());
- for (int i = 0; i < releasedItems.size(); i++) {
+ for (size_t i = 0; i < releasedItems.size(); i++) {
ASSERT_EQ(discardedBuffers[i], releasedItems[i].mGraphicBuffer);
}
@@ -815,10 +815,6 @@
return binder::Status::ok();
}
- binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* /*outLayers*/) override {
- return binder::Status::ok();
- }
-
binder::Status getCompositionPreference(gui::CompositionPreference* /*outPref*/) override {
return binder::Status::ok();
}
diff --git a/libs/gui/tests/WindowInfo_test.cpp b/libs/gui/tests/WindowInfo_test.cpp
index 5eb5d3b..ce22082 100644
--- a/libs/gui/tests/WindowInfo_test.cpp
+++ b/libs/gui/tests/WindowInfo_test.cpp
@@ -64,7 +64,7 @@
i.ownerUid = gui::Uid{24};
i.packageName = "com.example.package";
i.inputConfig = WindowInfo::InputConfig::NOT_FOCUSABLE;
- i.displayId = 34;
+ i.displayId = ui::LogicalDisplayId{34};
i.replaceTouchableRegionWithCrop = true;
i.touchableRegionCropHandle = touchableRegionCropHandle;
i.applicationInfo.name = "ApplicationFooBar";
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 3278c23..cc0649c 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -30,6 +30,7 @@
"android/os/InputEventInjectionResult.aidl",
"android/os/InputEventInjectionSync.aidl",
"android/os/InputConfig.aidl",
+ "android/os/PointerIconType.aidl",
],
}
@@ -88,11 +89,6 @@
bindgen_flags: [
"--verbose",
- "--allowlist-var=AMOTION_EVENT_FLAG_CANCELED",
- "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED",
- "--allowlist-var=AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED",
- "--allowlist-var=AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT",
- "--allowlist-var=AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE",
"--allowlist-var=AMOTION_EVENT_ACTION_CANCEL",
"--allowlist-var=AMOTION_EVENT_ACTION_UP",
"--allowlist-var=AMOTION_EVENT_ACTION_POINTER_DOWN",
@@ -245,6 +241,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"liblog",
"libPlatformProperties",
@@ -288,17 +285,6 @@
target: {
android: {
- export_shared_lib_headers: ["libbinder"],
-
- shared_libs: [
- "libutils",
- "libbinder",
- // Stats logging library and its dependencies.
- "libstatslog_libinput",
- "libstatsbootstrap",
- "android.os.statsbootstrap_aidl-cpp",
- ],
-
required: [
"motion_predictor_model_prebuilt",
"motion_predictor_model_config",
@@ -313,43 +299,6 @@
},
}
-// Use bootstrap version of stats logging library.
-// libinput is a bootstrap process (starts early in the boot process), and thus can't use the normal
-// `libstatslog` because that requires `libstatssocket`, which is only available later in the boot.
-cc_library {
- name: "libstatslog_libinput",
- generated_sources: ["statslog_libinput.cpp"],
- generated_headers: ["statslog_libinput.h"],
- export_generated_headers: ["statslog_libinput.h"],
- shared_libs: [
- "libbinder",
- "libstatsbootstrap",
- "libutils",
- "android.os.statsbootstrap_aidl-cpp",
- ],
-}
-
-genrule {
- name: "statslog_libinput.h",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_libinput.h --module libinput" +
- " --namespace android,stats,libinput --bootstrap",
- out: [
- "statslog_libinput.h",
- ],
-}
-
-genrule {
- name: "statslog_libinput.cpp",
- tools: ["stats-log-api-gen"],
- cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_libinput.cpp --module libinput" +
- " --namespace android,stats,libinput --importHeader statslog_libinput.h" +
- " --bootstrap",
- out: [
- "statslog_libinput.cpp",
- ],
-}
-
cc_defaults {
name: "libinput_fuzz_defaults",
cpp_std: "c++20",
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 61a964e..d271563 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -27,7 +27,6 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
-#include <gui/constants.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
@@ -293,8 +292,8 @@
event.getButtonState()};
}
-void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
- std::array<uint8_t, 32> hmac) {
+void InputEvent::initialize(int32_t id, int32_t deviceId, uint32_t source,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac) {
mId = id;
mDeviceId = deviceId;
mSource = source;
@@ -356,10 +355,11 @@
return InputEventLookup::getKeyCodeByLabel(label);
}
-void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
- std::array<uint8_t, 32> hmac, int32_t action, int32_t flags,
- int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
- nsecs_t downTime, nsecs_t eventTime) {
+void KeyEvent::initialize(int32_t id, int32_t deviceId, uint32_t source,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime,
+ nsecs_t eventTime) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
mAction = action;
mFlags = flags;
@@ -556,14 +556,15 @@
// --- MotionEvent ---
-void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int32_t displayId,
- std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton,
- int32_t flags, int32_t edgeFlags, int32_t metaState,
- int32_t buttonState, MotionClassification classification,
- const ui::Transform& transform, float xPrecision, float yPrecision,
- float rawXCursorPosition, float rawYCursorPosition,
- const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
- size_t pointerCount, const PointerProperties* pointerProperties,
+void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags,
+ int32_t metaState, int32_t buttonState,
+ MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float rawXCursorPosition,
+ float rawYCursorPosition, const ui::Transform& rawTransform,
+ nsecs_t downTime, nsecs_t eventTime, size_t pointerCount,
+ const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
InputEvent::initialize(id, deviceId, source, displayId, hmac);
mAction = action;
@@ -835,7 +836,7 @@
mId = parcel->readInt32();
mDeviceId = parcel->readInt32();
mSource = parcel->readUint32();
- mDisplayId = parcel->readInt32();
+ mDisplayId = ui::LogicalDisplayId{parcel->readInt32()};
std::vector<uint8_t> hmac;
status_t result = parcel->readByteVector(&hmac);
if (result != OK || hmac.size() != 32) {
@@ -903,7 +904,7 @@
parcel->writeInt32(mId);
parcel->writeInt32(mDeviceId);
parcel->writeUint32(mSource);
- parcel->writeInt32(mDisplayId);
+ parcel->writeInt32(mDisplayId.val());
std::vector<uint8_t> hmac(mHmac.begin(), mHmac.end());
parcel->writeByteVector(hmac);
parcel->writeInt32(mAction);
@@ -1203,7 +1204,7 @@
void FocusEvent::initialize(int32_t id, bool hasFocus) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE, INVALID_HMAC);
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
}
@@ -1216,7 +1217,7 @@
void CaptureEvent::initialize(int32_t id, bool pointerCaptureEnabled) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE, INVALID_HMAC);
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC);
mPointerCaptureEnabled = pointerCaptureEnabled;
}
@@ -1229,7 +1230,7 @@
void DragEvent::initialize(int32_t id, float x, float y, bool isExiting) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE, INVALID_HMAC);
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC);
mIsExiting = isExiting;
mX = x;
mY = y;
@@ -1246,7 +1247,7 @@
void TouchModeEvent::initialize(int32_t id, bool isInTouchMode) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
- ADISPLAY_ID_NONE, INVALID_HMAC);
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC);
mIsInTouchMode = isInTouchMode;
}
diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp
index e0d874e..abc0392 100644
--- a/libs/input/InputConsumer.cpp
+++ b/libs/input/InputConsumer.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <cstdint>
#define LOG_TAG "InputTransport"
#define ATRACE_TAG ATRACE_TAG_INPUT
@@ -80,10 +81,10 @@
void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) {
event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
- msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action,
- msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode,
- msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime,
- msg.body.key.eventTime);
+ ui::LogicalDisplayId{msg.body.key.displayId}, msg.body.key.hmac,
+ msg.body.key.action, msg.body.key.flags, msg.body.key.keyCode,
+ msg.body.key.scanCode, msg.body.key.metaState, msg.body.key.repeatCount,
+ msg.body.key.downTime, msg.body.key.eventTime);
}
void initializeFocusEvent(FocusEvent& event, const InputMessage& msg) {
@@ -116,13 +117,14 @@
msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw,
0, 0, 1});
event.initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
- msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action,
- msg.body.motion.actionButton, msg.body.motion.flags, msg.body.motion.edgeFlags,
- msg.body.motion.metaState, msg.body.motion.buttonState,
- msg.body.motion.classification, transform, msg.body.motion.xPrecision,
- msg.body.motion.yPrecision, msg.body.motion.xCursorPosition,
- msg.body.motion.yCursorPosition, displayTransform, msg.body.motion.downTime,
- msg.body.motion.eventTime, pointerCount, pointerProperties, pointerCoords);
+ ui::LogicalDisplayId{msg.body.motion.displayId}, msg.body.motion.hmac,
+ msg.body.motion.action, msg.body.motion.actionButton, msg.body.motion.flags,
+ msg.body.motion.edgeFlags, msg.body.motion.metaState,
+ msg.body.motion.buttonState, msg.body.motion.classification, transform,
+ msg.body.motion.xPrecision, msg.body.motion.yPrecision,
+ msg.body.motion.xCursorPosition, msg.body.motion.yCursorPosition,
+ displayTransform, msg.body.motion.downTime, msg.body.motion.eventTime,
+ pointerCount, pointerProperties, pointerCoords);
}
void addSample(MotionEvent& event, const InputMessage& msg) {
@@ -194,9 +196,21 @@
InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel,
bool enableTouchResampling)
- : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {}
+ : mResampleTouch(enableTouchResampling),
+ mChannel(channel),
+ mProcessingTraceTag(StringPrintf("InputConsumer processing on %s (%p)",
+ mChannel->getName().c_str(), this)),
+ mLifetimeTraceTag(StringPrintf("InputConsumer lifetime on %s (%p)",
+ mChannel->getName().c_str(), this)),
+ mLifetimeTraceCookie(
+ static_cast<int32_t>(reinterpret_cast<std::uintptr_t>(this) & 0xFFFFFFFF)),
+ mMsgDeferred(false) {
+ ATRACE_ASYNC_BEGIN(mLifetimeTraceTag.c_str(), /*cookie=*/mLifetimeTraceCookie);
+}
-InputConsumer::~InputConsumer() {}
+InputConsumer::~InputConsumer() {
+ ATRACE_ASYNC_END(mLifetimeTraceTag.c_str(), /*cookie=*/mLifetimeTraceCookie);
+}
bool InputConsumer::isTouchResamplingEnabled() {
return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
@@ -228,7 +242,7 @@
mMsg.header.seq);
// Trace the event processing timeline - event was just read from the socket
- ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq);
+ ATRACE_ASYNC_BEGIN(mProcessingTraceTag.c_str(), /*cookie=*/mMsg.header.seq);
}
if (result) {
// Consume the next batched event unless batches are being held for later.
@@ -325,9 +339,10 @@
case InputMessage::Type::FINISHED:
case InputMessage::Type::TIMELINE: {
- LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
- "InputConsumer!",
- ftl::enum_string(mMsg.header.type).c_str());
+ LOG(FATAL) << "Consumed a " << ftl::enum_string(mMsg.header.type)
+ << " message, which should never be seen by "
+ "InputConsumer on "
+ << mChannel->getName();
break;
}
@@ -768,7 +783,7 @@
popConsumeTime(seq);
// Trace the event processing timeline - event was just finished
- ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq);
+ ATRACE_ASYNC_END(mProcessingTraceTag.c_str(), /*cookie=*/seq);
}
return result;
}
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
index 52acb51..15d992f 100644
--- a/libs/input/InputConsumerNoResampling.cpp
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -47,10 +47,10 @@
std::unique_ptr<KeyEvent> createKeyEvent(const InputMessage& msg) {
std::unique_ptr<KeyEvent> event = std::make_unique<KeyEvent>();
event->initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
- msg.body.key.displayId, msg.body.key.hmac, msg.body.key.action,
- msg.body.key.flags, msg.body.key.keyCode, msg.body.key.scanCode,
- msg.body.key.metaState, msg.body.key.repeatCount, msg.body.key.downTime,
- msg.body.key.eventTime);
+ ui::LogicalDisplayId{msg.body.key.displayId}, msg.body.key.hmac,
+ msg.body.key.action, msg.body.key.flags, msg.body.key.keyCode,
+ msg.body.key.scanCode, msg.body.key.metaState, msg.body.key.repeatCount,
+ msg.body.key.downTime, msg.body.key.eventTime);
return event;
}
@@ -93,8 +93,8 @@
msg.body.motion.dtdyRaw, msg.body.motion.dsdyRaw, msg.body.motion.tyRaw,
0, 0, 1});
event->initialize(msg.body.motion.eventId, msg.body.motion.deviceId, msg.body.motion.source,
- msg.body.motion.displayId, msg.body.motion.hmac, msg.body.motion.action,
- msg.body.motion.actionButton, msg.body.motion.flags,
+ ui::LogicalDisplayId{msg.body.motion.displayId}, msg.body.motion.hmac,
+ msg.body.motion.action, msg.body.motion.actionButton, msg.body.motion.flags,
msg.body.motion.edgeFlags, msg.body.motion.metaState,
msg.body.motion.buttonState, msg.body.motion.classification, transform,
msg.body.motion.xPrecision, msg.body.motion.yPrecision,
@@ -413,10 +413,9 @@
case InputMessage::Type::FINISHED:
case InputMessage::Type::TIMELINE: {
- LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by "
- "InputConsumer on %s",
- ftl::enum_string(msg.header.type).c_str(),
- mChannel->getName().c_str());
+ LOG(FATAL) << "Consumed a " << ftl::enum_string(msg.header.type)
+ << " message, which should never be seen by InputConsumer on "
+ << mChannel->getName();
break;
}
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index c348833..50239a1 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -23,7 +23,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <ftl/enum.h>
-#include <gui/constants.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -170,7 +169,7 @@
// --- InputDeviceInfo ---
InputDeviceInfo::InputDeviceInfo() {
- initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false, ADISPLAY_ID_NONE);
+ initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false, ui::ADISPLAY_ID_NONE);
}
InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other)
@@ -187,6 +186,7 @@
mKeyCharacterMap(other.mKeyCharacterMap),
mUsiVersion(other.mUsiVersion),
mAssociatedDisplayId(other.mAssociatedDisplayId),
+ mEnabled(other.mEnabled),
mHasVibrator(other.mHasVibrator),
mHasBattery(other.mHasBattery),
mHasButtonUnderPad(other.mHasButtonUnderPad),
@@ -201,8 +201,9 @@
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,
- InputDeviceViewBehavior viewBehavior) {
+ bool isExternal, bool hasMic,
+ ui::LogicalDisplayId associatedDisplayId,
+ InputDeviceViewBehavior viewBehavior, bool enabled) {
mId = id;
mGeneration = generation;
mControllerNumber = controllerNumber;
@@ -213,6 +214,7 @@
mSources = 0;
mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE;
mAssociatedDisplayId = associatedDisplayId;
+ mEnabled = enabled;
mHasVibrator = false;
mHasBattery = false;
mHasButtonUnderPad = false;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 1869483..47b4228 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -387,9 +387,9 @@
status_t InputChannel::sendMessage(const InputMessage* msg) {
ATRACE_NAME_IF(ATRACE_ENABLED(),
- StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=0x%" PRIx32
- ")",
- name.c_str(), msg->header.seq, msg->header.type));
+ StringPrintf("sendMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=%s)",
+ name.c_str(), msg->header.seq,
+ ftl::enum_string(msg->header.type).c_str()));
const size_t msgLength = msg->size();
InputMessage cleanMsg;
msg->getSanitizedCopy(&cleanMsg);
@@ -458,9 +458,10 @@
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 ")",
- name.c_str(), msg->header.seq, msg->header.type);
+ std::string message =
+ StringPrintf("receiveMessage(inputChannel=%s, seq=0x%" PRIx32 ", type=%s)",
+ name.c_str(), msg->header.seq,
+ ftl::enum_string(msg->header.type).c_str());
ATRACE_NAME(message.c_str());
}
return OK;
@@ -529,7 +530,7 @@
}
status_t InputPublisher::publishKeyEvent(uint32_t seq, int32_t eventId, int32_t deviceId,
- int32_t source, int32_t displayId,
+ int32_t source, ui::LogicalDisplayId displayId,
std::array<uint8_t, 32> hmac, int32_t action,
int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, int32_t repeatCount, nsecs_t downTime,
@@ -557,7 +558,7 @@
msg.body.key.eventId = eventId;
msg.body.key.deviceId = deviceId;
msg.body.key.source = source;
- msg.body.key.displayId = displayId;
+ msg.body.key.displayId = displayId.val();
msg.body.key.hmac = std::move(hmac);
msg.body.key.action = action;
msg.body.key.flags = flags;
@@ -571,11 +572,11 @@
}
status_t InputPublisher::publishMotionEvent(
- uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source, int32_t displayId,
- std::array<uint8_t, 32> hmac, int32_t action, int32_t actionButton, int32_t flags,
- int32_t edgeFlags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, const ui::Transform& transform, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition,
+ uint32_t seq, int32_t eventId, int32_t deviceId, int32_t source,
+ ui::LogicalDisplayId displayId, std::array<uint8_t, 32> hmac, int32_t action,
+ int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, const ui::Transform& transform,
+ float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords) {
@@ -595,13 +596,13 @@
std::string transformString;
transform.dump(transformString, "transform", " ");
ALOGD("channel '%s' publisher ~ %s: seq=%u, id=%d, deviceId=%d, source=%s, "
- "displayId=%" PRId32 ", "
+ "displayId=%s, "
"action=%s, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
"metaState=0x%x, buttonState=0x%x, classification=%s,"
"xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
"pointerCount=%" PRIu32 "\n%s",
mChannel->getName().c_str(), __func__, seq, eventId, deviceId,
- inputEventSourceToString(source).c_str(), displayId,
+ inputEventSourceToString(source).c_str(), displayId.toString().c_str(),
MotionEvent::actionToString(action).c_str(), actionButton, flags, edgeFlags,
metaState, buttonState, motionClassificationToString(classification), xPrecision,
yPrecision, downTime, eventTime, pointerCount, transformString.c_str());
@@ -624,7 +625,7 @@
msg.body.motion.eventId = eventId;
msg.body.motion.deviceId = deviceId;
msg.body.motion.source = source;
- msg.body.motion.displayId = displayId;
+ msg.body.motion.displayId = displayId.val();
msg.body.motion.hmac = std::move(hmac);
msg.body.motion.action = action;
msg.body.motion.actionButton = actionButton;
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e2feabc..41909bf 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -28,7 +28,6 @@
#include <input/KeyCharacterMap.h>
#include <input/Keyboard.h>
-#include <gui/constants.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/Timers.h>
@@ -496,11 +495,11 @@
return false;
}
-void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents,
- int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time) {
+void KeyCharacterMap::addKey(Vector<KeyEvent>& outEvents, int32_t deviceId, int32_t keyCode,
+ int32_t metaState, bool down, nsecs_t time) {
outEvents.push();
KeyEvent& event = outEvents.editTop();
- event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE,
INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode,
0, metaState, 0, time, time);
}
diff --git a/libs/input/MotionPredictor.cpp b/libs/input/MotionPredictor.cpp
index c4e3ff6..5b61d39 100644
--- a/libs/input/MotionPredictor.cpp
+++ b/libs/input/MotionPredictor.cpp
@@ -18,21 +18,29 @@
#include <input/MotionPredictor.h>
+#include <algorithm>
+#include <array>
#include <cinttypes>
#include <cmath>
#include <cstddef>
#include <cstdint>
+#include <limits>
+#include <optional>
#include <string>
+#include <utility>
#include <vector>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/input.h>
+#include <com_android_input_flags.h>
#include <attestation/HmacKeyManager.h>
#include <ftl/enum.h>
#include <input/TfLiteMotionPredictor.h>
+namespace input_flags = com::android::input::flags;
+
namespace android {
namespace {
@@ -55,8 +63,73 @@
return {.x = axisTo.x + x_delta, .y = axisTo.y + y_delta};
}
+float normalizeRange(float x, float min, float max) {
+ const float normalized = (x - min) / (max - min);
+ return std::min(1.0f, std::max(0.0f, normalized));
+}
+
} // namespace
+// --- JerkTracker ---
+
+JerkTracker::JerkTracker(bool normalizedDt) : mNormalizedDt(normalizedDt) {}
+
+void JerkTracker::pushSample(int64_t timestamp, float xPos, float yPos) {
+ mTimestamps.pushBack(timestamp);
+ const int numSamples = mTimestamps.size();
+
+ std::array<float, 4> newXDerivatives;
+ std::array<float, 4> newYDerivatives;
+
+ /**
+ * Diagram showing the calculation of higher order derivatives of sample x3
+ * collected at time=t3.
+ * Terms in parentheses are not stored (and not needed for calculations)
+ * t0 ----- t1 ----- t2 ----- t3
+ * (x0)-----(x1) ----- x2 ----- x3
+ * (x'0) --- x'1 --- x'2
+ * x''0 - x''1
+ * x'''0
+ *
+ * In this example:
+ * x'2 = (x3 - x2) / (t3 - t2)
+ * x''1 = (x'2 - x'1) / (t2 - t1)
+ * x'''0 = (x''1 - x''0) / (t1 - t0)
+ * Therefore, timestamp history is needed to calculate higher order derivatives,
+ * compared to just the last calculated derivative sample.
+ *
+ * If mNormalizedDt = true, then dt = 1 and the division is moot.
+ */
+ for (int i = 0; i < numSamples; ++i) {
+ if (i == 0) {
+ newXDerivatives[i] = xPos;
+ newYDerivatives[i] = yPos;
+ } else {
+ newXDerivatives[i] = newXDerivatives[i - 1] - mXDerivatives[i - 1];
+ newYDerivatives[i] = newYDerivatives[i - 1] - mYDerivatives[i - 1];
+ if (!mNormalizedDt) {
+ const float dt = mTimestamps[numSamples - i] - mTimestamps[numSamples - i - 1];
+ newXDerivatives[i] = newXDerivatives[i] / dt;
+ newYDerivatives[i] = newYDerivatives[i] / dt;
+ }
+ }
+ }
+
+ std::swap(newXDerivatives, mXDerivatives);
+ std::swap(newYDerivatives, mYDerivatives);
+}
+
+void JerkTracker::reset() {
+ mTimestamps.clear();
+}
+
+std::optional<float> JerkTracker::jerkMagnitude() const {
+ if (mTimestamps.size() == mTimestamps.capacity()) {
+ return std::hypot(mXDerivatives[3], mYDerivatives[3]);
+ }
+ return std::nullopt;
+}
+
// --- MotionPredictor ---
MotionPredictor::MotionPredictor(nsecs_t predictionTimestampOffsetNanos,
@@ -103,6 +176,7 @@
if (action == AMOTION_EVENT_ACTION_UP || action == AMOTION_EVENT_ACTION_CANCEL) {
ALOGD_IF(isDebug(), "End of event stream");
mBuffers->reset();
+ mJerkTracker.reset();
mLastEvent.reset();
return {};
} else if (action != AMOTION_EVENT_ACTION_DOWN && action != AMOTION_EVENT_ACTION_MOVE) {
@@ -137,6 +211,9 @@
0, i),
.orientation = event.getHistoricalOrientation(0, i),
});
+ mJerkTracker.pushSample(event.getHistoricalEventTime(i),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords->getAxisValue(AMOTION_EVENT_AXIS_Y));
}
if (!mLastEvent) {
@@ -184,6 +261,17 @@
int64_t predictionTime = mBuffers->lastTimestamp();
const int64_t futureTime = timestamp + mPredictionTimestampOffsetNanos;
+ const float jerkMagnitude = mJerkTracker.jerkMagnitude().value_or(0);
+ const float fractionKept =
+ 1 - normalizeRange(jerkMagnitude, mModel->config().lowJerk, mModel->config().highJerk);
+ // float to ensure proper division below.
+ const float predictionTimeWindow = futureTime - predictionTime;
+ const int maxNumPredictions = static_cast<int>(
+ std::ceil(predictionTimeWindow / mModel->config().predictionInterval * fractionKept));
+ ALOGD_IF(isDebug(),
+ "jerk (d^3p/normalizedDt^3): %f, fraction of prediction window pruned: %f, max number "
+ "of predictions: %d",
+ jerkMagnitude, 1 - fractionKept, maxNumPredictions);
for (size_t i = 0; i < static_cast<size_t>(predictedR.size()) && predictionTime <= futureTime;
++i) {
if (predictedR[i] < mModel->config().distanceNoiseFloor) {
@@ -197,7 +285,13 @@
// device starts to speed up, but avoids producing noisy predictions as it slows down.
break;
}
- // TODO(b/266747654): Stop predictions if confidence is < some threshold.
+ if (input_flags::enable_prediction_pruning_via_jerk_thresholding()) {
+ if (i >= static_cast<size_t>(maxNumPredictions)) {
+ break;
+ }
+ }
+ // TODO(b/266747654): Stop predictions if confidence is < some
+ // threshold. Currently predictions are pruned via jerk thresholding.
const TfLiteMotionPredictorSample::Point predictedPoint =
convertPrediction(axisFrom, axisTo, predictedR[i], predictedPhi[i]);
diff --git a/libs/input/MotionPredictorMetricsManager.cpp b/libs/input/MotionPredictorMetricsManager.cpp
index 6872af2..cda39ce 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -25,10 +25,6 @@
#include "Eigen/Core"
#include "Eigen/Geometry"
-#ifdef __ANDROID__
-#include <statslog_libinput.h>
-#endif
-
namespace android {
namespace {
@@ -48,22 +44,9 @@
void MotionPredictorMetricsManager::defaultReportAtomFunction(
const MotionPredictorMetricsManager::AtomFields& atomFields) {
- // Call stats_write logging function only on Android targets (not supported on host).
-#ifdef __ANDROID__
- android::stats::libinput::
- stats_write(android::stats::libinput::STYLUS_PREDICTION_METRICS_REPORTED,
- /*stylus_vendor_id=*/0,
- /*stylus_product_id=*/0,
- atomFields.deltaTimeBucketMilliseconds,
- atomFields.alongTrajectoryErrorMeanMillipixels,
- atomFields.alongTrajectoryErrorStdMillipixels,
- atomFields.offTrajectoryRmseMillipixels,
- atomFields.pressureRmseMilliunits,
- atomFields.highVelocityAlongTrajectoryRmse,
- atomFields.highVelocityOffTrajectoryRmse,
- atomFields.scaleInvariantAlongTrajectoryRmse,
- atomFields.scaleInvariantOffTrajectoryRmse);
-#endif
+ // TODO(b/338106546): Fix bootanimation build dependency issue, then re-add
+ // the stats_write function call here.
+ (void)atomFields;
}
MotionPredictorMetricsManager::MotionPredictorMetricsManager(
diff --git a/libs/input/TfLiteMotionPredictor.cpp b/libs/input/TfLiteMotionPredictor.cpp
index d17476e..b843a4b 100644
--- a/libs/input/TfLiteMotionPredictor.cpp
+++ b/libs/input/TfLiteMotionPredictor.cpp
@@ -281,6 +281,8 @@
Config config{
.predictionInterval = parseXMLInt64(*configRoot, "prediction-interval"),
.distanceNoiseFloor = parseXMLFloat(*configRoot, "distance-noise-floor"),
+ .lowJerk = parseXMLFloat(*configRoot, "low-jerk"),
+ .highJerk = parseXMLFloat(*configRoot, "high-jerk"),
};
return std::unique_ptr<TfLiteMotionPredictorModel>(
diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl
index 8f6f95b..90ed2b7 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -49,12 +49,105 @@
const int POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000;
/**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event directly passed through
+ * the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ */
+ const int MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED = 0x1;
+
+ /**
+ * This flag indicates that the window that received this motion event is partly
+ * or wholly obscured by another visible window above it and the event did not directly pass
+ * through the obscured area.
+ *
+ * A security sensitive application can check this flag to identify situations in which
+ * a malicious application may have covered up part of its content for the purpose
+ * of misleading the user or hijacking touches. An appropriate response might be
+ * to drop the suspect touches or to take additional precautions to confirm the user's
+ * actual intent.
+ *
+ * Unlike FLAG_WINDOW_IS_OBSCURED, this is only true if the window that received this event is
+ * obstructed in areas other than the touched location.
+ */
+ const int MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED = 0x2;
+
+ /**
+ * This private flag is only set on {@link #ACTION_HOVER_MOVE} events and indicates that
+ * this event will be immediately followed by a {@link #ACTION_HOVER_EXIT}. It is used to
+ * prevent generating redundant {@link #ACTION_HOVER_ENTER} events.
+ * @hide
+ */
+ const int MOTION_EVENT_FLAG_HOVER_EXIT_PENDING = 0x4;
+
+ /**
+ * This flag indicates that the event has been generated by a gesture generator. It
+ * provides a hint to the GestureDetector to not apply any touch slop.
+ *
+ * @hide
+ */
+ const int MOTION_EVENT_FLAG_IS_GENERATED_GESTURE = 0x8;
+
+ /**
+ * This flag is only set for events with {@link #ACTION_POINTER_UP} and {@link #ACTION_CANCEL}.
+ * It indicates that the pointer going up was an unintentional user touch. When FLAG_CANCELED
+ * is set, the typical actions that occur in response for a pointer going up (such as click
+ * handlers, end of drawing) should be aborted. This flag is typically set when the user was
+ * accidentally touching the screen, such as by gripping the device, or placing the palm on the
+ * screen.
+ *
+ * @see #ACTION_POINTER_UP
+ * @see #ACTION_CANCEL
+ */
+ const int INPUT_EVENT_FLAG_CANCELED = 0x20;
+
+ /**
+ * This flag indicates that the event will not cause a focus change if it is directed to an
+ * unfocused window, even if it an {@link #ACTION_DOWN}. This is typically used with pointer
+ * gestures to allow the user to direct gestures to an unfocused window without bringing the
+ * window into focus.
+ * @hide
+ */
+ const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40;
+
+ /**
* The input event was generated or modified by accessibility service.
* Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either
* set of flags, including in input/Input.h and in android/input.h.
*/
const int INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800;
+ /**
+ * Private flag that indicates when the system has detected that this motion event
+ * may be inconsistent with respect to the sequence of previously delivered motion events,
+ * such as when a pointer move event is sent but the pointer is not down.
+ *
+ * @hide
+ * @see #isTainted
+ * @see #setTainted
+ */
+ const int INPUT_EVENT_FLAG_TAINTED = 0x80000000;
+
+ /**
+ * Private flag indicating that this event was synthesized by the system and should be delivered
+ * to the accessibility focused view first. When being dispatched such an event is not handled
+ * by predecessors of the accessibility focused view and after the event reaches that view the
+ * flag is cleared and normal event dispatch is performed. This ensures that the platform can
+ * click on any view that has accessibility focus which is semantically equivalent to asking the
+ * view to perform a click accessibility action but more generic as views not implementing click
+ * action correctly can still be activated.
+ *
+ * @hide
+ * @see #isTargetAccessibilityFocus()
+ * @see #setTargetAccessibilityFocus(boolean)
+ */
+ const int MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS = 0x40000000;
+
/* The default pointer acceleration value. */
const int DEFAULT_POINTER_ACCELERATION = 3;
diff --git a/libs/input/android/os/InputConfig.aidl b/libs/input/android/os/InputConfig.aidl
index 5d39155..da62e03 100644
--- a/libs/input/android/os/InputConfig.aidl
+++ b/libs/input/android/os/InputConfig.aidl
@@ -157,4 +157,14 @@
* like StatusBar and TaskBar.
*/
GLOBAL_STYLUS_BLOCKS_TOUCH = 1 << 17,
+
+ /**
+ * InputConfig used to indicate that this window is privacy sensitive. This may be used to
+ * redact input interactions from tracing or screen mirroring.
+ *
+ * This must be set on windows that use {@link WindowManager.LayoutParams#FLAG_SECURE},
+ * but it may also be set without setting FLAG_SECURE. The tracing configuration will
+ * determine how these sensitive events are eventually traced.
+ */
+ SENSITIVE_FOR_PRIVACY = 1 << 18,
}
diff --git a/libs/input/android/os/PointerIconType.aidl b/libs/input/android/os/PointerIconType.aidl
new file mode 100644
index 0000000..f244c62
--- /dev/null
+++ b/libs/input/android/os/PointerIconType.aidl
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2024, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Represents an icon that can be used as a mouse pointer.
+ * Please look at frameworks/base/core/java/android/view/PointerIcon.java for the detailed
+ * explanation of each constant.
+ * @hide
+ */
+@Backing(type="int")
+enum PointerIconType {
+ CUSTOM = -1,
+ TYPE_NULL = 0,
+ NOT_SPECIFIED = 1,
+ ARROW = 1000,
+ CONTEXT_MENU = 1001,
+ HAND = 1002,
+ HELP = 1003,
+ WAIT = 1004,
+ CELL = 1006,
+ CROSSHAIR = 1007,
+ TEXT = 1008,
+ VERTICAL_TEXT = 1009,
+ ALIAS = 1010,
+ COPY = 1011,
+ NO_DROP = 1012,
+ ALL_SCROLL = 1013,
+ HORIZONTAL_DOUBLE_ARROW = 1014,
+ VERTICAL_DOUBLE_ARROW = 1015,
+ TOP_RIGHT_DOUBLE_ARROW = 1016,
+ TOP_LEFT_DOUBLE_ARROW = 1017,
+ ZOOM_IN = 1018,
+ ZOOM_OUT = 1019,
+ GRAB = 1020,
+ GRABBING = 1021,
+ HANDWRITING = 1022,
+
+ SPOT_HOVER = 2000,
+ SPOT_TOUCH = 2001,
+ SPOT_ANCHOR = 2002,
+}
diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig
index b48b0fb..560166c 100644
--- a/libs/input/input_flags.aconfig
+++ b/libs/input/input_flags.aconfig
@@ -16,13 +16,6 @@
}
flag {
- name: "enable_pointer_choreographer"
- namespace: "input"
- description: "Set to true to enable PointerChoreographer: the new pipeline for showing pointer icons"
- bug: "293587049"
-}
-
-flag {
name: "enable_gestures_library_timer_provider"
namespace: "input"
description: "Set to true to enable timer support for the touchpad Gestures library"
@@ -128,3 +121,32 @@
description: "Enable fling scrolling to be stopped by putting a finger on the touchpad again"
bug: "281106755"
}
+
+flag {
+ name: "enable_prediction_pruning_via_jerk_thresholding"
+ namespace: "input"
+ description: "Enable prediction pruning based on jerk thresholds."
+ bug: "266747654"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "device_associations"
+ namespace: "input"
+ description: "Binds InputDevice name and InputDevice description against display unique id."
+ bug: "324075859"
+}
+
+flag {
+ name: "enable_multi_device_same_window_stream"
+ namespace: "input"
+ description: "Allow multiple input devices to be active in the same window simultaneously"
+ bug: "330752824"
+}
+
+flag {
+ name: "hide_pointer_indicators_for_secure_windows"
+ namespace: "input"
+ description: "Hide touch and pointer indicators if a secure window is present on display"
+ bug: "325252005"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 705c959..d0dbd6f 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -17,6 +17,15 @@
//! Common definitions of the Android Input Framework in rust.
use bitflags::bitflags;
+use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_CANCELED;
+use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+use inputconstants::aidl::android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+use inputconstants::aidl::android::os::IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
use std::fmt;
/// The InputDevice ID.
@@ -182,18 +191,24 @@
/// MotionEvent flags.
#[derive(Debug)]
pub struct MotionFlags: u32 {
- /// FLAG_CANCELED
- const CANCELED = input_bindgen::AMOTION_EVENT_FLAG_CANCELED as u32;
/// FLAG_WINDOW_IS_OBSCURED
- const WINDOW_IS_OBSCURED = input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ const WINDOW_IS_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_OBSCURED as u32;
/// FLAG_WINDOW_IS_PARTIALLY_OBSCURED
- const WINDOW_IS_PARTIALLY_OBSCURED =
- input_bindgen::AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- /// FLAG_IS_ACCESSIBILITY_EVENT
- const IS_ACCESSIBILITY_EVENT =
- input_bindgen::AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT;
+ const WINDOW_IS_PARTIALLY_OBSCURED = MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32;
+ /// FLAG_HOVER_EXIT_PENDING
+ const HOVER_EXIT_PENDING = MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
+ /// FLAG_IS_GENERATED_GESTURE
+ const IS_GENERATED_GESTURE = MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
+ /// FLAG_CANCELED
+ const CANCELED = INPUT_EVENT_FLAG_CANCELED as u32;
/// FLAG_NO_FOCUS_CHANGE
- const NO_FOCUS_CHANGE = input_bindgen::AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE;
+ const NO_FOCUS_CHANGE = MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+ /// FLAG_IS_ACCESSIBILITY_EVENT
+ const IS_ACCESSIBILITY_EVENT = INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
+ /// FLAG_TAINTED
+ const TAINTED = INPUT_EVENT_FLAG_TAINTED as u32;
+ /// FLAG_TARGET_ACCESSIBILITY_FOCUS
+ const TARGET_ACCESSIBILITY_FOCUS = MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
}
}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 01d9599..fb3f520 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -79,12 +79,20 @@
pointer_properties: &[RustPointerProperties],
flags: u32,
) -> String {
+ let motion_flags = MotionFlags::from_bits(flags);
+ if motion_flags.is_none() {
+ panic!(
+ "The conversion of flags 0x{:08x} failed, please check if some flags have not been \
+ added to MotionFlags.",
+ flags
+ );
+ }
let result = verifier.process_movement(
DeviceId(device_id),
Source::from_bits(source).unwrap(),
action,
pointer_properties,
- MotionFlags::from_bits(flags).unwrap(),
+ motion_flags.unwrap(),
);
match result {
Ok(()) => "".to_string(),
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index e67a65a..6e724ac 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,6 +36,7 @@
"tensorflow_headers",
],
static_libs: [
+ "libflagtest",
"libgmock",
"libgui_window_info_static",
"libinput",
@@ -64,6 +65,7 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libstatslog",
"libtinyxml2",
"libutils",
"server_configurable_flags",
@@ -82,14 +84,6 @@
address: true,
},
},
- android: {
- static_libs: [
- // Stats logging library and its dependencies.
- "libstatslog_libinput",
- "libstatsbootstrap",
- "android.os.statsbootstrap_aidl-cpp",
- ],
- },
},
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 0df06b7..cc2574d 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -21,14 +21,13 @@
#include <attestation/HmacKeyManager.h>
#include <binder/Parcel.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <input/InputEventBuilders.h>
namespace android {
// Default display id.
-static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
@@ -229,7 +228,7 @@
ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set display id.
- constexpr int32_t newDisplayId = 2;
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
event.setDisplayId(newDisplayId);
ASSERT_EQ(newDisplayId, event.getDisplayId());
}
@@ -528,7 +527,7 @@
ASSERT_EQ(AINPUT_SOURCE_JOYSTICK, event.getSource());
// Set displayId.
- constexpr int32_t newDisplayId = 2;
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
event.setDisplayId(newDisplayId);
ASSERT_EQ(newDisplayId, event.getDisplayId());
@@ -860,7 +859,7 @@
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
- /* displayId */ 0, INVALID_HMAC, action,
+ /* displayId */ ui::ADISPLAY_ID_DEFAULT, INVALID_HMAC, action,
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
index 6593497..7ae1cd8 100644
--- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -18,7 +18,6 @@
#include <attestation/HmacKeyManager.h>
#include <ftl/enum.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/BlockingQueue.h>
#include <input/InputConsumerNoResampling.h>
#include <input/InputTransport.h>
@@ -56,7 +55,7 @@
const int32_t eventId;
const int32_t deviceId = 1;
const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
- const int32_t displayId = ADISPLAY_ID_DEFAULT;
+ const ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT;
const int32_t actionButton = 0;
const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
@@ -446,7 +445,7 @@
int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
- constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT;
constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
@@ -738,9 +737,10 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
@@ -755,9 +755,10 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
@@ -776,9 +777,10 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 332831f..d0dbe2a 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -16,7 +16,6 @@
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/InputConsumer.h>
#include <input/InputTransport.h>
@@ -49,7 +48,7 @@
const int32_t eventId;
const int32_t deviceId = 1;
const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
- const int32_t displayId = ADISPLAY_ID_DEFAULT;
+ const ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT;
const int32_t actionButton = 0;
const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP;
const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
@@ -263,7 +262,7 @@
int32_t eventId = InputEvent::nextId();
constexpr int32_t deviceId = 1;
constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
- constexpr int32_t displayId = ADISPLAY_ID_DEFAULT;
+ constexpr ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_DEFAULT;
constexpr std::array<uint8_t, 32> hmac = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10,
9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
@@ -623,13 +622,13 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
- ASSERT_EQ(BAD_VALUE, status)
- << "publisher publishMotionEvent should return BAD_VALUE";
+ ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
}
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
@@ -640,17 +639,17 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
- ASSERT_EQ(BAD_VALUE, status)
- << "publisher publishMotionEvent should return BAD_VALUE";
+ ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
}
TEST_F(InputPublisherAndConsumerTest,
- PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+ PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
status_t status;
const size_t pointerCount = MAX_POINTERS + 1;
PointerProperties pointerProperties[pointerCount];
@@ -662,13 +661,13 @@
ui::Transform identityTransform;
status =
- mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0,
- 0, 0, 0, MotionClassification::NONE, identityTransform,
- 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, ui::ADISPLAY_ID_DEFAULT,
+ INVALID_HMAC, 0, 0, 0, 0, 0, 0,
+ MotionClassification::NONE, identityTransform, 0, 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
0, 0, pointerCount, pointerProperties, pointerCoords);
- ASSERT_EQ(BAD_VALUE, status)
- << "publisher publishMotionEvent should return BAD_VALUE";
+ ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE";
}
TEST_F(InputPublisherAndConsumerTest, PublishMultipleEvents_EndToEnd) {
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 3343114..1c9f0c7 100644
--- a/libs/input/tests/MotionPredictor_test.cpp
+++ b/libs/input/tests/MotionPredictor_test.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+// TODO(b/331815574): Decouple this test from assumed config values.
#include <chrono>
+#include <cmath>
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <input/MotionPredictor.h>
@@ -55,8 +58,8 @@
}
ui::Transform identityTransform;
- event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT, {0},
- action, /*actionButton=*/0, /*flags=*/0, AMOTION_EVENT_EDGE_FLAG_NONE,
+ event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_STYLUS, ui::ADISPLAY_ID_DEFAULT,
+ {0}, action, /*actionButton=*/0, /*flags=*/0, AMOTION_EVENT_EDGE_FLAG_NONE,
AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, identityTransform,
/*xPrecision=*/0.1,
/*yPrecision=*/0.2, /*xCursorPosition=*/280, /*yCursorPosition=*/540,
@@ -65,6 +68,108 @@
return event;
}
+TEST(JerkTrackerTest, JerkReadiness) {
+ JerkTracker jerkTracker(true);
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+ jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+ jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+ jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+ jerkTracker.pushSample(/*timestamp=*/3, 35, 70);
+ EXPECT_TRUE(jerkTracker.jerkMagnitude());
+ jerkTracker.reset();
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+ jerkTracker.pushSample(/*timestamp=*/4, 30, 60);
+ EXPECT_FALSE(jerkTracker.jerkMagnitude());
+}
+
+TEST(JerkTrackerTest, JerkCalculationNormalizedDtTrue) {
+ JerkTracker jerkTracker(true);
+ jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
+ jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
+ jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
+ jerkTracker.pushSample(/*timestamp=*/3, 45, 70);
+ /**
+ * Jerk derivative table
+ * x: 20 25 30 45
+ * x': 5 5 15
+ * x'': 0 10
+ * x''': 10
+ *
+ * y: 50 53 60 70
+ * y': 3 7 10
+ * y'': 4 3
+ * y''': -1
+ */
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(10, -1));
+ jerkTracker.pushSample(/*timestamp=*/4, 20, 65);
+ /**
+ * (continuing from above table)
+ * x: 45 -> 20
+ * x': 15 -> -25
+ * x'': 10 -> -40
+ * x''': -50
+ *
+ * y: 70 -> 65
+ * y': 10 -> -5
+ * y'': 3 -> -15
+ * y''': -18
+ */
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-50, -18));
+}
+
+TEST(JerkTrackerTest, JerkCalculationNormalizedDtFalse) {
+ JerkTracker jerkTracker(false);
+ jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
+ jerkTracker.pushSample(/*timestamp=*/10, 25, 53);
+ jerkTracker.pushSample(/*timestamp=*/20, 30, 60);
+ jerkTracker.pushSample(/*timestamp=*/30, 45, 70);
+ /**
+ * Jerk derivative table
+ * x: 20 25 30 45
+ * x': .5 .5 1.5
+ * x'': 0 .1
+ * x''': .01
+ *
+ * y: 50 53 60 70
+ * y': .3 .7 1
+ * y'': .04 .03
+ * y''': -.001
+ */
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(.01, -.001));
+ jerkTracker.pushSample(/*timestamp=*/50, 20, 65);
+ /**
+ * (continuing from above table)
+ * x: 45 -> 20
+ * x': 1.5 -> -1.25 (delta above, divide by 20)
+ * x'': .1 -> -.275 (delta above, divide by 10)
+ * x''': -.0375 (delta above, divide by 10)
+ *
+ * y: 70 -> 65
+ * y': 1 -> -.25 (delta above, divide by 20)
+ * y'': .03 -> -.125 (delta above, divide by 10)
+ * y''': -.0155 (delta above, divide by 10)
+ */
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(-.0375, -.0155));
+}
+
+TEST(JerkTrackerTest, JerkCalculationAfterReset) {
+ JerkTracker jerkTracker(true);
+ jerkTracker.pushSample(/*timestamp=*/0, 20, 50);
+ jerkTracker.pushSample(/*timestamp=*/1, 25, 53);
+ jerkTracker.pushSample(/*timestamp=*/2, 30, 60);
+ jerkTracker.pushSample(/*timestamp=*/3, 45, 70);
+ jerkTracker.pushSample(/*timestamp=*/4, 20, 65);
+ jerkTracker.reset();
+ jerkTracker.pushSample(/*timestamp=*/5, 20, 50);
+ jerkTracker.pushSample(/*timestamp=*/6, 25, 53);
+ jerkTracker.pushSample(/*timestamp=*/7, 30, 60);
+ jerkTracker.pushSample(/*timestamp=*/8, 45, 70);
+ EXPECT_FLOAT_EQ(jerkTracker.jerkMagnitude().value(), std::hypot(10, -1));
+}
+
TEST(MotionPredictorTest, IsPredictionAvailable) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
@@ -94,18 +199,14 @@
TEST(MotionPredictorTest, FollowsGesture) {
MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
[]() { return true /*enable prediction*/; });
+ predictor.record(getMotionEvent(DOWN, 3.75, 3, 20ms));
+ predictor.record(getMotionEvent(MOVE, 4.8, 3, 30ms));
+ predictor.record(getMotionEvent(MOVE, 6.2, 3, 40ms));
+ predictor.record(getMotionEvent(MOVE, 8, 3, 50ms));
+ EXPECT_NE(nullptr, predictor.predict(90 * NSEC_PER_MSEC));
- // MOVE without a DOWN is ignored.
- predictor.record(getMotionEvent(MOVE, 1, 3, 10ms));
- EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
-
- predictor.record(getMotionEvent(DOWN, 2, 5, 20ms));
- predictor.record(getMotionEvent(MOVE, 2, 7, 30ms));
- predictor.record(getMotionEvent(MOVE, 3, 9, 40ms));
- EXPECT_NE(nullptr, predictor.predict(50 * NSEC_PER_MSEC));
-
- predictor.record(getMotionEvent(UP, 4, 11, 50ms));
- EXPECT_EQ(nullptr, predictor.predict(20 * NSEC_PER_MSEC));
+ predictor.record(getMotionEvent(UP, 10.25, 3, 60ms));
+ EXPECT_EQ(nullptr, predictor.predict(100 * NSEC_PER_MSEC));
}
TEST(MotionPredictorTest, MultipleDevicesNotSupported) {
@@ -147,6 +248,63 @@
ASSERT_FALSE(predictor.isPredictionAvailable(/*deviceId=*/1, AINPUT_SOURCE_TOUCHSCREEN));
}
+TEST_WITH_FLAGS(
+ MotionPredictorTest, LowJerkNoPruning,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ enable_prediction_pruning_via_jerk_thresholding))) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; });
+
+ // Jerk is low (0.05 normalized).
+ predictor.record(getMotionEvent(DOWN, 2, 7, 20ms));
+ predictor.record(getMotionEvent(MOVE, 2.75, 7, 30ms));
+ predictor.record(getMotionEvent(MOVE, 3.8, 7, 40ms));
+ predictor.record(getMotionEvent(MOVE, 5.2, 7, 50ms));
+ predictor.record(getMotionEvent(MOVE, 7, 7, 60ms));
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(90 * NSEC_PER_MSEC);
+ EXPECT_NE(nullptr, predicted);
+ EXPECT_EQ(static_cast<size_t>(5), predicted->getHistorySize() + 1);
+}
+
+TEST_WITH_FLAGS(
+ MotionPredictorTest, HighJerkPredictionsPruned,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ enable_prediction_pruning_via_jerk_thresholding))) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; });
+
+ // Jerk is incredibly high.
+ predictor.record(getMotionEvent(DOWN, 0, 5, 20ms));
+ predictor.record(getMotionEvent(MOVE, 0, 70, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, 139, 40ms));
+ predictor.record(getMotionEvent(MOVE, 0, 1421, 50ms));
+ predictor.record(getMotionEvent(MOVE, 0, 41233, 60ms));
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(90 * NSEC_PER_MSEC);
+ EXPECT_EQ(nullptr, predicted);
+}
+
+TEST_WITH_FLAGS(
+ MotionPredictorTest, MediumJerkPredictionsSomePruned,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ enable_prediction_pruning_via_jerk_thresholding))) {
+ MotionPredictor predictor(/*predictionTimestampOffsetNanos=*/0,
+ []() { return true /*enable prediction*/; });
+
+ // Jerk is medium (1.05 normalized, which is halfway between LOW_JANK and HIGH_JANK)
+ predictor.record(getMotionEvent(DOWN, 0, 5.2, 20ms));
+ predictor.record(getMotionEvent(MOVE, 0, 11.5, 30ms));
+ predictor.record(getMotionEvent(MOVE, 0, 22, 40ms));
+ predictor.record(getMotionEvent(MOVE, 0, 37.75, 50ms));
+ predictor.record(getMotionEvent(MOVE, 0, 59.8, 60ms));
+ std::unique_ptr<MotionEvent> predicted = predictor.predict(82 * NSEC_PER_MSEC);
+ EXPECT_NE(nullptr, predicted);
+ // Halfway between LOW_JANK and HIGH_JANK means that half of the predictions
+ // will be pruned. If model prediction window is close enough to predict()
+ // call time window, then half of the model predictions (5/2 -> 2) will be
+ // ouputted.
+ EXPECT_EQ(static_cast<size_t>(3), predicted->getHistorySize() + 1);
+}
+
using AtomFields = MotionPredictorMetricsManager::AtomFields;
using ReportAtomFunction = MotionPredictorMetricsManager::ReportAtomFunction;
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 6e23d4e..1694cad 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -84,7 +84,8 @@
ADD_FAILURE() << "Downtime should be equal to 0 (hardcoded for convenience)";
}
return mPublisher->publishMotionEvent(mSeq++, InputEvent::nextId(), /*deviceId=*/1,
- AINPUT_SOURCE_TOUCHSCREEN, /*displayId=*/0, INVALID_HMAC,
+ AINPUT_SOURCE_TOUCHSCREEN,
+ /*displayId=*/ui::ADISPLAY_ID_DEFAULT, INVALID_HMAC,
action, /*actionButton=*/0, /*flags=*/0, /*edgeFlags=*/0,
AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
identityTransform, /*xPrecision=*/0, /*yPrecision=*/0,
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index f9ca280..b4c4e12 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -24,7 +24,6 @@
#include <android-base/stringprintf.h>
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/VelocityTracker.h>
using std::literals::chrono_literals::operator""ms;
@@ -34,7 +33,7 @@
namespace android {
-constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT; // default display id
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT; // default display id
constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests
@@ -156,7 +155,7 @@
MotionEvent event;
ui::Transform identityTransform;
event.initialize(InputEvent::nextId(), /*deviceId=*/5, AINPUT_SOURCE_ROTARY_ENCODER,
- ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC, AMOTION_EVENT_ACTION_SCROLL,
/*actionButton=*/0, /*flags=*/0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
/*buttonState=*/0, MotionClassification::NONE, identityTransform,
/*xPrecision=*/0, /*yPrecision=*/0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp
index 277d74d..40cfaae 100644
--- a/libs/input/tests/VerifiedInputEvent_test.cpp
+++ b/libs/input/tests/VerifiedInputEvent_test.cpp
@@ -16,7 +16,6 @@
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/Input.h>
namespace android {
@@ -24,7 +23,7 @@
static KeyEvent getKeyEventWithFlags(int32_t flags) {
KeyEvent event;
event.initialize(InputEvent::nextId(), /*deviceId=*/2, AINPUT_SOURCE_GAMEPAD,
- ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags,
+ ui::ADISPLAY_ID_DEFAULT, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, flags,
AKEYCODE_BUTTON_X, /*scanCode=*/121, AMETA_ALT_ON, /*repeatCount=*/1,
/*downTime=*/1000, /*eventTime=*/2000);
return event;
@@ -44,10 +43,11 @@
ui::Transform transform;
transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1});
ui::Transform identity;
- event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT,
- INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0, flags,
- AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
- MotionClassification::NONE, transform, /*xPrecision=*/0.1, /*yPrecision=*/0.2,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_MOUSE,
+ ui::ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ /*actionButton=*/0, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE,
+ /*buttonState=*/0, MotionClassification::NONE, transform, /*xPrecision=*/0.1,
+ /*yPrecision=*/0.2,
/*xCursorPosition=*/280, /*yCursorPosition=*/540, identity, /*downTime=*/100,
/*eventTime=*/200, pointerCount, pointerProperties, pointerCoords);
return event;
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 969a5cf..33c303a 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -41,6 +41,8 @@
#include <system/graphics.h>
#include <unistd.h>
+#include <vndk/hardware_buffer.h>
+
// system/window.h is a superset of the vndk and apex apis
#include <apex/window.h>
#include <vndk/window.h>
@@ -257,6 +259,7 @@
NATIVE_WINDOW_SET_QUERY_INTERCEPTOR = 47, /* private */
NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO = 48, /* private */
NATIVE_WINDOW_GET_LAST_QUEUED_BUFFER2 = 49, /* private */
+ NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS = 50,
// clang-format on
};
@@ -1182,6 +1185,26 @@
return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineInfo);
}
+/**
+ * native_window_set_buffers_additional_options(..., ExtendableType* additionalOptions, size_t size)
+ * All buffers dequeued after this call will have the additionalOptions specified.
+ *
+ * This must only be called after api_connect, otherwise NO_INIT is returned. The options are
+ * cleared in api_disconnect & api_connect
+ *
+ * If IAllocator is not v2 or newer this method returns INVALID_OPERATION
+ *
+ * \return NO_ERROR on success.
+ * \return NO_INIT if no api is connected
+ * \return INVALID_OPERATION if additional option support is not available
+ */
+static inline int native_window_set_buffers_additional_options(
+ struct ANativeWindow* window, const AHardwareBufferLongOptions* additionalOptions,
+ size_t additionalOptionsSize) {
+ return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_ADDITIONAL_OPTIONS, additionalOptions,
+ additionalOptionsSize);
+}
+
// ------------------------------------------------------------------------------------------------
// Candidates for APEX visibility
// These functions are planned to be made stable for APEX modules, but have not
diff --git a/libs/nativewindow/rust/Android.bp b/libs/nativewindow/rust/Android.bp
index a3df482..97740db 100644
--- a/libs/nativewindow/rust/Android.bp
+++ b/libs/nativewindow/rust/Android.bp
@@ -54,6 +54,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_library {
@@ -78,6 +82,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_test {
@@ -116,6 +124,10 @@
},
min_sdk_version: "VanillaIceCream",
vendor_available: true,
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
}
rust_test {
diff --git a/libs/nativewindow/rust/src/lib.rs b/libs/nativewindow/rust/src/lib.rs
index 22ad834..dc3f51f 100644
--- a/libs/nativewindow/rust/src/lib.rs
+++ b/libs/nativewindow/rust/src/lib.rs
@@ -16,7 +16,8 @@
extern crate nativewindow_bindgen as ffi;
-pub mod surface;
+mod surface;
+pub use surface::Surface;
pub use ffi::{AHardwareBuffer_Format, AHardwareBuffer_UsageFlags};
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 0defc7e..c003111 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -84,6 +84,7 @@
"skia/Cache.cpp",
"skia/ColorSpaces.cpp",
"skia/GaneshVkRenderEngine.cpp",
+ "skia/GraphiteVkRenderEngine.cpp",
"skia/GLExtensions.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
@@ -91,6 +92,8 @@
"skia/VulkanInterface.cpp",
"skia/compat/GaneshBackendTexture.cpp",
"skia/compat/GaneshGpuContext.cpp",
+ "skia/compat/GraphiteBackendTexture.cpp",
+ "skia/compat/GraphiteGpuContext.cpp",
"skia/debug/CaptureTimer.cpp",
"skia/debug/CommonPool.cpp",
"skia/debug/SkiaCapture.cpp",
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 233134d..1c60563 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -16,40 +16,45 @@
#include <renderengine/RenderEngine.h>
-#include <cutils/properties.h>
-#include <log/log.h>
#include "renderengine/ExternalTexture.h"
+#include "skia/GaneshVkRenderEngine.h"
+#include "skia/GraphiteVkRenderEngine.h"
+#include "skia/SkiaGLRenderEngine.h"
#include "threaded/RenderEngineThreaded.h"
-#include "skia/SkiaGLRenderEngine.h"
-#include "skia/SkiaVkRenderEngine.h"
+#include <cutils/properties.h>
+#include <log/log.h>
namespace android {
namespace renderengine {
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
- if (args.threaded == Threaded::YES) {
- switch (args.graphicsApi) {
- case GraphicsApi::GL:
- ALOGD("Threaded RenderEngine with SkiaGL Backend");
- return renderengine::threaded::RenderEngineThreaded::create([args]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(args);
- });
- case GraphicsApi::VK:
- ALOGD("Threaded RenderEngine with SkiaVK Backend");
- return renderengine::threaded::RenderEngineThreaded::create([args]() {
- return android::renderengine::skia::SkiaVkRenderEngine::create(args);
- });
+ threaded::CreateInstanceFactory createInstanceFactory;
+
+ ALOGD("%sRenderEngine with %s Backend (%s)", args.threaded == Threaded::YES ? "Threaded " : "",
+ args.graphicsApi == GraphicsApi::GL ? "SkiaGL" : "SkiaVK",
+ args.skiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite");
+
+ if (args.skiaBackend == SkiaBackend::GRAPHITE) {
+ createInstanceFactory = [args]() {
+ return android::renderengine::skia::GraphiteVkRenderEngine::create(args);
+ };
+ } else { // GANESH
+ if (args.graphicsApi == GraphicsApi::VK) {
+ createInstanceFactory = [args]() {
+ return android::renderengine::skia::GaneshVkRenderEngine::create(args);
+ };
+ } else { // GL
+ createInstanceFactory = [args]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(args);
+ };
}
}
- switch (args.graphicsApi) {
- case GraphicsApi::GL:
- ALOGD("RenderEngine with SkiaGL Backend");
- return renderengine::skia::SkiaGLRenderEngine::create(args);
- case GraphicsApi::VK:
- ALOGD("RenderEngine with SkiaVK Backend");
- return renderengine::skia::SkiaVkRenderEngine::create(args);
+ if (args.threaded == Threaded::YES) {
+ return renderengine::threaded::RenderEngineThreaded::create(createInstanceFactory);
+ } else {
+ return createInstanceFactory();
}
}
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
index 101f519..05a2063 100644
--- a/libs/renderengine/benchmark/RenderEngineBench.cpp
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -71,7 +71,7 @@
.setImageCacheSize(1)
.setEnableProtectedContext(true)
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
+ .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
.setContextPriority(RenderEngine::ContextPriority::REALTIME)
.setThreaded(threaded)
.setGraphicsApi(graphicsApi)
diff --git a/libs/renderengine/include/renderengine/BorderRenderInfo.h b/libs/renderengine/include/renderengine/BorderRenderInfo.h
deleted file mode 100644
index 0ee6661..0000000
--- a/libs/renderengine/include/renderengine/BorderRenderInfo.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-#include <math/mat4.h>
-#include <ui/Region.h>
-
-namespace android {
-namespace renderengine {
-
-struct BorderRenderInfo {
- float width = 0;
- half4 color;
- Region combinedRegion;
-
- bool operator==(const BorderRenderInfo& rhs) const {
- return (width == rhs.width && color == rhs.color &&
- combinedRegion.hasSameRects(rhs.combinedRegion));
- }
-};
-
-} // namespace renderengine
-} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 8d7c13c..deb6253 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -22,7 +22,6 @@
#include <math/mat4.h>
#include <renderengine/PrintMatrix.h>
-#include <renderengine/BorderRenderInfo.h>
#include <ui/DisplayId.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -87,8 +86,6 @@
// Configures the rendering intent of the output display. This is used for tonemapping.
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent =
aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC;
-
- std::vector<renderengine::BorderRenderInfo> borderInfoList;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
@@ -100,8 +97,7 @@
lhs.deviceHandlesColorTransform == rhs.deviceHandlesColorTransform &&
lhs.orientation == rhs.orientation &&
lhs.targetLuminanceNits == rhs.targetLuminanceNits &&
- lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent &&
- lhs.borderInfoList == rhs.borderInfoList;
+ lhs.dimmingStage == rhs.dimmingStage && lhs.renderIntent == rhs.renderIntent;
}
static const char* orientation_to_string(uint32_t orientation) {
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index de05268..980d913 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -50,6 +50,11 @@
#define PROPERTY_DEBUG_RENDERENGINE_CAPTURE_FILENAME "debug.renderengine.capture_filename"
/**
+ * Switches the cross-window background blur algorithm.
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM "debug.renderengine.blur_algorithm"
+
+/**
* Allows recording of Skia drawing commands with systrace.
*/
#define PROPERTY_SKIA_ATRACE_ENABLED "debug.renderengine.skia_atrace_enabled"
@@ -102,9 +107,37 @@
VK,
};
+ enum class SkiaBackend {
+ GANESH,
+ GRAPHITE,
+ };
+
+ enum class BlurAlgorithm {
+ NONE,
+ GAUSSIAN,
+ KAWASE,
+ };
+
static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
- static bool canSupport(GraphicsApi);
+ // Check if the device supports the given GraphicsApi.
+ //
+ // If called for GraphicsApi::VK then underlying (unprotected) VK resources will be preserved
+ // to optimize subsequent VK initialization, but teardown(GraphicsApi::VK) must be invoked if
+ // the caller subsequently decides to NOT use VK.
+ //
+ // The first call may require significant resource initialization, but subsequent checks are
+ // cached internally.
+ static bool canSupport(GraphicsApi graphicsApi);
+
+ // Teardown any GPU API resources that were previously initialized but are no longer needed.
+ //
+ // Must be called with GraphicsApi::VK if canSupport(GraphicsApi::VK) was previously invoked but
+ // the caller subsequently decided to not use VK.
+ //
+ // This is safe to call if there is nothing to teardown, but NOT safe to call if a RenderEngine
+ // instance exists. The RenderEngine destructor will handle its own teardown logic.
+ static void teardown(GraphicsApi graphicsApi);
virtual ~RenderEngine() = 0;
@@ -253,10 +286,11 @@
bool useColorManagement;
bool enableProtectedContext;
bool precacheToneMapperShaderOnly;
- bool supportsBackgroundBlur;
+ RenderEngine::BlurAlgorithm blurAlgorithm;
RenderEngine::ContextPriority contextPriority;
RenderEngine::Threaded threaded;
RenderEngine::GraphicsApi graphicsApi;
+ RenderEngine::SkiaBackend skiaBackend;
struct Builder;
@@ -264,18 +298,20 @@
// must be created by Builder via constructor with full argument list
RenderEngineCreationArgs(int _pixelFormat, uint32_t _imageCacheSize,
bool _enableProtectedContext, bool _precacheToneMapperShaderOnly,
- bool _supportsBackgroundBlur,
+ RenderEngine::BlurAlgorithm _blurAlgorithm,
RenderEngine::ContextPriority _contextPriority,
RenderEngine::Threaded _threaded,
- RenderEngine::GraphicsApi _graphicsApi)
+ RenderEngine::GraphicsApi _graphicsApi,
+ RenderEngine::SkiaBackend _skiaBackend)
: pixelFormat(_pixelFormat),
imageCacheSize(_imageCacheSize),
enableProtectedContext(_enableProtectedContext),
precacheToneMapperShaderOnly(_precacheToneMapperShaderOnly),
- supportsBackgroundBlur(_supportsBackgroundBlur),
+ blurAlgorithm(_blurAlgorithm),
contextPriority(_contextPriority),
threaded(_threaded),
- graphicsApi(_graphicsApi) {}
+ graphicsApi(_graphicsApi),
+ skiaBackend(_skiaBackend) {}
RenderEngineCreationArgs() = delete;
};
@@ -298,8 +334,8 @@
this->precacheToneMapperShaderOnly = precacheToneMapperShaderOnly;
return *this;
}
- Builder& setSupportsBackgroundBlur(bool supportsBackgroundBlur) {
- this->supportsBackgroundBlur = supportsBackgroundBlur;
+ Builder& setBlurAlgorithm(RenderEngine::BlurAlgorithm blurAlgorithm) {
+ this->blurAlgorithm = blurAlgorithm;
return *this;
}
Builder& setContextPriority(RenderEngine::ContextPriority contextPriority) {
@@ -314,10 +350,14 @@
this->graphicsApi = graphicsApi;
return *this;
}
+ Builder& setSkiaBackend(RenderEngine::SkiaBackend skiaBackend) {
+ this->skiaBackend = skiaBackend;
+ return *this;
+ }
RenderEngineCreationArgs build() const {
return RenderEngineCreationArgs(pixelFormat, imageCacheSize, enableProtectedContext,
- precacheToneMapperShaderOnly, supportsBackgroundBlur,
- contextPriority, threaded, graphicsApi);
+ precacheToneMapperShaderOnly, blurAlgorithm,
+ contextPriority, threaded, graphicsApi, skiaBackend);
}
private:
@@ -326,10 +366,11 @@
uint32_t imageCacheSize = 0;
bool enableProtectedContext = false;
bool precacheToneMapperShaderOnly = false;
- bool supportsBackgroundBlur = false;
+ RenderEngine::BlurAlgorithm blurAlgorithm = RenderEngine::BlurAlgorithm::NONE;
RenderEngine::ContextPriority contextPriority = RenderEngine::ContextPriority::MEDIUM;
RenderEngine::Threaded threaded = RenderEngine::Threaded::YES;
RenderEngine::GraphicsApi graphicsApi = RenderEngine::GraphicsApi::GL;
+ RenderEngine::SkiaBackend skiaBackend = RenderEngine::SkiaBackend::GANESH;
};
} // namespace renderengine
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
index e76a4c3..68798bf 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.cpp
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
@@ -23,9 +23,26 @@
#include <log/log_main.h>
#include <sync/sync.h>
+#include <utils/Trace.h>
namespace android::renderengine::skia {
+std::unique_ptr<GaneshVkRenderEngine> GaneshVkRenderEngine::create(
+ const RenderEngineCreationArgs& args) {
+ std::unique_ptr<GaneshVkRenderEngine> engine(new GaneshVkRenderEngine(args));
+ engine->ensureContextsCreated();
+
+ if (getVulkanInterface(false).isInitialized()) {
+ ALOGD("GaneshVkRenderEngine::%s: successfully initialized GaneshVkRenderEngine", __func__);
+ return engine;
+ } else {
+ ALOGE("GaneshVkRenderEngine::%s: could not create GaneshVkRenderEngine. "
+ "Likely insufficient Vulkan support",
+ __func__);
+ return {};
+ }
+}
+
// Ganesh-specific function signature for fFinishedProc callback.
static void unref_semaphore(void* semaphore) {
SkiaVkRenderEngine::DestroySemaphoreInfo* info =
@@ -33,6 +50,12 @@
info->unref();
}
+std::unique_ptr<SkiaGpuContext> GaneshVkRenderEngine::createContext(
+ VulkanInterface& vulkanInterface) {
+ return SkiaGpuContext::MakeVulkan_Ganesh(vulkanInterface.getGaneshBackendContext(),
+ mSkSLCacheMonitor);
+}
+
void GaneshVkRenderEngine::waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) {
if (fenceFd.get() < 0) return;
@@ -51,11 +74,18 @@
context->grDirectContext()->wait(1, &beSemaphore, kDeleteAfterWait);
}
-base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context) {
+base::unique_fd GaneshVkRenderEngine::flushAndSubmit(SkiaGpuContext* context,
+ sk_sp<SkSurface> dstSurface) {
sk_sp<GrDirectContext> grContext = context->grDirectContext();
+ {
+ ATRACE_NAME("flush surface");
+ // TODO: Investigate feasibility of combining this "surface flush" into the "context flush"
+ // below.
+ context->grDirectContext()->flush(dstSurface.get());
+ }
+
VulkanInterface& vi = getVulkanInterface(isProtected());
VkSemaphore semaphore = vi.createExportableSemaphore();
-
GrBackendSemaphore backendSemaphore = GrBackendSemaphores::MakeVk(semaphore);
GrFlushInfo flushInfo;
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
index 90e2487..e6123c2 100644
--- a/libs/renderengine/skia/GaneshVkRenderEngine.h
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -21,14 +21,16 @@
namespace android::renderengine::skia {
class GaneshVkRenderEngine : public SkiaVkRenderEngine {
- friend std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
- const RenderEngineCreationArgs& args);
+public:
+ static std::unique_ptr<GaneshVkRenderEngine> create(const RenderEngineCreationArgs& args);
protected:
- GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
-
+ std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
- base::unique_fd flushAndSubmit(SkiaGpuContext* context) override;
+ base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+
+private:
+ GaneshVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
};
} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.cpp b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
new file mode 100644
index 0000000..b5cb21b
--- /dev/null
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 "GraphiteVkRenderEngine.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/graphite/BackendSemaphore.h>
+#include <include/gpu/graphite/Context.h>
+#include <include/gpu/graphite/Recording.h>
+
+#include <log/log_main.h>
+#include <sync/sync.h>
+
+#include <memory>
+#include <vector>
+
+namespace android::renderengine::skia {
+
+std::unique_ptr<GraphiteVkRenderEngine> GraphiteVkRenderEngine::create(
+ const RenderEngineCreationArgs& args) {
+ std::unique_ptr<GraphiteVkRenderEngine> engine(new GraphiteVkRenderEngine(args));
+ engine->ensureContextsCreated();
+
+ if (getVulkanInterface(false).isInitialized()) {
+ ALOGD("GraphiteVkRenderEngine::%s: successfully initialized GraphiteVkRenderEngine",
+ __func__);
+ return engine;
+ } else {
+ ALOGE("GraphiteVkRenderEngine::%s: could not create GraphiteVkRenderEngine. "
+ "Likely insufficient Vulkan support",
+ __func__);
+ return {};
+ }
+}
+
+// Graphite-specific function signature for fFinishedProc callback.
+static void unref_semaphore(void* semaphore, skgpu::CallbackResult result) {
+ if (result != skgpu::CallbackResult::kSuccess) {
+ ALOGE("Graphite submission of work to GPU failed, check for Skia errors");
+ }
+ SkiaVkRenderEngine::DestroySemaphoreInfo* info =
+ reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore);
+ info->unref();
+}
+
+std::unique_ptr<SkiaGpuContext> GraphiteVkRenderEngine::createContext(
+ VulkanInterface& vulkanInterface) {
+ return SkiaGpuContext::MakeVulkan_Graphite(vulkanInterface.getGraphiteBackendContext());
+}
+
+void GraphiteVkRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) {
+ if (fenceFd.get() < 0) return;
+
+ int dupedFd = dup(fenceFd.get());
+ if (dupedFd < 0) {
+ ALOGE("failed to create duplicate fence fd: %d", dupedFd);
+ sync_wait(fenceFd.get(), -1);
+ return;
+ }
+
+ base::unique_fd fenceDup(dupedFd);
+ VkSemaphore waitSemaphore =
+ getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release());
+ graphite::BackendSemaphore beSemaphore(waitSemaphore);
+ mStagedWaitSemaphores.push_back(beSemaphore);
+}
+
+base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface>) {
+ // Minimal Recording setup. Required even if there are no incoming semaphores to wait on, and if
+ // creating the outgoing signaling semaphore fails.
+ std::unique_ptr<graphite::Recording> recording = context->graphiteRecorder()->snap();
+ graphite::InsertRecordingInfo insertInfo;
+ insertInfo.fRecording = recording.get();
+
+ VulkanInterface& vulkanInterface = getVulkanInterface(isProtected());
+ // This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism
+ // as "wait" semaphores from waitFence.
+ VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore();
+ graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore);
+
+ // Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work.
+ std::vector<VkSemaphore> vkSemaphoresToCleanUp;
+ if (vkSignalSemaphore != VK_NULL_HANDLE) {
+ vkSemaphoresToCleanUp.push_back(vkSignalSemaphore);
+ }
+ for (auto backendWaitSemaphore : mStagedWaitSemaphores) {
+ vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore());
+ }
+
+ DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
+ if (vkSemaphoresToCleanUp.size() > 0) {
+ destroySemaphoreInfo =
+ new DestroySemaphoreInfo(vulkanInterface, std::move(vkSemaphoresToCleanUp));
+
+ insertInfo.fNumWaitSemaphores = mStagedWaitSemaphores.size();
+ insertInfo.fWaitSemaphores = mStagedWaitSemaphores.data();
+ insertInfo.fNumSignalSemaphores = 1;
+ insertInfo.fSignalSemaphores = &backendSignalSemaphore;
+ insertInfo.fFinishedProc = unref_semaphore;
+ insertInfo.fFinishedContext = destroySemaphoreInfo;
+ }
+
+ const bool inserted = context->graphiteContext()->insertRecording(insertInfo);
+ LOG_ALWAYS_FATAL_IF(!inserted,
+ "graphite::Context::insertRecording(...) failed, check for Skia errors");
+ const bool submitted = context->graphiteContext()->submit(graphite::SyncToCpu::kNo);
+ LOG_ALWAYS_FATAL_IF(!submitted, "graphite::Context::submit(...) failed, check for Skia errors");
+ // Skia's "backend" semaphores can be deleted immediately after inserting the recording; only
+ // the underlying VK semaphores need to be kept until GPU work is complete.
+ mStagedWaitSemaphores.clear();
+
+ base::unique_fd drawFenceFd(-1);
+ if (vkSignalSemaphore != VK_NULL_HANDLE) {
+ drawFenceFd.reset(vulkanInterface.exportSemaphoreSyncFd(vkSignalSemaphore));
+ }
+ // Now that drawFenceFd has been created, we can delete RE's reference to this semaphore, as
+ // another reference is still held until fFinishedProc is called after completion of GPU work.
+ if (destroySemaphoreInfo) {
+ destroySemaphoreInfo->unref();
+ }
+ return drawFenceFd;
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GraphiteVkRenderEngine.h b/libs/renderengine/skia/GraphiteVkRenderEngine.h
new file mode 100644
index 0000000..cf24a3b
--- /dev/null
+++ b/libs/renderengine/skia/GraphiteVkRenderEngine.h
@@ -0,0 +1,40 @@
+/*
+ * 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 "SkiaVkRenderEngine.h"
+
+#include <include/gpu/graphite/BackendSemaphore.h>
+
+namespace android::renderengine::skia {
+
+class GraphiteVkRenderEngine : public SkiaVkRenderEngine {
+public:
+ static std::unique_ptr<GraphiteVkRenderEngine> create(const RenderEngineCreationArgs& args);
+
+protected:
+ std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) override;
+ void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
+ base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
+
+private:
+ GraphiteVkRenderEngine(const RenderEngineCreationArgs& args) : SkiaVkRenderEngine(args) {}
+
+ std::vector<graphite::BackendSemaphore> mStagedWaitSemaphores;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index f10c98d..48270e1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -271,7 +271,7 @@
EGLContext ctxt, EGLSurface placeholder,
EGLContext protectedContext, EGLSurface protectedPlaceholder)
: SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
- args.supportsBackgroundBlur),
+ args.blurAlgorithm),
mEGLDisplay(display),
mEGLContext(ctxt),
mPlaceholderSurface(placeholder),
@@ -279,7 +279,7 @@
mProtectedPlaceholderSurface(protectedPlaceholder) {}
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
- finishRenderingAndAbandonContext();
+ finishRenderingAndAbandonContexts();
if (mPlaceholderSurface != EGL_NO_SURFACE) {
eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
}
@@ -337,8 +337,14 @@
}
}
-base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context) {
- base::unique_fd drawFence = flush();
+base::unique_fd SkiaGLRenderEngine::flushAndSubmit(SkiaGpuContext* context,
+ sk_sp<SkSurface> dstSurface) {
+ sk_sp<GrDirectContext> grContext = context->grDirectContext();
+ {
+ ATRACE_NAME("flush surface");
+ grContext->flush(dstSurface.get());
+ }
+ base::unique_fd drawFence = flushGL();
bool requireSync = drawFence.get() < 0;
if (requireSync) {
@@ -346,8 +352,7 @@
} else {
ATRACE_BEGIN("Submit(sync=false)");
}
- bool success =
- context->grDirectContext()->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
+ bool success = grContext->submit(requireSync ? GrSyncCpu::kYes : GrSyncCpu::kNo);
ATRACE_END();
if (!success) {
ALOGE("Failed to flush RenderEngine commands");
@@ -394,7 +399,7 @@
return true;
}
-base::unique_fd SkiaGLRenderEngine::flush() {
+base::unique_fd SkiaGLRenderEngine::flushGL() {
ATRACE_CALL();
if (!GLExtensions::getInstance().hasNativeFenceSync()) {
return base::unique_fd();
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 2e22478..bd177e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -63,7 +63,7 @@
bool supportsProtectedContentImpl() const override;
bool useProtectedContextImpl(GrProtected isProtected) override;
void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override;
- base::unique_fd flushAndSubmit(SkiaGpuContext* context) override;
+ base::unique_fd flushAndSubmit(SkiaGpuContext* context, sk_sp<SkSurface> dstSurface) override;
void appendBackendSpecificInfoToDump(std::string& result) override;
private:
@@ -71,7 +71,7 @@
EGLSurface placeholder, EGLContext protectedContext,
EGLSurface protectedPlaceholder);
bool waitGpuFence(base::borrowed_fd fenceFd);
- base::unique_fd flush();
+ base::unique_fd flushGL();
static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
static EGLContext createEglContext(EGLDisplay display, EGLConfig config,
EGLContext shareContext,
diff --git a/libs/renderengine/skia/SkiaRenderEngine.cpp b/libs/renderengine/skia/SkiaRenderEngine.cpp
index 9e8fe68..325a911 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -90,7 +90,7 @@
// Debugging settings
static const bool kPrintLayerSettings = false;
-static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
+static const bool kGaneshFlushAfterEveryLayer = kPrintLayerSettings;
static constexpr bool kEnableLayerBrightening = true;
} // namespace
@@ -273,32 +273,47 @@
}
SkiaRenderEngine::SkiaRenderEngine(Threaded threaded, PixelFormat pixelFormat,
- bool supportsBackgroundBlur)
+ BlurAlgorithm blurAlgorithm)
: RenderEngine(threaded), mDefaultPixelFormat(pixelFormat) {
- if (supportsBackgroundBlur) {
- ALOGD("Background Blurs Enabled");
- mBlurFilter = new KawaseBlurFilter();
+ switch (blurAlgorithm) {
+ case BlurAlgorithm::GAUSSIAN: {
+ ALOGD("Background Blurs Enabled (Gaussian algorithm)");
+ mBlurFilter = new GaussianBlurFilter();
+ break;
+ }
+ case BlurAlgorithm::KAWASE: {
+ ALOGD("Background Blurs Enabled (Kawase algorithm)");
+ mBlurFilter = new KawaseBlurFilter();
+ break;
+ }
+ default: {
+ mBlurFilter = nullptr;
+ break;
+ }
}
+
mCapture = std::make_unique<SkiaCapture>();
}
SkiaRenderEngine::~SkiaRenderEngine() { }
-// To be called from backend dtors.
-void SkiaRenderEngine::finishRenderingAndAbandonContext() {
+// To be called from backend dtors. Used to clean up Skia objects before GPU API contexts are
+// destroyed by subclasses.
+void SkiaRenderEngine::finishRenderingAndAbandonContexts() {
std::lock_guard<std::mutex> lock(mRenderingMutex);
if (mBlurFilter) {
delete mBlurFilter;
}
- if (mContext) {
- mContext->finishRenderingAndAbandonContext();
- }
+ // Leftover textures may hold refs to backend-specific Skia contexts, which must be released
+ // before ~SkiaGpuContext is called.
+ mTextureCleanupMgr.setDeferredStatus(false);
+ mTextureCleanupMgr.cleanup();
- if (mProtectedContext) {
- mProtectedContext->finishRenderingAndAbandonContext();
- }
+ // ~SkiaGpuContext must be called before GPU API contexts are torn down.
+ mContext.reset();
+ mProtectedContext.reset();
}
void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) {
@@ -405,9 +420,7 @@
// If we were to support caching protected buffers then we will need to switch the
// currently bound context if we are not already using the protected context (and subsequently
- // switch back after the buffer is cached). However, for non-protected content we can bind
- // the texture in either GL context because they are initialized with the same share_context
- // which allows the texture state to be shared between them.
+ // switch back after the buffer is cached).
auto context = getActiveContext();
auto& cache = mTextureCache;
@@ -665,8 +678,8 @@
validateOutputBufferUsage(buffer->getBuffer());
auto context = getActiveContext();
- LOG_ALWAYS_FATAL_IF(context->isAbandoned(), "Context is abandoned/device lost at start of %s",
- __func__);
+ LOG_ALWAYS_FATAL_IF(context->isAbandonedOrDeviceLost(),
+ "Context is abandoned/device lost at start of %s", __func__);
// any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called
DeferTextureCleanup dtc(mTextureCleanupMgr);
@@ -1122,40 +1135,21 @@
} else {
canvas->drawRect(bounds.rect(), paint);
}
- if (kFlushAfterEveryLayer) {
+ if (kGaneshFlushAfterEveryLayer) {
ATRACE_NAME("flush surface");
+ // No-op in Graphite. If "flushing" Skia's drawing commands after each layer is desired
+ // in Graphite, then a graphite::Recording would need to be snapped and tracked for each
+ // layer, which is likely possible but adds non-trivial complexity (in both bookkeeping
+ // and refactoring).
skgpu::ganesh::Flush(activeSurface);
}
}
- for (const auto& borderRenderInfo : display.borderInfoList) {
- SkPaint p;
- p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g,
- borderRenderInfo.color.b, borderRenderInfo.color.a});
- p.setAntiAlias(true);
- p.setStyle(SkPaint::kStroke_Style);
- p.setStrokeWidth(borderRenderInfo.width);
- SkRegion sk_region;
- SkPath path;
-
- // Construct a final SkRegion using Regions
- for (const auto& r : borderRenderInfo.combinedRegion) {
- sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op);
- }
-
- sk_region.getBoundaryPath(&path);
- canvas->drawPath(path, p);
- path.close();
- }
surfaceAutoSaveRestore.restore();
mCapture->endCapture();
- {
- ATRACE_NAME("flush surface");
- LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
- skgpu::ganesh::Flush(activeSurface);
- }
- auto drawFence = sp<Fence>::make(flushAndSubmit(context));
+ LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+ auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
if (ATRACE_ENABLED()) {
static gui::FenceMonitor sMonitor("RE Completion");
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index a39adbe..38db810 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -59,7 +59,7 @@
class SkiaRenderEngine : public RenderEngine {
public:
static std::unique_ptr<SkiaRenderEngine> create(const RenderEngineCreationArgs& args);
- SkiaRenderEngine(Threaded, PixelFormat pixelFormat, bool supportsBackgroundBlur);
+ SkiaRenderEngine(Threaded, PixelFormat pixelFormat, BlurAlgorithm);
~SkiaRenderEngine() override;
std::future<void> primeCache(bool shouldPrimeUltraHDR) override final;
@@ -79,9 +79,9 @@
void ensureContextsCreated();
protected:
- // This is so backends can stop the generic rendering state first before
- // cleaning up backend-specific state
- void finishRenderingAndAbandonContext();
+ // This is so backends can stop the generic rendering state first before cleaning up
+ // backend-specific state. SkiaGpuContexts are invalid after invocation.
+ void finishRenderingAndAbandonContexts();
// Functions that a given backend (GLES, Vulkan) must implement
using Contexts = std::pair<unique_ptr<SkiaGpuContext>, unique_ptr<SkiaGpuContext>>;
@@ -89,7 +89,8 @@
virtual bool supportsProtectedContentImpl() const = 0;
virtual bool useProtectedContextImpl(GrProtected isProtected) = 0;
virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) = 0;
- virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) = 0;
+ virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context,
+ sk_sp<SkSurface> dstSurface) = 0;
virtual void appendBackendSpecificInfoToDump(std::string& result) = 0;
size_t getMaxTextureSize() const override final;
@@ -167,9 +168,6 @@
// Number of external holders of ExternalTexture references, per GraphicBuffer ID.
std::unordered_map<GraphicBufferId, int32_t> mGraphicBufferExternalRefs
GUARDED_BY(mRenderingMutex);
- // For GL, this cache is shared between protected and unprotected contexts. For Vulkan, it is
- // only used for the unprotected context, because Vulkan does not allow sharing between
- // contexts, and protected is less common.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index 473fa0a..bd50107 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -69,12 +69,38 @@
case GraphicsApi::GL:
return true;
case GraphicsApi::VK: {
- if (!sVulkanInterface.isInitialized()) {
- sVulkanInterface.init(false /* no protected content */);
- ALOGD("%s: initialized == %s.", __func__,
- sVulkanInterface.isInitialized() ? "true" : "false");
+ // Static local variables are initialized once, on first invocation of the function.
+ static const bool canSupportVulkan = []() {
+ if (!sVulkanInterface.isInitialized()) {
+ sVulkanInterface.init(false /* no protected content */);
+ ALOGD("%s: initialized == %s.", __func__,
+ sVulkanInterface.isInitialized() ? "true" : "false");
+ if (!sVulkanInterface.isInitialized()) {
+ sVulkanInterface.teardown();
+ return false;
+ }
+ }
+ return true;
+ }();
+ return canSupportVulkan;
+ }
+ }
+}
+
+void RenderEngine::teardown(GraphicsApi graphicsApi) {
+ switch (graphicsApi) {
+ case GraphicsApi::GL:
+ break;
+ case GraphicsApi::VK: {
+ if (sVulkanInterface.isInitialized()) {
+ sVulkanInterface.teardown();
+ ALOGD("Tearing down the unprotected VulkanInterface.");
}
- return sVulkanInterface.isInitialized();
+ if (sProtectedContentVulkanInterface.isInitialized()) {
+ sProtectedContentVulkanInterface.teardown();
+ ALOGD("Tearing down the protected VulkanInterface.");
+ }
+ break;
}
}
}
@@ -83,41 +109,37 @@
using base::StringAppendF;
-std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
- const RenderEngineCreationArgs& args) {
- // TODO: b/293371537 - Ganesh vs. Graphite subclass based on flag in RenderEngineCreationArgs
- std::unique_ptr<SkiaVkRenderEngine> engine(new GaneshVkRenderEngine(args));
- engine->ensureContextsCreated();
-
- if (sVulkanInterface.isInitialized()) {
- ALOGD("SkiaVkRenderEngine::%s: successfully initialized SkiaVkRenderEngine", __func__);
- return engine;
- } else {
- ALOGD("SkiaVkRenderEngine::%s: could not create SkiaVkRenderEngine. "
- "Likely insufficient Vulkan support",
- __func__);
- return {};
- }
-}
-
SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
: SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
- args.supportsBackgroundBlur) {}
+ args.blurAlgorithm) {}
SkiaVkRenderEngine::~SkiaVkRenderEngine() {
- finishRenderingAndAbandonContext();
+ finishRenderingAndAbandonContexts();
+ // Teardown VulkanInterfaces after Skia contexts have been abandoned
+ teardown(GraphicsApi::VK);
}
SkiaRenderEngine::Contexts SkiaVkRenderEngine::createContexts() {
sSetupVulkanInterface();
+ // More work would need to be done in order to have multiple RenderEngine instances. In
+ // particular, they would not be able to share the same VulkanInterface(s).
+ LOG_ALWAYS_FATAL_IF(!sVulkanInterface.takeOwnership(),
+ "SkiaVkRenderEngine couldn't take ownership of existing unprotected "
+ "VulkanInterface! Only one SkiaVkRenderEngine instance may exist at a "
+ "time.");
+ if (sProtectedContentVulkanInterface.isInitialized()) {
+ // takeOwnership fails on an uninitialized VulkanInterface, but protected content support is
+ // optional.
+ LOG_ALWAYS_FATAL_IF(!sProtectedContentVulkanInterface.takeOwnership(),
+ "SkiaVkRenderEngine couldn't take ownership of existing protected "
+ "VulkanInterface! Only one SkiaVkRenderEngine instance may exist at a "
+ "time.");
+ }
SkiaRenderEngine::Contexts contexts;
- contexts.first = SkiaGpuContext::MakeVulkan_Ganesh(sVulkanInterface.getGaneshBackendContext(),
- mSkSLCacheMonitor);
+ contexts.first = createContext(sVulkanInterface);
if (supportsProtectedContentImpl()) {
- contexts.second = SkiaGpuContext::MakeVulkan_Ganesh(sProtectedContentVulkanInterface
- .getGaneshBackendContext(),
- mSkSLCacheMonitor);
+ contexts.second = createContext(sProtectedContentVulkanInterface);
}
return contexts;
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 5fd911f..0a2f9b2 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -29,7 +29,6 @@
class SkiaVkRenderEngine : public SkiaRenderEngine {
public:
- static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args);
~SkiaVkRenderEngine() override;
int getContextPriority() override;
@@ -71,9 +70,11 @@
};
protected:
+ virtual std::unique_ptr<SkiaGpuContext> createContext(VulkanInterface& vulkanInterface) = 0;
// Redeclare parent functions that Ganesh vs. Graphite subclasses must implement.
virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) override = 0;
- virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context) override = 0;
+ virtual base::unique_fd flushAndSubmit(SkiaGpuContext* context,
+ sk_sp<SkSurface> dstSurface) override = 0;
SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
index 2f09a38..fc16c56 100644
--- a/libs/renderengine/skia/VulkanInterface.cpp
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -20,6 +20,8 @@
#include "VulkanInterface.h"
#include <include/gpu/GpuTypes.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
+
#include <log/log_main.h>
#include <utils/Timers.h>
@@ -47,6 +49,23 @@
return backendContext;
};
+VulkanBackendContext VulkanInterface::getGraphiteBackendContext() {
+ VulkanBackendContext backendContext;
+ backendContext.fInstance = mInstance;
+ backendContext.fPhysicalDevice = mPhysicalDevice;
+ backendContext.fDevice = mDevice;
+ backendContext.fQueue = mQueue;
+ backendContext.fGraphicsQueueIndex = mQueueIndex;
+ backendContext.fMaxAPIVersion = mApiVersion;
+ backendContext.fVkExtensions = &mGrExtensions;
+ backendContext.fDeviceFeatures2 = mPhysicalDeviceFeatures2;
+ backendContext.fGetProc = mGrGetProc;
+ backendContext.fProtectedContext = mIsProtected ? Protected::kYes : Protected::kNo;
+ backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived
+ backendContext.fDeviceLostProc = onVkDeviceFault;
+ return backendContext;
+};
+
VkSemaphore VulkanInterface::createExportableSemaphore() {
VkExportSemaphoreCreateInfo exportInfo;
exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
@@ -225,7 +244,9 @@
VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion));
if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
- return;
+ BAIL("Vulkan instance API version %" PRIu32 ".%" PRIu32 ".%" PRIu32 " < 1.1.0",
+ VK_VERSION_MAJOR(instanceVersion), VK_VERSION_MINOR(instanceVersion),
+ VK_VERSION_PATCH(instanceVersion));
}
const VkApplicationInfo appInfo = {
@@ -304,13 +325,11 @@
}
vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps);
- if (physDevProps.properties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
- BAIL("Could not find a Vulkan 1.1+ physical device");
- }
-
- if (physDevProps.properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) {
- // TODO: b/326633110 - SkiaVK is not working correctly on swiftshader path.
- BAIL("CPU implementations of Vulkan is not supported");
+ const uint32_t physicalDeviceApiVersion = physDevProps.properties.apiVersion;
+ if (physicalDeviceApiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ BAIL("Vulkan physical device API version %" PRIu32 ".%" PRIu32 ".%" PRIu32 " < 1.1.0",
+ VK_VERSION_MAJOR(physicalDeviceApiVersion), VK_VERSION_MINOR(physicalDeviceApiVersion),
+ VK_VERSION_PATCH(physicalDeviceApiVersion));
}
// Check for syncfd support. Bail if we cannot both import and export them.
@@ -525,7 +544,7 @@
mDevice = device;
mQueue = graphicsQueue;
mQueueIndex = graphicsQueueIndex;
- mApiVersion = physDevProps.properties.apiVersion;
+ mApiVersion = physicalDeviceApiVersion;
// grExtensions already constructed
// feature pointers already constructed
mGrGetProc = sGetProc;
@@ -538,13 +557,16 @@
ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs);
}
-// TODO: b/293371537 - Iterate on this.
-// Currently unused, but copied over from its original location for potential future use. This
-// should likely be improved to walk the pNext chain of mPhysicalDeviceFeatures2 and free everything
-// like HWUI's VulkanManager. Also, not all fields are being reset.
-void VulkanInterface::teardown() {
- mInitialized = false;
+bool VulkanInterface::takeOwnership() {
+ if (!isInitialized() || mIsOwned) {
+ return false;
+ }
+ mIsOwned = true;
+ return true;
+}
+void VulkanInterface::teardown() {
+ // Core resources that must be destroyed using Vulkan functions.
if (mDevice != VK_NULL_HANDLE) {
mFuncs.vkDeviceWaitIdle(mDevice);
mFuncs.vkDestroyDevice(mDevice, nullptr);
@@ -555,26 +577,42 @@
mInstance = VK_NULL_HANDLE;
}
+ // Optional features that can be deleted directly.
+ // TODO: b/293371537 - This section should likely be improved to walk the pNext chain of
+ // mPhysicalDeviceFeatures2 and free everything like HWUI's VulkanManager.
if (mProtectedMemoryFeatures) {
delete mProtectedMemoryFeatures;
+ mProtectedMemoryFeatures = nullptr;
}
-
if (mSamplerYcbcrConversionFeatures) {
delete mSamplerYcbcrConversionFeatures;
+ mSamplerYcbcrConversionFeatures = nullptr;
}
-
if (mPhysicalDeviceFeatures2) {
delete mPhysicalDeviceFeatures2;
+ mPhysicalDeviceFeatures2 = nullptr;
}
-
if (mDeviceFaultFeatures) {
delete mDeviceFaultFeatures;
+ mDeviceFaultFeatures = nullptr;
}
- mSamplerYcbcrConversionFeatures = nullptr;
- mPhysicalDeviceFeatures2 = nullptr;
- mProtectedMemoryFeatures = nullptr;
- mDeviceFaultFeatures = nullptr;
+ // Misc. fields that can be trivially reset without special deletion:
+ mInitialized = false;
+ mIsOwned = false;
+ mPhysicalDevice = VK_NULL_HANDLE; // Implicitly destroyed by destroying mInstance.
+ mQueue = VK_NULL_HANDLE; // Implicitly destroyed by destroying mDevice.
+ mQueueIndex = 0;
+ mApiVersion = 0;
+ mGrExtensions = GrVkExtensions();
+ mGrGetProc = nullptr;
+ mIsProtected = false;
+ mIsRealtimePriority = false;
+
+ mFuncs = VulkanFuncs();
+
+ mInstanceExtensionNames.clear();
+ mDeviceExtensionNames.clear();
}
} // namespace skia
diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h
index 512e62c..af0489a 100644
--- a/libs/renderengine/skia/VulkanInterface.h
+++ b/libs/renderengine/skia/VulkanInterface.h
@@ -23,6 +23,10 @@
using namespace skgpu;
+namespace skgpu {
+struct VulkanBackendContext;
+} // namespace skgpu
+
namespace android {
namespace renderengine {
namespace skia {
@@ -37,10 +41,14 @@
VulkanInterface& operator=(VulkanInterface&&) = delete;
void init(bool protectedContent = false);
+ // Returns true and marks this VulkanInterface as "owned" if it is initialized but unused by any
+ // RenderEngine instances. Returns false if already owned, indicating that it must not be used
+ // by a new RE instance.
+ bool takeOwnership();
void teardown();
- // TODO: b/293371537 - Graphite variant (external/skia/include/gpu/vk/VulkanBackendContext.h)
GrVkBackendContext getGaneshBackendContext();
+ VulkanBackendContext getGraphiteBackendContext();
VkSemaphore createExportableSemaphore();
VkSemaphore importSemaphoreFromSyncFd(int syncFd);
int exportSemaphoreSyncFd(VkSemaphore semaphore);
@@ -68,7 +76,9 @@
const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
const std::vector<std::byte>& vendorBinaryData);
+ // Note: keep all field defaults in sync with teardown()
bool mInitialized = false;
+ bool mIsOwned = false;
VkInstance mInstance = VK_NULL_HANDLE;
VkPhysicalDevice mPhysicalDevice = VK_NULL_HANDLE;
VkDevice mDevice = VK_NULL_HANDLE;
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
index 87f00e4..b2eae00 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.cpp
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -38,7 +38,6 @@
namespace android::renderengine::skia {
namespace {
-// TODO: b/293371537 - Graphite variant.
static GrContextOptions ganeshOptions(GrContextOptions::PersistentCache& skSLCacheMonitor) {
GrContextOptions options;
options.fDisableDriverCorrectnessWorkarounds = true;
@@ -67,6 +66,11 @@
LOG_ALWAYS_FATAL_IF(mGrContext.get() == nullptr, "GrDirectContext creation failed");
}
+GaneshGpuContext::~GaneshGpuContext() {
+ mGrContext->flushAndSubmit(GrSyncCpu::kYes);
+ mGrContext->abandonContext();
+};
+
sk_sp<GrDirectContext> GaneshGpuContext::grDirectContext() {
return mGrContext;
}
@@ -93,7 +97,7 @@
return mGrContext->maxTextureSize();
};
-bool GaneshGpuContext::isAbandoned() {
+bool GaneshGpuContext::isAbandonedOrDeviceLost() {
return mGrContext->abandoned();
}
@@ -101,11 +105,6 @@
mGrContext->setResourceCacheLimit(maxResourceBytes);
}
-void GaneshGpuContext::finishRenderingAndAbandonContext() {
- mGrContext->flushAndSubmit(GrSyncCpu::kYes);
- mGrContext->abandonContext();
-};
-
void GaneshGpuContext::purgeUnlockedScratchResources() {
mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly);
}
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h
index ec0162d..aeb1a82 100644
--- a/libs/renderengine/skia/compat/GaneshGpuContext.h
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.h
@@ -25,7 +25,7 @@
class GaneshGpuContext : public SkiaGpuContext {
public:
GaneshGpuContext(sk_sp<GrDirectContext> grContext);
- ~GaneshGpuContext() override = default;
+ ~GaneshGpuContext() override;
sk_sp<GrDirectContext> grDirectContext() override;
@@ -36,10 +36,9 @@
size_t getMaxRenderTargetSize() const override;
size_t getMaxTextureSize() const override;
- bool isAbandoned() override;
+ bool isAbandonedOrDeviceLost() override;
void setResourceCacheLimit(size_t maxResourceBytes) override;
- void finishRenderingAndAbandonContext() override;
void purgeUnlockedScratchResources() override;
void resetContextIfApplicable() override;
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
new file mode 100644
index 0000000..3dd9ed2
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 "GraphiteBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <include/core/SkSurfaceProps.h>
+#include <include/gpu/graphite/Image.h>
+#include <include/gpu/graphite/Surface.h>
+#include <include/gpu/graphite/TextureInfo.h>
+
+#include "skia/ColorSpaces.h"
+
+#include <android/hardware_buffer.h>
+#include <inttypes.h>
+#include <log/log_main.h>
+#include <utils/Trace.h>
+
+namespace android::renderengine::skia {
+
+GraphiteBackendTexture::GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
+ AHardwareBuffer* buffer, bool isOutputBuffer)
+ : SkiaBackendTexture(buffer, isOutputBuffer), mRecorder(std::move(recorder)) {
+ ATRACE_CALL();
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+ const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+
+ const SkISize dimensions = {static_cast<int32_t>(desc.width),
+ static_cast<int32_t>(desc.height)};
+ LOG_ALWAYS_FATAL_IF(static_cast<uint32_t>(dimensions.width()) != desc.width ||
+ static_cast<uint32_t>(dimensions.height()) != desc.height,
+ "Failed to create a valid texture, casting unsigned dimensions [%" PRIu32
+ ",%" PRIu32 "] to signed [%" PRIo32 ",%" PRIo32 "] "
+ "is invalid",
+ desc.width, desc.height, dimensions.width(), dimensions.height());
+
+ mBackendTexture = mRecorder->createBackendTexture(buffer, isOutputBuffer, createProtectedImage,
+ dimensions, false);
+ if (!mBackendTexture.isValid() || !dimensions.width() || !dimensions.height()) {
+ LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+ "isWriteable:%d format:%d",
+ this, dimensions.width(), dimensions.height(), createProtectedImage,
+ isOutputBuffer, desc.format);
+ }
+}
+
+GraphiteBackendTexture::~GraphiteBackendTexture() {
+ if (mBackendTexture.isValid()) {
+ mRecorder->deleteBackendTexture(mBackendTexture);
+ mBackendTexture = {};
+ }
+}
+
+sk_sp<SkImage> GraphiteBackendTexture::makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) {
+ const SkColorType colorType = colorTypeForImage(alphaType);
+ sk_sp<SkImage> image =
+ SkImages::WrapTexture(mRecorder.get(), mBackendTexture, colorType, alphaType,
+ toSkColorSpace(dataspace), releaseImageProc, releaseContext);
+ if (!image) {
+ logFatalTexture("Unable to generate SkImage.", dataspace, colorType);
+ }
+ return image;
+}
+
+sk_sp<SkSurface> GraphiteBackendTexture::makeSurface(ui::Dataspace dataspace,
+ TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) {
+ const SkColorType colorType = internalColorType();
+ SkSurfaceProps props;
+ sk_sp<SkSurface> surface =
+ SkSurfaces::WrapBackendTexture(mRecorder.get(), mBackendTexture, colorType,
+ toSkColorSpace(dataspace), &props, releaseSurfaceProc,
+ releaseContext);
+ if (!surface) {
+ logFatalTexture("Unable to generate SkSurface.", dataspace, colorType);
+ }
+ return surface;
+}
+
+void GraphiteBackendTexture::logFatalTexture(const char* msg, ui::Dataspace dataspace,
+ SkColorType colorType) {
+ // TODO: b/293371537 - Iterate on this logging (validate failure cases, possibly check
+ // VulkanTextureInfo, etc.)
+ const skgpu::graphite::TextureInfo& textureInfo = mBackendTexture.info();
+ LOG_ALWAYS_FATAL("%s isOutputBuffer:%d, dataspace:%d, colorType:%d"
+ "\n\tBackendTexture: isValid:%d, dimensions:%dx%d"
+ "\n\t\tTextureInfo: isValid:%d, numSamples:%d, mipmapped:%d, isProtected: %d",
+ msg, isOutputBuffer(), static_cast<int32_t>(dataspace), colorType,
+ mBackendTexture.isValid(), mBackendTexture.dimensions().width(),
+ mBackendTexture.dimensions().height(), textureInfo.isValid(),
+ textureInfo.numSamples(), static_cast<int32_t>(textureInfo.mipmapped()),
+ static_cast<int32_t>(textureInfo.isProtected()));
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteBackendTexture.h b/libs/renderengine/skia/compat/GraphiteBackendTexture.h
new file mode 100644
index 0000000..3bec3f7
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteBackendTexture.h
@@ -0,0 +1,59 @@
+/*
+ * 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 "SkiaBackendTexture.h"
+
+#include <include/core/SkColorSpace.h>
+#include <include/core/SkImage.h>
+#include <include/core/SkSurface.h>
+#include <include/gpu/graphite/BackendTexture.h>
+#include <include/gpu/graphite/Recorder.h>
+
+#include <android-base/macros.h>
+#include <ui/GraphicTypes.h>
+
+#include <memory>
+
+namespace android::renderengine::skia {
+
+class GraphiteBackendTexture : public SkiaBackendTexture {
+public:
+ // Creates an internal skgpu::graphite::BackendTexture whose contents come from the provided
+ // buffer.
+ GraphiteBackendTexture(std::shared_ptr<skgpu::graphite::Recorder> recorder,
+ AHardwareBuffer* buffer, bool isOutputBuffer);
+
+ ~GraphiteBackendTexture() override;
+
+ sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) override;
+
+ sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace, TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GraphiteBackendTexture);
+
+ void logFatalTexture(const char* msg, ui::Dataspace dataspace, SkColorType colorType);
+
+ const std::shared_ptr<skgpu::graphite::Recorder> mRecorder;
+ skgpu::graphite::BackendTexture mBackendTexture;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.cpp b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
new file mode 100644
index 0000000..69f5832
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "GraphiteGpuContext.h"
+
+#include <include/core/SkImageInfo.h>
+#include <include/core/SkSurface.h>
+#include <include/core/SkTraceMemoryDump.h>
+#include <include/gpu/graphite/GraphiteTypes.h>
+#include <include/gpu/graphite/Surface.h>
+#include <include/gpu/graphite/vk/VulkanGraphiteUtils.h>
+
+#include "GpuTypes.h"
+#include "skia/compat/GraphiteBackendTexture.h"
+
+#include <android-base/macros.h>
+#include <log/log_main.h>
+#include <memory>
+
+namespace android::renderengine::skia {
+
+namespace {
+static skgpu::graphite::ContextOptions graphiteOptions() {
+ skgpu::graphite::ContextOptions options;
+ options.fDisableDriverCorrectnessWorkarounds = true;
+ return options;
+}
+} // namespace
+
+std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Graphite(
+ const skgpu::VulkanBackendContext& vulkanBackendContext) {
+ return std::make_unique<GraphiteGpuContext>(
+ skgpu::graphite::ContextFactory::MakeVulkan(vulkanBackendContext, graphiteOptions()));
+}
+
+GraphiteGpuContext::GraphiteGpuContext(std::unique_ptr<skgpu::graphite::Context> context)
+ : mContext(std::move(context)) {
+ LOG_ALWAYS_FATAL_IF(mContext.get() == nullptr, "graphite::Context creation failed");
+ LOG_ALWAYS_FATAL_IF(mContext->backend() != skgpu::BackendApi::kVulkan,
+ "graphite::Context::backend() == %d, but GraphiteBackendContext makes "
+ "assumptions that are only valid for Vulkan (%d)",
+ static_cast<int>(mContext->backend()),
+ static_cast<int>(skgpu::BackendApi::kVulkan));
+
+ // TODO: b/293371537 - Iterate on default cache limits (the Recorder should have the majority of
+ // the budget, and the Context should be given a smaller fraction.)
+ skgpu::graphite::RecorderOptions recorderOptions = skgpu::graphite::RecorderOptions();
+ this->mRecorder = mContext->makeRecorder(recorderOptions);
+ LOG_ALWAYS_FATAL_IF(mRecorder.get() == nullptr, "graphite::Recorder creation failed");
+}
+
+GraphiteGpuContext::~GraphiteGpuContext() {
+ // The equivalent operation would occur when destroying the graphite::Context, but calling this
+ // explicitly allows any outstanding GraphiteBackendTextures to be released, thus allowing us to
+ // assert that this GraphiteGpuContext holds the last ref to the underlying graphite::Recorder.
+ mContext->submit(skgpu::graphite::SyncToCpu::kYes);
+ // We must call the Context's and Recorder's dtors before exiting this function, so all other
+ // refs must be released by now. Note: these assertions may be unreliable in a hypothetical
+ // future world where we take advantage of Graphite's multi-threading capabilities!
+ LOG_ALWAYS_FATAL_IF(mRecorder.use_count() > 1,
+ "Something other than GraphiteGpuContext holds a ref to the underlying "
+ "graphite::Recorder");
+ LOG_ALWAYS_FATAL_IF(mContext.use_count() > 1,
+ "Something other than GraphiteGpuContext holds a ref to the underlying "
+ "graphite::Context");
+};
+
+std::shared_ptr<skgpu::graphite::Context> GraphiteGpuContext::graphiteContext() {
+ return mContext;
+}
+
+std::shared_ptr<skgpu::graphite::Recorder> GraphiteGpuContext::graphiteRecorder() {
+ return mRecorder;
+}
+
+std::unique_ptr<SkiaBackendTexture> GraphiteGpuContext::makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) {
+ return std::make_unique<GraphiteBackendTexture>(graphiteRecorder(), buffer, isOutputBuffer);
+}
+
+sk_sp<SkSurface> GraphiteGpuContext::createRenderTarget(SkImageInfo imageInfo) {
+ constexpr SkSurfaceProps* kProps = nullptr;
+ return SkSurfaces::RenderTarget(mRecorder.get(), imageInfo, skgpu::Mipmapped::kNo, kProps);
+}
+
+size_t GraphiteGpuContext::getMaxRenderTargetSize() const {
+ // maxRenderTargetSize only differs from maxTextureSize on GL, so as long as Graphite implies
+ // Vk, then the distinction is irrelevant.
+ return getMaxTextureSize();
+};
+
+size_t GraphiteGpuContext::getMaxTextureSize() const {
+ return mContext->maxTextureSize();
+};
+
+bool GraphiteGpuContext::isAbandonedOrDeviceLost() {
+ return mContext->isDeviceLost();
+}
+
+void GraphiteGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
+ mContext->dumpMemoryStatistics(traceMemoryDump);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GraphiteGpuContext.h b/libs/renderengine/skia/compat/GraphiteGpuContext.h
new file mode 100644
index 0000000..413817f
--- /dev/null
+++ b/libs/renderengine/skia/compat/GraphiteGpuContext.h
@@ -0,0 +1,64 @@
+/*
+ * 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 "SkiaGpuContext.h"
+#include "graphite/Recorder.h"
+
+#include <android-base/macros.h>
+
+namespace android::renderengine::skia {
+
+class GraphiteGpuContext : public SkiaGpuContext {
+public:
+ GraphiteGpuContext(std::unique_ptr<skgpu::graphite::Context> context);
+ ~GraphiteGpuContext() override;
+
+ std::shared_ptr<skgpu::graphite::Context> graphiteContext() override;
+ std::shared_ptr<skgpu::graphite::Recorder> graphiteRecorder() override;
+
+ std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) override;
+
+ sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) override;
+
+ size_t getMaxRenderTargetSize() const override;
+ size_t getMaxTextureSize() const override;
+ bool isAbandonedOrDeviceLost() override;
+ // No-op (large resources like textures, surfaces, images, etc. created by clients don't count
+ // towards Graphite's internal caching budgets, so adjusting its limits based on display change
+ // events should be unnecessary. Additionally, Graphite doesn't expose many cache tweaking
+ // functions yet, as its design may evolve.)
+ void setResourceCacheLimit(size_t maxResourceBytes) override{};
+
+ // TODO: b/293371537 - Triple-check and validate that no cleanup is necessary when switching
+ // contexts.
+ // No-op (unnecessary during context switch for Graphite's client-budgeted memory model).
+ void purgeUnlockedScratchResources() override{};
+ // No-op (only applicable to GL).
+ void resetContextIfApplicable() override{};
+
+ void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GraphiteGpuContext);
+
+ std::shared_ptr<skgpu::graphite::Context> mContext;
+ std::shared_ptr<skgpu::graphite::Recorder> mRecorder;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
index ddf1642..282dfe7 100644
--- a/libs/renderengine/skia/compat/SkiaGpuContext.h
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.h
@@ -22,7 +22,9 @@
#include <include/core/SkSurface.h>
#include <include/gpu/GrDirectContext.h>
#include <include/gpu/gl/GrGLInterface.h>
+#include <include/gpu/graphite/Context.h>
#include <include/gpu/vk/GrVkBackendContext.h>
+#include "include/gpu/vk/VulkanBackendContext.h"
#include "SkiaBackendTexture.h"
@@ -34,21 +36,37 @@
/**
* Abstraction over Ganesh and Graphite's underlying context-like objects.
+ *
+ * On destruction, subclasses will submit any pending work before destroying their internal Skia
+ * context(s). Any unused cached SkiaBackendTextures created from a SkiaGpuContext that are awaiting
+ * cleanup must be deleted before destroying that SkiaGpuContext, and any textures that are released
+ * during ~SkiaGpuContext must be configured to be deleted immediately.
*/
class SkiaGpuContext {
public:
+ /**
+ * glInterface must remain valid until after SkiaGpuContext is destroyed.
+ */
static std::unique_ptr<SkiaGpuContext> MakeGL_Ganesh(
sk_sp<const GrGLInterface> glInterface,
GrContextOptions::PersistentCache& skSLCacheMonitor);
- // TODO: b/293371537 - Graphite variant.
+ /**
+ * grVkBackendContext must remain valid until after SkiaGpuContext is destroyed.
+ */
static std::unique_ptr<SkiaGpuContext> MakeVulkan_Ganesh(
const GrVkBackendContext& grVkBackendContext,
GrContextOptions::PersistentCache& skSLCacheMonitor);
+ // TODO: b/293371537 - Need shader / pipeline monitoring support in Graphite.
+ /**
+ * vulkanBackendContext must remain valid until after SkiaGpuContext is destroyed.
+ */
+ static std::unique_ptr<SkiaGpuContext> MakeVulkan_Graphite(
+ const skgpu::VulkanBackendContext& vulkanBackendContext);
+
virtual ~SkiaGpuContext() = default;
- // TODO: b/293371537 - Maybe expose whether this SkiaGpuContext is using Ganesh or Graphite?
/**
* Only callable on Ganesh-backed instances of SkiaGpuContext, otherwise fatal.
*/
@@ -56,6 +74,20 @@
LOG_ALWAYS_FATAL("grDirectContext() called on a non-Ganesh instance of SkiaGpuContext!");
}
+ /**
+ * Only callable on Graphite-backed instances of SkiaGpuContext, otherwise fatal.
+ */
+ virtual std::shared_ptr<skgpu::graphite::Context> graphiteContext() {
+ LOG_ALWAYS_FATAL("graphiteContext() called on a non-Graphite instance of SkiaGpuContext!");
+ }
+
+ /**
+ * Only callable on Graphite-backed instances of SkiaGpuContext, otherwise fatal.
+ */
+ virtual std::shared_ptr<skgpu::graphite::Recorder> graphiteRecorder() {
+ LOG_ALWAYS_FATAL("graphiteRecorder() called on a non-Graphite instance of SkiaGpuContext!");
+ }
+
virtual std::unique_ptr<SkiaBackendTexture> makeBackendTexture(AHardwareBuffer* buffer,
bool isOutputBuffer) = 0;
@@ -68,12 +100,11 @@
*/
virtual sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) = 0;
- virtual bool isAbandoned() = 0;
+ virtual bool isAbandonedOrDeviceLost() = 0;
virtual size_t getMaxRenderTargetSize() const = 0;
virtual size_t getMaxTextureSize() const = 0;
virtual void setResourceCacheLimit(size_t maxResourceBytes) = 0;
- virtual void finishRenderingAndAbandonContext() = 0;
virtual void purgeUnlockedScratchResources() = 0;
virtual void resetContextIfApplicable() = 0; // No-op outside of GL (&& Ganesh at this point.)
diff --git a/libs/renderengine/skia/debug/SkiaCapture.cpp b/libs/renderengine/skia/debug/SkiaCapture.cpp
index 48dc77e..e778884 100644
--- a/libs/renderengine/skia/debug/SkiaCapture.cpp
+++ b/libs/renderengine/skia/debug/SkiaCapture.cpp
@@ -30,7 +30,7 @@
#include "SkCanvas.h"
#include "SkRect.h"
#include "SkTypeface.h"
-#include "src/utils/SkMultiPictureDocument.h"
+#include "include/docs/SkMultiPictureDocument.h"
#include <sys/stat.h>
namespace android {
@@ -196,7 +196,7 @@
// procs doesn't need to outlive this Make call
// The last argument is a callback for the endPage behavior.
// See SkSharingProc.h for more explanation of this callback.
- mMultiPic = SkMakeMultiPictureDocument(
+ mMultiPic = SkMultiPictureDocument::Make(
mOpenMultiPicStream.get(), &procs,
[sharingCtx = mSerialContext.get()](const SkPicture* pic) {
SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx);
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7b8eb84..cb8b016 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -107,6 +107,7 @@
virtual std::string name() = 0;
virtual renderengine::RenderEngine::GraphicsApi graphicsApi() = 0;
+ virtual renderengine::RenderEngine::SkiaBackend skiaBackend() = 0;
bool apiSupported() { return renderengine::RenderEngine::canSupport(graphicsApi()); }
std::unique_ptr<renderengine::RenderEngine> createRenderEngine() {
renderengine::RenderEngineCreationArgs reCreationArgs =
@@ -115,24 +116,16 @@
.setImageCacheSize(1)
.setEnableProtectedContext(false)
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(true)
+ .setBlurAlgorithm(renderengine::RenderEngine::BlurAlgorithm::KAWASE)
.setContextPriority(renderengine::RenderEngine::ContextPriority::MEDIUM)
.setThreaded(renderengine::RenderEngine::Threaded::NO)
.setGraphicsApi(graphicsApi())
+ .setSkiaBackend(skiaBackend())
.build();
return renderengine::RenderEngine::create(reCreationArgs);
}
};
-class SkiaVkRenderEngineFactory : public RenderEngineFactory {
-public:
- std::string name() override { return "SkiaVkRenderEngineFactory"; }
-
- renderengine::RenderEngine::GraphicsApi graphicsApi() override {
- return renderengine::RenderEngine::GraphicsApi::VK;
- }
-};
-
class SkiaGLESRenderEngineFactory : public RenderEngineFactory {
public:
std::string name() override { return "SkiaGLRenderEngineFactory"; }
@@ -140,6 +133,36 @@
renderengine::RenderEngine::GraphicsApi graphicsApi() {
return renderengine::RenderEngine::GraphicsApi::GL;
}
+
+ renderengine::RenderEngine::SkiaBackend skiaBackend() override {
+ return renderengine::RenderEngine::SkiaBackend::GANESH;
+ }
+};
+
+class GaneshVkRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "GaneshVkRenderEngineFactory"; }
+
+ renderengine::RenderEngine::GraphicsApi graphicsApi() override {
+ return renderengine::RenderEngine::GraphicsApi::VK;
+ }
+
+ renderengine::RenderEngine::SkiaBackend skiaBackend() override {
+ return renderengine::RenderEngine::SkiaBackend::GANESH;
+ }
+};
+
+class GraphiteVkRenderEngineFactory : public RenderEngineFactory {
+public:
+ std::string name() override { return "GraphiteVkRenderEngineFactory"; }
+
+ renderengine::RenderEngine::GraphicsApi graphicsApi() override {
+ return renderengine::RenderEngine::GraphicsApi::VK;
+ }
+
+ renderengine::RenderEngine::SkiaBackend skiaBackend() override {
+ return renderengine::RenderEngine::SkiaBackend::GRAPHITE;
+ }
};
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
@@ -219,7 +242,7 @@
RenderEngineTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+ ALOGI("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
}
~RenderEngineTest() {
@@ -228,7 +251,7 @@
}
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
- ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+ ALOGI("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
}
void writeBufferToFile(const char* basename) {
@@ -1242,7 +1265,12 @@
void RenderEngineTest::fillBufferWithPremultiplyAlpha() {
fillRedBufferWithPremultiplyAlpha();
- expectBufferColor(fullscreenRect(), 128, 0, 0, 128);
+ // Different backends and GPUs may round 255 * 0.5 = 127.5 differently, but
+ // either 127 or 128 are acceptable. Checking both 127 and 128 with a
+ // tolerance of 1 allows either 127 or 128 to pass, while preventing 126 or
+ // 129 from erroneously passing.
+ expectBufferColor(fullscreenRect(), 127, 0, 0, 127, 1);
+ expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1);
}
void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() {
@@ -1471,7 +1499,8 @@
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
- std::make_shared<SkiaVkRenderEngineFactory>()));
+ std::make_shared<GaneshVkRenderEngineFactory>(),
+ std::make_shared<GraphiteVkRenderEngineFactory>()));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
if (!GetParam()->apiSupported()) {
@@ -2490,51 +2519,6 @@
expectBufferColor(rect, 0, 128, 0, 128);
}
-TEST_P(RenderEngineTest, testBorder) {
- if (!GetParam()->apiSupported()) {
- GTEST_SKIP();
- }
-
- initializeRenderEngine();
-
- const ui::Dataspace dataspace = ui::Dataspace::V0_SRGB;
-
- const auto displayRect = Rect(1080, 2280);
- renderengine::DisplaySettings display{
- .physicalDisplay = displayRect,
- .clip = displayRect,
- .outputDataspace = dataspace,
- };
- display.borderInfoList.clear();
- renderengine::BorderRenderInfo info;
- info.combinedRegion = Region(Rect(99, 99, 199, 199));
- info.width = 20.0f;
- info.color = half4{1.0f, 128.0f / 255.0f, 0.0f, 1.0f};
- display.borderInfoList.emplace_back(info);
-
- const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
- const renderengine::LayerSettings greenLayer{
- .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
- .source =
- renderengine::PixelSource{
- .buffer =
- renderengine::Buffer{
- .buffer = greenBuffer,
- .usePremultipliedAlpha = true,
- },
- },
- .alpha = 1.0f,
- .sourceDataspace = dataspace,
- .whitePointNits = 200.f,
- };
-
- std::vector<renderengine::LayerSettings> layers;
- layers.emplace_back(greenLayer);
- invokeDraw(display, layers);
-
- expectBufferColor(Rect(99, 99, 101, 101), 255, 128, 0, 255, 1);
-}
-
TEST_P(RenderEngineTest, testDimming) {
if (!GetParam()->apiSupported()) {
GTEST_SKIP();
@@ -3147,6 +3131,11 @@
}
TEST_P(RenderEngineTest, primeShaderCache) {
+ // TODO: b/331447071 - Fix in Graphite and re-enable.
+ if (GetParam()->skiaBackend() == renderengine::RenderEngine::SkiaBackend::GRAPHITE) {
+ GTEST_SKIP();
+ }
+
if (!GetParam()->apiSupported()) {
GTEST_SKIP();
}
diff --git a/libs/sensor/Android.bp b/libs/sensor/Android.bp
index cc92bc3..7fa47b4 100644
--- a/libs/sensor/Android.bp
+++ b/libs/sensor/Android.bp
@@ -24,6 +24,7 @@
aconfig_declarations {
name: "libsensor_flags",
package: "com.android.hardware.libsensor.flags",
+ container: "system",
srcs: ["libsensor_flags.aconfig"],
}
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
index ef4d737..c511f4a 100644
--- a/libs/sensor/libsensor_flags.aconfig
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -1,4 +1,5 @@
package: "com.android.hardware.libsensor.flags"
+container: "system"
flag {
name: "sensormanager_ping_binder"
@@ -6,4 +7,4 @@
description: "Whether to pingBinder on SensorManager init"
bug: "322228259"
is_fixed_read_only: true
-}
\ No newline at end of file
+}
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 9cb298a..12230f9 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -105,9 +105,6 @@
cc_library_shared {
name: "libui",
vendor_available: true,
- vndk: {
- enabled: true,
- },
double_loadable: true,
cflags: [
@@ -280,13 +277,3 @@
"tests",
"tools",
]
-
-filegroup {
- name: "libui_host_common",
- srcs: [
- "Rect.cpp",
- "Region.cpp",
- "PixelFormat.cpp",
- "Transform.cpp",
- ],
-}
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 8675f14..bee58e5 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -22,14 +22,12 @@
#include <android-base/stringprintf.h>
#include <string>
-using android::base::StringAppendF;
using android::base::StringPrintf;
using android::ui::ColorMode;
using android::ui::RenderIntent;
-std::string decodeStandard(android_dataspace dataspace) {
- const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
- switch (dataspaceSelect) {
+std::string decodeStandardOnly(uint32_t dataspaceStandard) {
+ switch (dataspaceStandard) {
case HAL_DATASPACE_STANDARD_BT709:
return std::string("BT709");
@@ -62,63 +60,44 @@
case HAL_DATASPACE_STANDARD_ADOBE_RGB:
return std::string("AdobeRGB");
-
- case 0:
- switch (dataspace & 0xffff) {
- case HAL_DATASPACE_JFIF:
- return std::string("(deprecated) JFIF (BT601_625)");
-
- case HAL_DATASPACE_BT601_625:
- return std::string("(deprecated) BT601_625");
-
- case HAL_DATASPACE_BT601_525:
- return std::string("(deprecated) BT601_525");
-
- case HAL_DATASPACE_SRGB_LINEAR:
- case HAL_DATASPACE_SRGB:
- return std::string("(deprecated) sRGB");
-
- case HAL_DATASPACE_BT709:
- return std::string("(deprecated) BT709");
-
- case HAL_DATASPACE_ARBITRARY:
- return std::string("ARBITRARY");
-
- case HAL_DATASPACE_UNKNOWN:
- // Fallthrough
- default:
- return StringPrintf("Unknown deprecated dataspace code %d", dataspace);
- }
}
- return StringPrintf("Unknown dataspace code %d", dataspaceSelect);
+ return StringPrintf("Unknown dataspace code %d", dataspaceStandard);
}
-std::string decodeTransfer(android_dataspace dataspace) {
- const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
- if (dataspaceSelect == 0) {
+std::string decodeStandard(android_dataspace dataspace) {
+ const uint32_t dataspaceStandard = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+ if (dataspaceStandard == 0) {
switch (dataspace & 0xffff) {
case HAL_DATASPACE_JFIF:
+ return std::string("(deprecated) JFIF (BT601_625)");
+
case HAL_DATASPACE_BT601_625:
+ return std::string("(deprecated) BT601_625");
+
case HAL_DATASPACE_BT601_525:
- case HAL_DATASPACE_BT709:
- return std::string("SMPTE_170M");
+ return std::string("(deprecated) BT601_525");
case HAL_DATASPACE_SRGB_LINEAR:
- case HAL_DATASPACE_ARBITRARY:
- return std::string("Linear");
-
case HAL_DATASPACE_SRGB:
- return std::string("sRGB");
+ return std::string("(deprecated) sRGB");
+
+ case HAL_DATASPACE_BT709:
+ return std::string("(deprecated) BT709");
+
+ case HAL_DATASPACE_ARBITRARY:
+ return std::string("ARBITRARY");
case HAL_DATASPACE_UNKNOWN:
// Fallthrough
default:
- return std::string("");
+ return StringPrintf("Unknown deprecated dataspace code %d", dataspace);
}
}
+ return decodeStandardOnly(dataspaceStandard);
+}
- const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK);
+std::string decodeTransferOnly(uint32_t dataspaceTransfer) {
switch (dataspaceTransfer) {
case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
return std::string("Unspecified");
@@ -151,6 +130,52 @@
return StringPrintf("Unknown dataspace transfer %d", dataspaceTransfer);
}
+std::string decodeTransfer(android_dataspace dataspace) {
+ const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
+ if (dataspaceSelect == 0) {
+ switch (dataspace & 0xffff) {
+ case HAL_DATASPACE_JFIF:
+ case HAL_DATASPACE_BT601_625:
+ case HAL_DATASPACE_BT601_525:
+ case HAL_DATASPACE_BT709:
+ return std::string("SMPTE_170M");
+
+ case HAL_DATASPACE_SRGB_LINEAR:
+ case HAL_DATASPACE_ARBITRARY:
+ return std::string("Linear");
+
+ case HAL_DATASPACE_SRGB:
+ return std::string("sRGB");
+
+ case HAL_DATASPACE_UNKNOWN:
+ // Fallthrough
+ default:
+ return std::string("");
+ }
+ }
+
+ const uint32_t dataspaceTransfer = (dataspace & HAL_DATASPACE_TRANSFER_MASK);
+ return decodeTransferOnly(dataspaceTransfer);
+}
+
+std::string decodeRangeOnly(uint32_t dataspaceRange) {
+ switch (dataspaceRange) {
+ case HAL_DATASPACE_RANGE_UNSPECIFIED:
+ return std::string("Range Unspecified");
+
+ case HAL_DATASPACE_RANGE_FULL:
+ return std::string("Full range");
+
+ case HAL_DATASPACE_RANGE_LIMITED:
+ return std::string("Limited range");
+
+ case HAL_DATASPACE_RANGE_EXTENDED:
+ return std::string("Extended range");
+ }
+
+ return StringPrintf("Unknown dataspace range %d", dataspaceRange);
+}
+
std::string decodeRange(android_dataspace dataspace) {
const uint32_t dataspaceSelect = (dataspace & HAL_DATASPACE_STANDARD_MASK);
if (dataspaceSelect == 0) {
@@ -174,21 +199,7 @@
}
const uint32_t dataspaceRange = (dataspace & HAL_DATASPACE_RANGE_MASK);
- switch (dataspaceRange) {
- case HAL_DATASPACE_RANGE_UNSPECIFIED:
- return std::string("Range Unspecified");
-
- case HAL_DATASPACE_RANGE_FULL:
- return std::string("Full range");
-
- case HAL_DATASPACE_RANGE_LIMITED:
- return std::string("Limited range");
-
- case HAL_DATASPACE_RANGE_EXTENDED:
- return std::string("Extended range");
- }
-
- return StringPrintf("Unknown dataspace range %d", dataspaceRange);
+ return decodeRangeOnly(dataspaceRange);
}
std::string dataspaceDetails(android_dataspace dataspace) {
diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp
index 82e5427..0908ae8 100644
--- a/libs/ui/DisplayIdentification.cpp
+++ b/libs/ui/DisplayIdentification.cpp
@@ -23,67 +23,13 @@
#include <optional>
#include <span>
+#include <ftl/hash.h>
#include <log/log.h>
-
#include <ui/DisplayIdentification.h>
namespace android {
namespace {
-template <class T>
-inline T load(const void* p) {
- static_assert(std::is_integral<T>::value, "T must be integral");
-
- T r;
- std::memcpy(&r, p, sizeof(r));
- return r;
-}
-
-uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) {
- return (val >> shift) | (val << (64 - shift));
-}
-
-uint64_t shiftMix(uint64_t val) {
- return val ^ (val >> 47);
-}
-
-__attribute__((no_sanitize("unsigned-integer-overflow")))
-uint64_t hash64Len16(uint64_t u, uint64_t v) {
- constexpr uint64_t kMul = 0x9ddfea08eb382d69;
- uint64_t a = (u ^ v) * kMul;
- a ^= (a >> 47);
- uint64_t b = (v ^ a) * kMul;
- b ^= (b >> 47);
- b *= kMul;
- return b;
-}
-
-__attribute__((no_sanitize("unsigned-integer-overflow")))
-uint64_t hash64Len0To16(const char* s, uint64_t len) {
- constexpr uint64_t k2 = 0x9ae16a3b2f90404f;
- constexpr uint64_t k3 = 0xc949d7c7509e6557;
-
- if (len > 8) {
- const uint64_t a = load<uint64_t>(s);
- const uint64_t b = load<uint64_t>(s + len - 8);
- return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b;
- }
- if (len >= 4) {
- const uint32_t a = load<uint32_t>(s);
- const uint32_t b = load<uint32_t>(s + len - 4);
- return hash64Len16(len + (a << 3), b);
- }
- if (len > 0) {
- const unsigned char a = static_cast<unsigned char>(s[0]);
- const unsigned char b = static_cast<unsigned char>(s[len >> 1]);
- const unsigned char c = static_cast<unsigned char>(s[len - 1]);
- const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8);
- const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2);
- return shiftMix(y * k2 ^ z * k3) * k2;
- }
- return k2;
-}
-
using byte_view = std::span<const uint8_t>;
constexpr size_t kEdidBlockSize = 128;
@@ -320,7 +266,7 @@
// Hash model string instead of using product code or (integer) serial number, since the latter
// have been observed to change on some displays with multiple inputs. Use a stable hash instead
// of std::hash which is only required to be same within a single execution of a program.
- const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString));
+ const uint32_t modelHash = static_cast<uint32_t>(*ftl::stable_hash(modelString));
// Parse extension blocks.
std::optional<Cea861ExtensionBlock> cea861Block;
@@ -394,13 +340,4 @@
return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id);
}
-uint64_t cityHash64Len0To16(std::string_view sv) {
- auto len = sv.length();
- if (len > 16) {
- ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len);
- len = 16;
- }
- return hash64Len0To16(sv.data(), len);
-}
-
} // namespace android
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index 98082fb..1ebe597 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -291,5 +291,9 @@
return NO_ERROR;
}
+bool GraphicBufferAllocator::supportsAdditionalOptions() const {
+ return mAllocator->supportsAdditionalOptions();
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/include/ui/DebugUtils.h b/libs/ui/include/ui/DebugUtils.h
index 18cd487..7c4ac42 100644
--- a/libs/ui/include/ui/DebugUtils.h
+++ b/libs/ui/include/ui/DebugUtils.h
@@ -27,8 +27,11 @@
}
std::string decodeStandard(android_dataspace dataspace);
+std::string decodeStandardOnly(uint32_t dataspaceStandard);
std::string decodeTransfer(android_dataspace dataspace);
+std::string decodeTransferOnly(uint32_t dataspaceTransfer);
std::string decodeRange(android_dataspace dataspace);
+std::string decodeRangeOnly(uint32_t dataspaceRange);
std::string dataspaceDetails(android_dataspace dataspace);
std::string decodeColorMode(android::ui::ColorMode colormode);
std::string decodeColorTransform(android_color_transform colorTransform);
diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h
index fc9c0f4..8bc2017 100644
--- a/libs/ui/include/ui/DisplayIdentification.h
+++ b/libs/ui/include/ui/DisplayIdentification.h
@@ -80,7 +80,4 @@
PhysicalDisplayId getVirtualDisplayId(uint32_t id);
-// CityHash64 implementation that only hashes at most the first 16 characters of the given string.
-uint64_t cityHash64Len0To16(std::string_view sv);
-
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index e6015e0..4167dcb 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -226,6 +226,8 @@
const GraphicBufferAllocator::AllocationRequest&) const {
return GraphicBufferAllocator::AllocationResult(UNKNOWN_TRANSACTION);
}
+
+ virtual bool supportsAdditionalOptions() const { return false; }
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc5.h b/libs/ui/include/ui/Gralloc5.h
index f9e8f5e..5aa5019 100644
--- a/libs/ui/include/ui/Gralloc5.h
+++ b/libs/ui/include/ui/Gralloc5.h
@@ -178,6 +178,8 @@
[[nodiscard]] GraphicBufferAllocator::AllocationResult allocate(
const GraphicBufferAllocator::AllocationRequest&) const override;
+ bool supportsAdditionalOptions() const override { return true; }
+
private:
const Gralloc5Mapper &mMapper;
std::shared_ptr<aidl::android::hardware::graphics::allocator::IAllocator> mAllocator;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 8f461e1..bbb2d77 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -107,6 +107,8 @@
void dump(std::string& res, bool less = true) const;
static void dumpToSystemLog(bool less = true);
+ bool supportsAdditionalOptions() const;
+
protected:
struct alloc_rec_t {
uint32_t width;
diff --git a/libs/ui/include/ui/LayerStack.h b/libs/ui/include/ui/LayerStack.h
index d6ffeb7..f4c8ba2 100644
--- a/libs/ui/include/ui/LayerStack.h
+++ b/libs/ui/include/ui/LayerStack.h
@@ -55,6 +55,10 @@
return lhs.id > rhs.id;
}
+inline bool operator<(LayerStack lhs, LayerStack rhs) {
+ return lhs.id < rhs.id;
+}
+
// A LayerFilter determines if a layer is included for output to a display.
struct LayerFilter {
LayerStack layerStack;
diff --git a/libs/ui/include/ui/LogicalDisplayId.h b/libs/ui/include/ui/LogicalDisplayId.h
new file mode 100644
index 0000000..3e504e7
--- /dev/null
+++ b/libs/ui/include/ui/LogicalDisplayId.h
@@ -0,0 +1,56 @@
+/*
+ * 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/mixins.h>
+#include <sys/types.h>
+#include <string>
+
+#include <ostream>
+
+namespace android::ui {
+
+// Type-safe wrapper for a logical display id.
+struct LogicalDisplayId : ftl::Constructible<LogicalDisplayId, int32_t>,
+ ftl::Equatable<LogicalDisplayId>,
+ ftl::Orderable<LogicalDisplayId> {
+ using Constructible::Constructible;
+
+ constexpr auto val() const { return ftl::to_underlying(*this); }
+
+ constexpr bool isValid() const { return val() >= 0; }
+
+ std::string toString() const { return std::to_string(val()); }
+};
+
+constexpr LogicalDisplayId ADISPLAY_ID_NONE{-1};
+constexpr LogicalDisplayId ADISPLAY_ID_DEFAULT{0};
+
+inline std::ostream& operator<<(std::ostream& stream, LogicalDisplayId displayId) {
+ return stream << displayId.val();
+}
+
+} // namespace android::ui
+
+namespace std {
+template <>
+struct hash<android::ui::LogicalDisplayId> {
+ size_t operator()(const android::ui::LogicalDisplayId& displayId) const {
+ return hash<int32_t>()(displayId.val());
+ }
+};
+} // namespace std
\ No newline at end of file
diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp
index 736979a..721b466 100644
--- a/libs/ui/tests/DisplayIdentification_test.cpp
+++ b/libs/ui/tests/DisplayIdentification_test.cpp
@@ -21,9 +21,9 @@
#include <functional>
#include <string_view>
+#include <ftl/hash.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <ui/DisplayIdentification.h>
using ::testing::ElementsAre;
@@ -135,7 +135,7 @@
}
uint32_t hash(const char* str) {
- return static_cast<uint32_t>(cityHash64Len0To16(str));
+ return static_cast<uint32_t>(*ftl::stable_hash(str));
}
} // namespace
@@ -188,6 +188,7 @@
EXPECT_STREQ("SEC", edid->pnpId.data());
// ASCII text should be used as fallback if display name and serial number are missing.
EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+ EXPECT_EQ(hash("121AT11-801"), 626564263);
EXPECT_TRUE(edid->displayName.empty());
EXPECT_EQ(12610, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
@@ -199,6 +200,7 @@
EXPECT_EQ(0x22f0u, edid->manufacturerId);
EXPECT_STREQ("HWP", edid->pnpId.data());
EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
+ EXPECT_EQ(hash("HP ZR30w"), 918492362);
EXPECT_EQ("HP ZR30w", edid->displayName);
EXPECT_EQ(10348, edid->productId);
EXPECT_EQ(22, edid->manufactureOrModelYear);
@@ -210,6 +212,7 @@
EXPECT_EQ(0x4c2du, edid->manufacturerId);
EXPECT_STREQ("SAM", edid->pnpId.data());
EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
+ EXPECT_EQ(hash("SAMSUNG"), 1201368132);
EXPECT_EQ("SAMSUNG", edid->displayName);
EXPECT_EQ(2302, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
@@ -227,6 +230,7 @@
EXPECT_EQ(13481, edid->manufacturerId);
EXPECT_STREQ("MEI", edid->pnpId.data());
EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
+ EXPECT_EQ(hash("Panasonic-TV"), 3876373262);
EXPECT_EQ("Panasonic-TV", edid->displayName);
EXPECT_EQ(41622, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -244,6 +248,7 @@
EXPECT_EQ(8355, edid->manufacturerId);
EXPECT_STREQ("HEC", edid->pnpId.data());
EXPECT_EQ(hash("Hisense"), edid->modelHash);
+ EXPECT_EQ(hash("Hisense"), 2859844809);
EXPECT_EQ("Hisense", edid->displayName);
EXPECT_EQ(0, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -261,6 +266,7 @@
EXPECT_EQ(3724, edid->manufacturerId);
EXPECT_STREQ("CTL", edid->pnpId.data());
EXPECT_EQ(hash("LP2361"), edid->modelHash);
+ EXPECT_EQ(hash("LP2361"), 1523181158);
EXPECT_EQ("LP2361", edid->displayName);
EXPECT_EQ(9373, edid->productId);
EXPECT_EQ(23, edid->manufactureOrModelYear);
@@ -281,6 +287,7 @@
// Serial number should be used as fallback if display name is invalid.
const auto modelHash = hash("CN4202137Q");
EXPECT_EQ(modelHash, edid->modelHash);
+ EXPECT_EQ(modelHash, 3582951527);
EXPECT_TRUE(edid->displayName.empty());
// Parsing should succeed even if EDID is truncated.
diff --git a/libs/ultrahdr/OWNERS b/libs/ultrahdr/OWNERS
deleted file mode 100644
index 6ace354..0000000
--- a/libs/ultrahdr/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-arifdikici@google.com
-dichenzhang@google.com
-kyslov@google.com
\ No newline at end of file
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp b/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
deleted file mode 100644
index 2fa361f..0000000
--- a/libs/ultrahdr/adobe-hdr-gain-map-license/Android.bp
+++ /dev/null
@@ -1,19 +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.
-
-license {
- name: "adobe_hdr_gain_map_license-deprecated",
- license_kinds: ["legacy_by_exception_only"],
- license_text: ["NOTICE"],
-}
diff --git a/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE b/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
deleted file mode 100644
index 3f6c594..0000000
--- a/libs/ultrahdr/adobe-hdr-gain-map-license/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-This product includes Gain Map technology under license by Adobe.
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
deleted file mode 100644
index f1f4035..0000000
--- a/libs/ultrahdr/fuzzer/ultrahdr_dec_fuzzer.cpp
+++ /dev/null
@@ -1,73 +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.
- */
-
-// System include files
-#include <fuzzer/FuzzedDataProvider.h>
-#include <iostream>
-#include <vector>
-
-// User include files
-#include "ultrahdr/jpegr.h"
-
-using namespace android::ultrahdr;
-
-// Transfer functions for image data, sync with ultrahdr.h
-const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
-const int kOfMax = ULTRAHDR_OUTPUT_MAX;
-
-class UltraHdrDecFuzzer {
-public:
- UltraHdrDecFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
- void process();
-
-private:
- FuzzedDataProvider mFdp;
-};
-
-void UltraHdrDecFuzzer::process() {
- // hdr_of
- auto of = static_cast<ultrahdr_output_format>(mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
- auto buffer = mFdp.ConsumeRemainingBytes<uint8_t>();
- jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
- ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- JpegR jpegHdr;
- (void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
-//#define DUMP_PARAM
-#ifdef DUMP_PARAM
- std::cout << "input buffer size " << jpegImgR.length << std::endl;
- std::cout << "image dimensions " << info.width << " x " << info.width << std::endl;
-#endif
- size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- jpegr_uncompressed_struct decodedJpegR;
- auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
- decodedJpegR.data = decodedRaw.get();
- ultrahdr_metadata_struct metadata;
- jpegr_uncompressed_struct decodedGainMap{};
- (void)jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
- mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX), nullptr, of,
- &decodedGainMap, &metadata);
- if (decodedGainMap.data) free(decodedGainMap.data);
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- UltraHdrDecFuzzer fuzzHandle(data, size);
- fuzzHandle.process();
- return 0;
-}
diff --git a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp b/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
deleted file mode 100644
index 2d59e8b..0000000
--- a/libs/ultrahdr/fuzzer/ultrahdr_enc_fuzzer.cpp
+++ /dev/null
@@ -1,333 +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.
- */
-
-// System include files
-#include <fuzzer/FuzzedDataProvider.h>
-#include <algorithm>
-#include <iostream>
-#include <random>
-#include <vector>
-
-// User include files
-#include "ultrahdr/gainmapmath.h"
-#include "ultrahdr/jpegdecoderhelper.h"
-#include "ultrahdr/jpegencoderhelper.h"
-#include "utils/Log.h"
-
-using namespace android::ultrahdr;
-
-// Color gamuts for image data, sync with ultrahdr.h
-const int kCgMin = ULTRAHDR_COLORGAMUT_UNSPECIFIED + 1;
-const int kCgMax = ULTRAHDR_COLORGAMUT_MAX;
-
-// Transfer functions for image data, sync with ultrahdr.h
-const int kTfMin = ULTRAHDR_TF_UNSPECIFIED + 1;
-const int kTfMax = ULTRAHDR_TF_PQ;
-
-// Transfer functions for image data, sync with ultrahdr.h
-const int kOfMin = ULTRAHDR_OUTPUT_UNSPECIFIED + 1;
-const int kOfMax = ULTRAHDR_OUTPUT_MAX;
-
-// quality factor
-const int kQfMin = 0;
-const int kQfMax = 100;
-
-class UltraHdrEncFuzzer {
-public:
- UltraHdrEncFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};
- void process();
- void fillP010Buffer(uint16_t* data, int width, int height, int stride);
- void fill420Buffer(uint8_t* data, int width, int height, int stride);
-
-private:
- FuzzedDataProvider mFdp;
-};
-
-void UltraHdrEncFuzzer::fillP010Buffer(uint16_t* data, int width, int height, int stride) {
- uint16_t* tmp = data;
- std::vector<uint16_t> buffer(16);
- for (int i = 0; i < buffer.size(); i++) {
- buffer[i] = (mFdp.ConsumeIntegralInRange<int>(0, (1 << 10) - 1)) << 6;
- }
- for (int j = 0; j < height; j++) {
- for (int i = 0; i < width; i += buffer.size()) {
- memcpy(tmp + i, buffer.data(),
- std::min((int)buffer.size(), (width - i)) * sizeof(*data));
- std::shuffle(buffer.begin(), buffer.end(),
- std::default_random_engine(std::random_device{}()));
- }
- tmp += stride;
- }
-}
-
-void UltraHdrEncFuzzer::fill420Buffer(uint8_t* data, int width, int height, int stride) {
- uint8_t* tmp = data;
- std::vector<uint8_t> buffer(16);
- mFdp.ConsumeData(buffer.data(), buffer.size());
- for (int j = 0; j < height; j++) {
- for (int i = 0; i < width; i += buffer.size()) {
- memcpy(tmp + i, buffer.data(),
- std::min((int)buffer.size(), (width - i)) * sizeof(*data));
- std::shuffle(buffer.begin(), buffer.end(),
- std::default_random_engine(std::random_device{}()));
- }
- tmp += stride;
- }
-}
-
-void UltraHdrEncFuzzer::process() {
- while (mFdp.remaining_bytes()) {
- struct jpegr_uncompressed_struct p010Img {};
- struct jpegr_uncompressed_struct yuv420Img {};
- struct jpegr_uncompressed_struct grayImg {};
- struct jpegr_compressed_struct jpegImgR {};
- struct jpegr_compressed_struct jpegImg {};
- struct jpegr_compressed_struct jpegGainMap {};
-
- // which encode api to select
- int muxSwitch = mFdp.ConsumeIntegralInRange<int>(0, 4);
-
- // quality factor
- int quality = mFdp.ConsumeIntegralInRange<int>(kQfMin, kQfMax);
-
- // hdr_tf
- auto tf = static_cast<ultrahdr_transfer_function>(
- mFdp.ConsumeIntegralInRange<int>(kTfMin, kTfMax));
-
- // p010 Cg
- auto p010Cg =
- static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
-
- // 420 Cg
- auto yuv420Cg =
- static_cast<ultrahdr_color_gamut>(mFdp.ConsumeIntegralInRange<int>(kCgMin, kCgMax));
-
- // hdr_of
- auto of = static_cast<ultrahdr_output_format>(
- mFdp.ConsumeIntegralInRange<int>(kOfMin, kOfMax));
-
- int width = mFdp.ConsumeIntegralInRange<int>(kMinWidth, kMaxWidth);
- width = (width >> 1) << 1;
-
- int height = mFdp.ConsumeIntegralInRange<int>(kMinHeight, kMaxHeight);
- height = (height >> 1) << 1;
-
- std::unique_ptr<uint16_t[]> bufferYHdr = nullptr;
- std::unique_ptr<uint16_t[]> bufferUVHdr = nullptr;
- std::unique_ptr<uint8_t[]> bufferYSdr = nullptr;
- std::unique_ptr<uint8_t[]> bufferUVSdr = nullptr;
- std::unique_ptr<uint8_t[]> grayImgRaw = nullptr;
- if (muxSwitch != 4) {
- // init p010 image
- bool isUVContiguous = mFdp.ConsumeBool();
- bool hasYStride = mFdp.ConsumeBool();
- int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
- p010Img.width = width;
- p010Img.height = height;
- p010Img.colorGamut = p010Cg;
- p010Img.luma_stride = hasYStride ? yStride : 0;
- int bppP010 = 2;
- if (isUVContiguous) {
- size_t p010Size = yStride * height * 3 / 2;
- bufferYHdr = std::make_unique<uint16_t[]>(p010Size);
- p010Img.data = bufferYHdr.get();
- p010Img.chroma_data = nullptr;
- p010Img.chroma_stride = 0;
- fillP010Buffer(bufferYHdr.get(), width, height, yStride);
- fillP010Buffer(bufferYHdr.get() + yStride * height, width, height / 2, yStride);
- } else {
- int uvStride = mFdp.ConsumeIntegralInRange<int>(width, width + 128);
- size_t p010YSize = yStride * height;
- bufferYHdr = std::make_unique<uint16_t[]>(p010YSize);
- p010Img.data = bufferYHdr.get();
- fillP010Buffer(bufferYHdr.get(), width, height, yStride);
- size_t p010UVSize = uvStride * p010Img.height / 2;
- bufferUVHdr = std::make_unique<uint16_t[]>(p010UVSize);
- p010Img.chroma_data = bufferUVHdr.get();
- p010Img.chroma_stride = uvStride;
- fillP010Buffer(bufferUVHdr.get(), width, height / 2, uvStride);
- }
- } else {
- size_t map_width = width / kMapDimensionScaleFactor;
- size_t map_height = height / kMapDimensionScaleFactor;
- // init 400 image
- grayImg.width = map_width;
- grayImg.height = map_height;
- grayImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-
- const size_t graySize = map_width * map_height;
- grayImgRaw = std::make_unique<uint8_t[]>(graySize);
- grayImg.data = grayImgRaw.get();
- fill420Buffer(grayImgRaw.get(), map_width, map_height, map_width);
- grayImg.chroma_data = nullptr;
- grayImg.luma_stride = 0;
- grayImg.chroma_stride = 0;
- }
-
- if (muxSwitch > 0) {
- // init 420 image
- bool isUVContiguous = mFdp.ConsumeBool();
- bool hasYStride = mFdp.ConsumeBool();
- int yStride = hasYStride ? mFdp.ConsumeIntegralInRange<int>(width, width + 128) : width;
- yuv420Img.width = width;
- yuv420Img.height = height;
- yuv420Img.colorGamut = yuv420Cg;
- yuv420Img.luma_stride = hasYStride ? yStride : 0;
- if (isUVContiguous) {
- size_t yuv420Size = yStride * height * 3 / 2;
- bufferYSdr = std::make_unique<uint8_t[]>(yuv420Size);
- yuv420Img.data = bufferYSdr.get();
- yuv420Img.chroma_data = nullptr;
- yuv420Img.chroma_stride = 0;
- fill420Buffer(bufferYSdr.get(), width, height, yStride);
- fill420Buffer(bufferYSdr.get() + yStride * height, width / 2, height / 2,
- yStride / 2);
- fill420Buffer(bufferYSdr.get() + yStride * height * 5 / 4, width / 2, height / 2,
- yStride / 2);
- } else {
- int uvStride = mFdp.ConsumeIntegralInRange<int>(width / 2, width / 2 + 128);
- size_t yuv420YSize = yStride * height;
- bufferYSdr = std::make_unique<uint8_t[]>(yuv420YSize);
- yuv420Img.data = bufferYSdr.get();
- fill420Buffer(bufferYSdr.get(), width, height, yStride);
- size_t yuv420UVSize = uvStride * yuv420Img.height / 2 * 2;
- bufferUVSdr = std::make_unique<uint8_t[]>(yuv420UVSize);
- yuv420Img.chroma_data = bufferUVSdr.get();
- yuv420Img.chroma_stride = uvStride;
- fill420Buffer(bufferUVSdr.get(), width / 2, height / 2, uvStride);
- fill420Buffer(bufferUVSdr.get() + uvStride * height / 2, width / 2, height / 2,
- uvStride);
- }
- }
-
- // dest
- // 2 * p010 size as input data is random, DCT compression might not behave as expected
- jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
- auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
- jpegImgR.data = jpegImgRaw.get();
-
-//#define DUMP_PARAM
-#ifdef DUMP_PARAM
- std::cout << "Api Select " << muxSwitch << std::endl;
- std::cout << "image dimensions " << width << " x " << height << std::endl;
- std::cout << "p010 color gamut " << p010Img.colorGamut << std::endl;
- std::cout << "p010 luma stride " << p010Img.luma_stride << std::endl;
- std::cout << "p010 chroma stride " << p010Img.chroma_stride << std::endl;
- std::cout << "420 color gamut " << yuv420Img.colorGamut << std::endl;
- std::cout << "420 luma stride " << yuv420Img.luma_stride << std::endl;
- std::cout << "420 chroma stride " << yuv420Img.chroma_stride << std::endl;
- std::cout << "quality factor " << quality << std::endl;
-#endif
-
- JpegR jpegHdr;
- android::status_t status = android::UNKNOWN_ERROR;
- if (muxSwitch == 0) { // api 0
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, tf, &jpegImgR, quality, nullptr);
- } else if (muxSwitch == 1) { // api 1
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, tf, &jpegImgR, quality, nullptr);
- } else {
- // compressed img
- JpegEncoderHelper encoder;
- struct jpegr_uncompressed_struct yuv420ImgCopy = yuv420Img;
- if (yuv420ImgCopy.luma_stride == 0) yuv420ImgCopy.luma_stride = yuv420Img.width;
- if (!yuv420ImgCopy.chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420Img.data);
- yuv420ImgCopy.chroma_data = data + yuv420Img.luma_stride * yuv420Img.height;
- yuv420ImgCopy.chroma_stride = yuv420Img.luma_stride >> 1;
- }
-
- if (encoder.compressImage(reinterpret_cast<uint8_t*>(yuv420ImgCopy.data),
- reinterpret_cast<uint8_t*>(yuv420ImgCopy.chroma_data),
- yuv420ImgCopy.width, yuv420ImgCopy.height,
- yuv420ImgCopy.luma_stride, yuv420ImgCopy.chroma_stride,
- quality, nullptr, 0)) {
- jpegImg.length = encoder.getCompressedImageSize();
- jpegImg.maxLength = jpegImg.length;
- jpegImg.data = encoder.getCompressedImagePtr();
- jpegImg.colorGamut = yuv420Cg;
-
- if (muxSwitch == 2) { // api 2
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &yuv420Img, &jpegImg, tf, &jpegImgR);
- } else if (muxSwitch == 3) { // api 3
- jpegImgR.length = 0;
- status = jpegHdr.encodeJPEGR(&p010Img, &jpegImg, tf, &jpegImgR);
- } else if (muxSwitch == 4) { // api 4
- jpegImgR.length = 0;
- JpegEncoderHelper gainMapEncoder;
- if (gainMapEncoder.compressImage(reinterpret_cast<uint8_t*>(grayImg.data),
- nullptr, grayImg.width, grayImg.height,
- grayImg.width, 0, quality, nullptr, 0)) {
- jpegGainMap.length = gainMapEncoder.getCompressedImageSize();
- jpegGainMap.maxLength = jpegImg.length;
- jpegGainMap.data = gainMapEncoder.getCompressedImagePtr();
- jpegGainMap.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ultrahdr_metadata_struct metadata;
- metadata.version = kJpegrVersion;
- if (tf == ULTRAHDR_TF_HLG) {
- metadata.maxContentBoost = kHlgMaxNits / kSdrWhiteNits;
- } else if (tf == ULTRAHDR_TF_PQ) {
- metadata.maxContentBoost = kPqMaxNits / kSdrWhiteNits;
- } else {
- metadata.maxContentBoost = 1.0f;
- }
- metadata.minContentBoost = 1.0f;
- metadata.gamma = 1.0f;
- metadata.offsetSdr = 0.0f;
- metadata.offsetHdr = 0.0f;
- metadata.hdrCapacityMin = 1.0f;
- metadata.hdrCapacityMax = metadata.maxContentBoost;
- status = jpegHdr.encodeJPEGR(&jpegImg, &jpegGainMap, &metadata, &jpegImgR);
- }
- }
- }
- }
- if (status == android::OK) {
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
- if (status == android::OK) {
- size_t outSize =
- info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
- jpegr_uncompressed_struct decodedJpegR;
- auto decodedRaw = std::make_unique<uint8_t[]>(outSize);
- decodedJpegR.data = decodedRaw.get();
- ultrahdr_metadata_struct metadata;
- jpegr_uncompressed_struct decodedGainMap{};
- status = jpegHdr.decodeJPEGR(&jpegImgR, &decodedJpegR,
- mFdp.ConsumeFloatingPointInRange<float>(1.0, FLT_MAX),
- nullptr, of, &decodedGainMap, &metadata);
- if (status != android::OK) {
- ALOGE("encountered error during decoding %d", status);
- }
- if (decodedGainMap.data) free(decodedGainMap.data);
- } else {
- ALOGE("encountered error during get jpeg info %d", status);
- }
- } else {
- ALOGE("encountered error during encoding %d", status);
- }
- }
-}
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- UltraHdrEncFuzzer fuzzHandle(data, size);
- fuzzHandle.process();
- return 0;
-}
diff --git a/libs/ultrahdr/gainmapmath.cpp b/libs/ultrahdr/gainmapmath.cpp
deleted file mode 100644
index ae9c4ca..0000000
--- a/libs/ultrahdr/gainmapmath.cpp
+++ /dev/null
@@ -1,775 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cmath>
-#include <vector>
-#include <ultrahdr/gainmapmath.h>
-
-namespace android::ultrahdr {
-
-static const std::vector<float> kPqOETF = [] {
- std::vector<float> result;
- for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
- result.push_back(pqOetf(value));
- }
- return result;
-}();
-
-static const std::vector<float> kPqInvOETF = [] {
- std::vector<float> result;
- for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
- result.push_back(pqInvOetf(value));
- }
- return result;
-}();
-
-static const std::vector<float> kHlgOETF = [] {
- std::vector<float> result;
- for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
- result.push_back(hlgOetf(value));
- }
- return result;
-}();
-
-static const std::vector<float> kHlgInvOETF = [] {
- std::vector<float> result;
- for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
- result.push_back(hlgInvOetf(value));
- }
- return result;
-}();
-
-static const std::vector<float> kSrgbInvOETF = [] {
- std::vector<float> result;
- for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
- result.push_back(srgbInvOetf(value));
- }
- return result;
-}();
-
-// Use Shepard's method for inverse distance weighting. For more information:
-// en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
-
-float ShepardsIDW::euclideanDistance(float x1, float x2, float y1, float y2) {
- return sqrt(((y2 - y1) * (y2 - y1)) + (x2 - x1) * (x2 - x1));
-}
-
-void ShepardsIDW::fillShepardsIDW(float *weights, int incR, int incB) {
- for (int y = 0; y < mMapScaleFactor; y++) {
- for (int x = 0; x < mMapScaleFactor; x++) {
- float pos_x = ((float)x) / mMapScaleFactor;
- float pos_y = ((float)y) / mMapScaleFactor;
- int curr_x = floor(pos_x);
- int curr_y = floor(pos_y);
- int next_x = curr_x + incR;
- int next_y = curr_y + incB;
- float e1_distance = euclideanDistance(pos_x, curr_x, pos_y, curr_y);
- int index = y * mMapScaleFactor * 4 + x * 4;
- if (e1_distance == 0) {
- weights[index++] = 1.f;
- weights[index++] = 0.f;
- weights[index++] = 0.f;
- weights[index++] = 0.f;
- } else {
- float e1_weight = 1.f / e1_distance;
-
- float e2_distance = euclideanDistance(pos_x, curr_x, pos_y, next_y);
- float e2_weight = 1.f / e2_distance;
-
- float e3_distance = euclideanDistance(pos_x, next_x, pos_y, curr_y);
- float e3_weight = 1.f / e3_distance;
-
- float e4_distance = euclideanDistance(pos_x, next_x, pos_y, next_y);
- float e4_weight = 1.f / e4_distance;
-
- float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
-
- weights[index++] = e1_weight / total_weight;
- weights[index++] = e2_weight / total_weight;
- weights[index++] = e3_weight / total_weight;
- weights[index++] = e4_weight / total_weight;
- }
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// sRGB transformations
-
-static const float kMaxPixelFloat = 1.0f;
-static float clampPixelFloat(float value) {
- return (value < 0.0f) ? 0.0f : (value > kMaxPixelFloat) ? kMaxPixelFloat : value;
-}
-
-// See IEC 61966-2-1/Amd 1:2003, Equation F.7.
-static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
-
-float srgbLuminance(Color e) {
- return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b;
-}
-
-// See ITU-R BT.709-6, Section 3.
-// Uses the same coefficients for deriving luma signal as
-// IEC 61966-2-1/Amd 1:2003 states for luminance, so we reuse the luminance
-// function above.
-static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f;
-
-Color srgbRgbToYuv(Color e_gamma) {
- float y_gamma = srgbLuminance(e_gamma);
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kSrgbCb,
- (e_gamma.r - y_gamma) / kSrgbCr }}};
-}
-
-// See ITU-R BT.709-6, Section 3.
-// Same derivation to BT.2100's YUV->RGB, below. Similar to srgbRgbToYuv, we
-// can reuse the luminance coefficients since they are the same.
-static const float kSrgbGCb = kSrgbB * kSrgbCb / kSrgbG;
-static const float kSrgbGCr = kSrgbR * kSrgbCr / kSrgbG;
-
-Color srgbYuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kSrgbCr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kSrgbGCb * e_gamma.u - kSrgbGCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kSrgbCb * e_gamma.u) }}};
-}
-
-// See IEC 61966-2-1/Amd 1:2003, Equations F.5 and F.6.
-float srgbInvOetf(float e_gamma) {
- if (e_gamma <= 0.04045f) {
- return e_gamma / 12.92f;
- } else {
- return pow((e_gamma + 0.055f) / 1.055f, 2.4);
- }
-}
-
-Color srgbInvOetf(Color e_gamma) {
- return {{{ srgbInvOetf(e_gamma.r),
- srgbInvOetf(e_gamma.g),
- srgbInvOetf(e_gamma.b) }}};
-}
-
-// See IEC 61966-2-1, Equations F.5 and F.6.
-float srgbInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * (kSrgbInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- value = CLIP3(value, 0, kSrgbInvOETFNumEntries - 1);
- return kSrgbInvOETF[value];
-}
-
-Color srgbInvOetfLUT(Color e_gamma) {
- return {{{ srgbInvOetfLUT(e_gamma.r),
- srgbInvOetfLUT(e_gamma.g),
- srgbInvOetfLUT(e_gamma.b) }}};
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Display-P3 transformations
-
-// See SMPTE EG 432-1, Equation 7-8.
-static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
-
-float p3Luminance(Color e) {
- return kP3R * e.r + kP3G * e.g + kP3B * e.b;
-}
-
-// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
-// Unfortunately, calculation of luma signal differs from calculation of
-// luminance for Display-P3, so we can't reuse p3Luminance here.
-static const float kP3YR = 0.299f, kP3YG = 0.587f, kP3YB = 0.114f;
-static const float kP3Cb = 1.772f, kP3Cr = 1.402f;
-
-Color p3RgbToYuv(Color e_gamma) {
- float y_gamma = kP3YR * e_gamma.r + kP3YG * e_gamma.g + kP3YB * e_gamma.b;
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kP3Cb,
- (e_gamma.r - y_gamma) / kP3Cr }}};
-}
-
-// See ITU-R BT.601-7, Sections 2.5.1 and 2.5.2.
-// Same derivation to BT.2100's YUV->RGB, below. Similar to p3RgbToYuv, we must
-// use luma signal coefficients rather than the luminance coefficients.
-static const float kP3GCb = kP3YB * kP3Cb / kP3YG;
-static const float kP3GCr = kP3YR * kP3Cr / kP3YG;
-
-Color p3YuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kP3Cr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kP3GCb * e_gamma.u - kP3GCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kP3Cb * e_gamma.u) }}};
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-// BT.2100 transformations - according to ITU-R BT.2100-2
-
-// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
-static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
-
-float bt2100Luminance(Color e) {
- return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b;
-}
-
-// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
-// BT.2100 uses the same coefficients for calculating luma signal and luminance,
-// so we reuse the luminance function here.
-static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
-
-Color bt2100RgbToYuv(Color e_gamma) {
- float y_gamma = bt2100Luminance(e_gamma);
- return {{{ y_gamma,
- (e_gamma.b - y_gamma) / kBt2100Cb,
- (e_gamma.r - y_gamma) / kBt2100Cr }}};
-}
-
-// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
-//
-// Similar to bt2100RgbToYuv above, we can reuse the luminance coefficients.
-//
-// Derived by inversing bt2100RgbToYuv. The derivation for R and B are pretty
-// straight forward; we just invert the formulas for U and V above. But deriving
-// the formula for G is a bit more complicated:
-//
-// Start with equation for luminance:
-// Y = kBt2100R * R + kBt2100G * G + kBt2100B * B
-// Solve for G:
-// G = (Y - kBt2100R * R - kBt2100B * B) / kBt2100B
-// Substitute equations for R and B in terms YUV:
-// G = (Y - kBt2100R * (Y + kBt2100Cr * V) - kBt2100B * (Y + kBt2100Cb * U)) / kBt2100B
-// Simplify:
-// G = Y * ((1 - kBt2100R - kBt2100B) / kBt2100G)
-// + U * (kBt2100B * kBt2100Cb / kBt2100G)
-// + V * (kBt2100R * kBt2100Cr / kBt2100G)
-//
-// We then get the following coeficients for calculating G from YUV:
-//
-// Coef for Y = (1 - kBt2100R - kBt2100B) / kBt2100G = 1
-// Coef for U = kBt2100B * kBt2100Cb / kBt2100G = kBt2100GCb = ~0.1645
-// Coef for V = kBt2100R * kBt2100Cr / kBt2100G = kBt2100GCr = ~0.5713
-
-static const float kBt2100GCb = kBt2100B * kBt2100Cb / kBt2100G;
-static const float kBt2100GCr = kBt2100R * kBt2100Cr / kBt2100G;
-
-Color bt2100YuvToRgb(Color e_gamma) {
- return {{{ clampPixelFloat(e_gamma.y + kBt2100Cr * e_gamma.v),
- clampPixelFloat(e_gamma.y - kBt2100GCb * e_gamma.u - kBt2100GCr * e_gamma.v),
- clampPixelFloat(e_gamma.y + kBt2100Cb * e_gamma.u) }}};
-}
-
-// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
-static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
-
-float hlgOetf(float e) {
- if (e <= 1.0f/12.0f) {
- return sqrt(3.0f * e);
- } else {
- return kHlgA * log(12.0f * e - kHlgB) + kHlgC;
- }
-}
-
-Color hlgOetf(Color e) {
- return {{{ hlgOetf(e.r), hlgOetf(e.g), hlgOetf(e.b) }}};
-}
-
-float hlgOetfLUT(float e) {
- uint32_t value = static_cast<uint32_t>(e * (kHlgOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- value = CLIP3(value, 0, kHlgOETFNumEntries - 1);
-
- return kHlgOETF[value];
-}
-
-Color hlgOetfLUT(Color e) {
- return {{{ hlgOetfLUT(e.r), hlgOetfLUT(e.g), hlgOetfLUT(e.b) }}};
-}
-
-// See ITU-R BT.2100-2, Table 5, HLG Reference EOTF.
-float hlgInvOetf(float e_gamma) {
- if (e_gamma <= 0.5f) {
- return pow(e_gamma, 2.0f) / 3.0f;
- } else {
- return (exp((e_gamma - kHlgC) / kHlgA) + kHlgB) / 12.0f;
- }
-}
-
-Color hlgInvOetf(Color e_gamma) {
- return {{{ hlgInvOetf(e_gamma.r),
- hlgInvOetf(e_gamma.g),
- hlgInvOetf(e_gamma.b) }}};
-}
-
-float hlgInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * (kHlgInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- value = CLIP3(value, 0, kHlgInvOETFNumEntries - 1);
-
- return kHlgInvOETF[value];
-}
-
-Color hlgInvOetfLUT(Color e_gamma) {
- return {{{ hlgInvOetfLUT(e_gamma.r),
- hlgInvOetfLUT(e_gamma.g),
- hlgInvOetfLUT(e_gamma.b) }}};
-}
-
-// See ITU-R BT.2100-2, Table 4, Reference PQ OETF.
-static const float kPqM1 = 2610.0f / 16384.0f, kPqM2 = 2523.0f / 4096.0f * 128.0f;
-static const float kPqC1 = 3424.0f / 4096.0f, kPqC2 = 2413.0f / 4096.0f * 32.0f,
- kPqC3 = 2392.0f / 4096.0f * 32.0f;
-
-float pqOetf(float e) {
- if (e <= 0.0f) return 0.0f;
- return pow((kPqC1 + kPqC2 * pow(e, kPqM1)) / (1 + kPqC3 * pow(e, kPqM1)),
- kPqM2);
-}
-
-Color pqOetf(Color e) {
- return {{{ pqOetf(e.r), pqOetf(e.g), pqOetf(e.b) }}};
-}
-
-float pqOetfLUT(float e) {
- uint32_t value = static_cast<uint32_t>(e * (kPqOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- value = CLIP3(value, 0, kPqOETFNumEntries - 1);
-
- return kPqOETF[value];
-}
-
-Color pqOetfLUT(Color e) {
- return {{{ pqOetfLUT(e.r), pqOetfLUT(e.g), pqOetfLUT(e.b) }}};
-}
-
-// Derived from the inverse of the Reference PQ OETF.
-static const float kPqInvA = 128.0f, kPqInvB = 107.0f, kPqInvC = 2413.0f, kPqInvD = 2392.0f,
- kPqInvE = 6.2773946361f, kPqInvF = 0.0126833f;
-
-float pqInvOetf(float e_gamma) {
- // This equation blows up if e_gamma is 0.0, and checking on <= 0.0 doesn't
- // always catch 0.0. So, check on 0.0001, since anything this small will
- // effectively be crushed to zero anyways.
- if (e_gamma <= 0.0001f) return 0.0f;
- return pow((kPqInvA * pow(e_gamma, kPqInvF) - kPqInvB)
- / (kPqInvC - kPqInvD * pow(e_gamma, kPqInvF)),
- kPqInvE);
-}
-
-Color pqInvOetf(Color e_gamma) {
- return {{{ pqInvOetf(e_gamma.r),
- pqInvOetf(e_gamma.g),
- pqInvOetf(e_gamma.b) }}};
-}
-
-float pqInvOetfLUT(float e_gamma) {
- uint32_t value = static_cast<uint32_t>(e_gamma * (kPqInvOETFNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- value = CLIP3(value, 0, kPqInvOETFNumEntries - 1);
-
- return kPqInvOETF[value];
-}
-
-Color pqInvOetfLUT(Color e_gamma) {
- return {{{ pqInvOetfLUT(e_gamma.r),
- pqInvOetfLUT(e_gamma.g),
- pqInvOetfLUT(e_gamma.b) }}};
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Color conversions
-
-Color bt709ToP3(Color e) {
- return {{{ 0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
- 0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
- 0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b }}};
-}
-
-Color bt709ToBt2100(Color e) {
- return {{{ 0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
- 0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
- 0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b }}};
-}
-
-Color p3ToBt709(Color e) {
- return {{{ 1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
- -0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
- -0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b }}};
-}
-
-Color p3ToBt2100(Color e) {
- return {{{ 0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
- 0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
- -0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b }}};
-}
-
-Color bt2100ToBt709(Color e) {
- return {{{ 1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
- -0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
- -0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b }}};
-}
-
-Color bt2100ToP3(Color e) {
- return {{{ 1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
- -0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
- 0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b
- }}};
-}
-
-// TODO: confirm we always want to convert like this before calculating
-// luminance.
-ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut,
- ultrahdr_color_gamut hdr_gamut) {
- switch (sdr_gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- switch (hdr_gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- return identityConversion;
- case ULTRAHDR_COLORGAMUT_P3:
- return p3ToBt709;
- case ULTRAHDR_COLORGAMUT_BT2100:
- return bt2100ToBt709;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- return nullptr;
- }
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- switch (hdr_gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- return bt709ToP3;
- case ULTRAHDR_COLORGAMUT_P3:
- return identityConversion;
- case ULTRAHDR_COLORGAMUT_BT2100:
- return bt2100ToP3;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- return nullptr;
- }
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- switch (hdr_gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- return bt709ToBt2100;
- case ULTRAHDR_COLORGAMUT_P3:
- return p3ToBt2100;
- case ULTRAHDR_COLORGAMUT_BT2100:
- return identityConversion;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- return nullptr;
- }
- break;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- return nullptr;
- }
-}
-
-// All of these conversions are derived from the respective input YUV->RGB conversion followed by
-// the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in this
-// file, given that we uses BT.709 encoding for sRGB and BT.601 encoding for Display-P3, to match
-// DataSpace.
-
-Color yuv709To601(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.101579f * e_gamma.u + 0.196076f * e_gamma.v,
- 0.0f * e_gamma.y + 0.989854f * e_gamma.u + -0.110653f * e_gamma.v,
- 0.0f * e_gamma.y + -0.072453f * e_gamma.u + 0.983398f * e_gamma.v }}};
-}
-
-Color yuv709To2100(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.016969f * e_gamma.u + 0.096312f * e_gamma.v,
- 0.0f * e_gamma.y + 0.995306f * e_gamma.u + -0.051192f * e_gamma.v,
- 0.0f * e_gamma.y + 0.011507f * e_gamma.u + 1.002637f * e_gamma.v }}};
-}
-
-Color yuv601To709(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.118188f * e_gamma.u + -0.212685f * e_gamma.v,
- 0.0f * e_gamma.y + 1.018640f * e_gamma.u + 0.114618f * e_gamma.v,
- 0.0f * e_gamma.y + 0.075049f * e_gamma.u + 1.025327f * e_gamma.v }}};
-}
-
-Color yuv601To2100(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + -0.128245f * e_gamma.u + -0.115879f * e_gamma.v,
- 0.0f * e_gamma.y + 1.010016f * e_gamma.u + 0.061592f * e_gamma.v,
- 0.0f * e_gamma.y + 0.086969f * e_gamma.u + 1.029350f * e_gamma.v }}};
-}
-
-Color yuv2100To709(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.018149f * e_gamma.u + -0.095132f * e_gamma.v,
- 0.0f * e_gamma.y + 1.004123f * e_gamma.u + 0.051267f * e_gamma.v,
- 0.0f * e_gamma.y + -0.011524f * e_gamma.u + 0.996782f * e_gamma.v }}};
-}
-
-Color yuv2100To601(Color e_gamma) {
- return {{{ 1.0f * e_gamma.y + 0.117887f * e_gamma.u + 0.105521f * e_gamma.v,
- 0.0f * e_gamma.y + 0.995211f * e_gamma.u + -0.059549f * e_gamma.v,
- 0.0f * e_gamma.y + -0.084085f * e_gamma.u + 0.976518f * e_gamma.v }}};
-}
-
-void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
- ColorTransformFn fn) {
- Color yuv1 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 );
- Color yuv2 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 );
- Color yuv3 = getYuv420Pixel(image, x_chroma * 2, y_chroma * 2 + 1);
- Color yuv4 = getYuv420Pixel(image, x_chroma * 2 + 1, y_chroma * 2 + 1);
-
- yuv1 = fn(yuv1);
- yuv2 = fn(yuv2);
- yuv3 = fn(yuv3);
- yuv4 = fn(yuv4);
-
- Color new_uv = (yuv1 + yuv2 + yuv3 + yuv4) / 4.0f;
-
- size_t pixel_y1_idx = x_chroma * 2 + y_chroma * 2 * image->luma_stride;
- size_t pixel_y2_idx = (x_chroma * 2 + 1) + y_chroma * 2 * image->luma_stride;
- size_t pixel_y3_idx = x_chroma * 2 + (y_chroma * 2 + 1) * image->luma_stride;
- size_t pixel_y4_idx = (x_chroma * 2 + 1) + (y_chroma * 2 + 1) * image->luma_stride;
-
- uint8_t& y1_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y1_idx];
- uint8_t& y2_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y2_idx];
- uint8_t& y3_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y3_idx];
- uint8_t& y4_uint = reinterpret_cast<uint8_t*>(image->data)[pixel_y4_idx];
-
- size_t pixel_count = image->chroma_stride * image->height / 2;
- size_t pixel_uv_idx = x_chroma + y_chroma * (image->chroma_stride);
-
- uint8_t& u_uint = reinterpret_cast<uint8_t*>(image->chroma_data)[pixel_uv_idx];
- uint8_t& v_uint = reinterpret_cast<uint8_t*>(image->chroma_data)[pixel_count + pixel_uv_idx];
-
- y1_uint = static_cast<uint8_t>(CLIP3((yuv1.y * 255.0f + 0.5f), 0, 255));
- y2_uint = static_cast<uint8_t>(CLIP3((yuv2.y * 255.0f + 0.5f), 0, 255));
- y3_uint = static_cast<uint8_t>(CLIP3((yuv3.y * 255.0f + 0.5f), 0, 255));
- y4_uint = static_cast<uint8_t>(CLIP3((yuv4.y * 255.0f + 0.5f), 0, 255));
-
- u_uint = static_cast<uint8_t>(CLIP3((new_uv.u * 255.0f + 128.0f + 0.5f), 0, 255));
- v_uint = static_cast<uint8_t>(CLIP3((new_uv.v * 255.0f + 128.0f + 0.5f), 0, 255));
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Gain map calculations
-uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata) {
- return encodeGain(y_sdr, y_hdr, metadata,
- log2(metadata->minContentBoost), log2(metadata->maxContentBoost));
-}
-
-uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
- float log2MinContentBoost, float log2MaxContentBoost) {
- float gain = 1.0f;
- if (y_sdr > 0.0f) {
- gain = y_hdr / y_sdr;
- }
-
- if (gain < metadata->minContentBoost) gain = metadata->minContentBoost;
- if (gain > metadata->maxContentBoost) gain = metadata->maxContentBoost;
-
- return static_cast<uint8_t>((log2(gain) - log2MinContentBoost)
- / (log2MaxContentBoost - log2MinContentBoost)
- * 255.0f);
-}
-
-Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata) {
- float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
- + log2(metadata->maxContentBoost) * gain;
- float gainFactor = exp2(logBoost);
- return e * gainFactor;
-}
-
-Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost) {
- float logBoost = log2(metadata->minContentBoost) * (1.0f - gain)
- + log2(metadata->maxContentBoost) * gain;
- float gainFactor = exp2(logBoost * displayBoost / metadata->maxContentBoost);
- return e * gainFactor;
-}
-
-Color applyGainLUT(Color e, float gain, GainLUT& gainLUT) {
- float gainFactor = gainLUT.getGainFactor(gain);
- return e * gainFactor;
-}
-
-Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
- uint8_t* luma_data = reinterpret_cast<uint8_t*>(image->data);
- size_t luma_stride = image->luma_stride;
- uint8_t* chroma_data = reinterpret_cast<uint8_t*>(image->chroma_data);
- size_t chroma_stride = image->chroma_stride;
-
- size_t offset_cr = chroma_stride * (image->height / 2);
- size_t pixel_y_idx = x + y * luma_stride;
- size_t pixel_chroma_idx = x / 2 + (y / 2) * chroma_stride;
-
- uint8_t y_uint = luma_data[pixel_y_idx];
- uint8_t u_uint = chroma_data[pixel_chroma_idx];
- uint8_t v_uint = chroma_data[offset_cr + pixel_chroma_idx];
-
- // 128 bias for UV given we are using jpeglib; see:
- // https://github.com/kornelski/libjpeg/blob/master/structure.doc
- return {{{ static_cast<float>(y_uint) / 255.0f,
- (static_cast<float>(u_uint) - 128.0f) / 255.0f,
- (static_cast<float>(v_uint) - 128.0f) / 255.0f }}};
-}
-
-Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y) {
- uint16_t* luma_data = reinterpret_cast<uint16_t*>(image->data);
- size_t luma_stride = image->luma_stride == 0 ? image->width : image->luma_stride;
- uint16_t* chroma_data = reinterpret_cast<uint16_t*>(image->chroma_data);
- size_t chroma_stride = image->chroma_stride;
-
- size_t pixel_y_idx = y * luma_stride + x;
- size_t pixel_u_idx = (y >> 1) * chroma_stride + (x & ~0x1);
- size_t pixel_v_idx = pixel_u_idx + 1;
-
- uint16_t y_uint = luma_data[pixel_y_idx] >> 6;
- uint16_t u_uint = chroma_data[pixel_u_idx] >> 6;
- uint16_t v_uint = chroma_data[pixel_v_idx] >> 6;
-
- // Conversions include taking narrow-range into account.
- return {{{ (static_cast<float>(y_uint) - 64.0f) / 876.0f,
- (static_cast<float>(u_uint) - 64.0f) / 896.0f - 0.5f,
- (static_cast<float>(v_uint) - 64.0f) / 896.0f - 0.5f }}};
-}
-
-typedef Color (*getPixelFn)(jr_uncompressed_ptr, size_t, size_t);
-
-static Color samplePixels(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y,
- getPixelFn get_pixel_fn) {
- Color e = {{{ 0.0f, 0.0f, 0.0f }}};
- for (size_t dy = 0; dy < map_scale_factor; ++dy) {
- for (size_t dx = 0; dx < map_scale_factor; ++dx) {
- e += get_pixel_fn(image, x * map_scale_factor + dx, y * map_scale_factor + dy);
- }
- }
-
- return e / static_cast<float>(map_scale_factor * map_scale_factor);
-}
-
-Color sampleYuv420(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return samplePixels(image, map_scale_factor, x, y, getYuv420Pixel);
-}
-
-Color sampleP010(jr_uncompressed_ptr image, size_t map_scale_factor, size_t x, size_t y) {
- return samplePixels(image, map_scale_factor, x, y, getP010Pixel);
-}
-
-// TODO: do we need something more clever for filtering either the map or images
-// to generate the map?
-
-static size_t clamp(const size_t& val, const size_t& low, const size_t& high) {
- return val < low ? low : (high < val ? high : val);
-}
-
-static float mapUintToFloat(uint8_t map_uint) {
- return static_cast<float>(map_uint) / 255.0f;
-}
-
-static float pythDistance(float x_diff, float y_diff) {
- return sqrt(pow(x_diff, 2.0f) + pow(y_diff, 2.0f));
-}
-
-// TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
-float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y) {
- float x_map = static_cast<float>(x) / map_scale_factor;
- float y_map = static_cast<float>(y) / map_scale_factor;
-
- size_t x_lower = static_cast<size_t>(floor(x_map));
- size_t x_upper = x_lower + 1;
- size_t y_lower = static_cast<size_t>(floor(y_map));
- size_t y_upper = y_lower + 1;
-
- x_lower = clamp(x_lower, 0, map->width - 1);
- x_upper = clamp(x_upper, 0, map->width - 1);
- y_lower = clamp(y_lower, 0, map->height - 1);
- y_upper = clamp(y_upper, 0, map->height - 1);
-
- // Use Shepard's method for inverse distance weighting. For more information:
- // en.wikipedia.org/wiki/Inverse_distance_weighting#Shepard's_method
-
- float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
- float e1_dist = pythDistance(x_map - static_cast<float>(x_lower),
- y_map - static_cast<float>(y_lower));
- if (e1_dist == 0.0f) return e1;
-
- float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
- float e2_dist = pythDistance(x_map - static_cast<float>(x_lower),
- y_map - static_cast<float>(y_upper));
- if (e2_dist == 0.0f) return e2;
-
- float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
- float e3_dist = pythDistance(x_map - static_cast<float>(x_upper),
- y_map - static_cast<float>(y_lower));
- if (e3_dist == 0.0f) return e3;
-
- float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
- float e4_dist = pythDistance(x_map - static_cast<float>(x_upper),
- y_map - static_cast<float>(y_upper));
- if (e4_dist == 0.0f) return e2;
-
- float e1_weight = 1.0f / e1_dist;
- float e2_weight = 1.0f / e2_dist;
- float e3_weight = 1.0f / e3_dist;
- float e4_weight = 1.0f / e4_dist;
- float total_weight = e1_weight + e2_weight + e3_weight + e4_weight;
-
- return e1 * (e1_weight / total_weight)
- + e2 * (e2_weight / total_weight)
- + e3 * (e3_weight / total_weight)
- + e4 * (e4_weight / total_weight);
-}
-
-float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y,
- ShepardsIDW& weightTables) {
- // TODO: If map_scale_factor is guaranteed to be an integer power of 2, then optimize the
- // following by computing log2(map_scale_factor) once and then using >> log2(map_scale_factor)
- int x_lower = x / map_scale_factor;
- int x_upper = x_lower + 1;
- int y_lower = y / map_scale_factor;
- int y_upper = y_lower + 1;
-
- x_lower = std::min(x_lower, map->width - 1);
- x_upper = std::min(x_upper, map->width - 1);
- y_lower = std::min(y_lower, map->height - 1);
- y_upper = std::min(y_upper, map->height - 1);
-
- float e1 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_lower * map->width]);
- float e2 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_lower + y_upper * map->width]);
- float e3 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_lower * map->width]);
- float e4 = mapUintToFloat(reinterpret_cast<uint8_t*>(map->data)[x_upper + y_upper * map->width]);
-
- // TODO: If map_scale_factor is guaranteed to be an integer power of 2, then optimize the
- // following by using & (map_scale_factor - 1)
- int offset_x = x % map_scale_factor;
- int offset_y = y % map_scale_factor;
-
- float* weights = weightTables.mWeights;
- if (x_lower == x_upper && y_lower == y_upper) weights = weightTables.mWeightsC;
- else if (x_lower == x_upper) weights = weightTables.mWeightsNR;
- else if (y_lower == y_upper) weights = weightTables.mWeightsNB;
- weights += offset_y * map_scale_factor * 4 + offset_x * 4;
-
- return e1 * weights[0] + e2 * weights[1] + e3 * weights[2] + e4 * weights[3];
-}
-
-uint32_t colorToRgba1010102(Color e_gamma) {
- return (0x3ff & static_cast<uint32_t>(e_gamma.r * 1023.0f))
- | ((0x3ff & static_cast<uint32_t>(e_gamma.g * 1023.0f)) << 10)
- | ((0x3ff & static_cast<uint32_t>(e_gamma.b * 1023.0f)) << 20)
- | (0x3 << 30); // Set alpha to 1.0
-}
-
-uint64_t colorToRgbaF16(Color e_gamma) {
- return (uint64_t) floatToHalf(e_gamma.r)
- | (((uint64_t) floatToHalf(e_gamma.g)) << 16)
- | (((uint64_t) floatToHalf(e_gamma.b)) << 32)
- | (((uint64_t) floatToHalf(1.0f)) << 48);
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/icc.cpp b/libs/ultrahdr/icc.cpp
deleted file mode 100644
index e41b645..0000000
--- a/libs/ultrahdr/icc.cpp
+++ /dev/null
@@ -1,692 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef USE_BIG_ENDIAN
-#define USE_BIG_ENDIAN true
-#endif
-
-#include <ultrahdr/icc.h>
-#include <vector>
-#include <utils/Log.h>
-
-#ifndef FLT_MAX
-#define FLT_MAX 0x1.fffffep127f
-#endif
-
-namespace android::ultrahdr {
-static void Matrix3x3_apply(const Matrix3x3* m, float* x) {
- float y0 = x[0] * m->vals[0][0] + x[1] * m->vals[0][1] + x[2] * m->vals[0][2];
- float y1 = x[0] * m->vals[1][0] + x[1] * m->vals[1][1] + x[2] * m->vals[1][2];
- float y2 = x[0] * m->vals[2][0] + x[1] * m->vals[2][1] + x[2] * m->vals[2][2];
- x[0] = y0;
- x[1] = y1;
- x[2] = y2;
-}
-
-bool Matrix3x3_invert(const Matrix3x3* src, Matrix3x3* dst) {
- double a00 = src->vals[0][0],
- a01 = src->vals[1][0],
- a02 = src->vals[2][0],
- a10 = src->vals[0][1],
- a11 = src->vals[1][1],
- a12 = src->vals[2][1],
- a20 = src->vals[0][2],
- a21 = src->vals[1][2],
- a22 = src->vals[2][2];
-
- double b0 = a00*a11 - a01*a10,
- b1 = a00*a12 - a02*a10,
- b2 = a01*a12 - a02*a11,
- b3 = a20,
- b4 = a21,
- b5 = a22;
-
- double determinant = b0*b5
- - b1*b4
- + b2*b3;
-
- if (determinant == 0) {
- return false;
- }
-
- double invdet = 1.0 / determinant;
- if (invdet > +FLT_MAX || invdet < -FLT_MAX || !isfinitef_((float)invdet)) {
- return false;
- }
-
- b0 *= invdet;
- b1 *= invdet;
- b2 *= invdet;
- b3 *= invdet;
- b4 *= invdet;
- b5 *= invdet;
-
- dst->vals[0][0] = (float)( a11*b5 - a12*b4 );
- dst->vals[1][0] = (float)( a02*b4 - a01*b5 );
- dst->vals[2][0] = (float)( + b2 );
- dst->vals[0][1] = (float)( a12*b3 - a10*b5 );
- dst->vals[1][1] = (float)( a00*b5 - a02*b3 );
- dst->vals[2][1] = (float)( - b1 );
- dst->vals[0][2] = (float)( a10*b4 - a11*b3 );
- dst->vals[1][2] = (float)( a01*b3 - a00*b4 );
- dst->vals[2][2] = (float)( + b0 );
-
- for (int r = 0; r < 3; ++r)
- for (int c = 0; c < 3; ++c) {
- if (!isfinitef_(dst->vals[r][c])) {
- return false;
- }
- }
- return true;
-}
-
-static Matrix3x3 Matrix3x3_concat(const Matrix3x3* A, const Matrix3x3* B) {
- Matrix3x3 m = { { { 0,0,0 },{ 0,0,0 },{ 0,0,0 } } };
- for (int r = 0; r < 3; r++)
- for (int c = 0; c < 3; c++) {
- m.vals[r][c] = A->vals[r][0] * B->vals[0][c]
- + A->vals[r][1] * B->vals[1][c]
- + A->vals[r][2] * B->vals[2][c];
- }
- return m;
-}
-
-static void float_XYZD50_to_grid16_lab(const float* xyz_float, uint8_t* grid16_lab) {
- float v[3] = {
- xyz_float[0] / kD50_x,
- xyz_float[1] / kD50_y,
- xyz_float[2] / kD50_z,
- };
- for (size_t i = 0; i < 3; ++i) {
- v[i] = v[i] > 0.008856f ? cbrtf(v[i]) : v[i] * 7.787f + (16 / 116.0f);
- }
- const float L = v[1] * 116.0f - 16.0f;
- const float a = (v[0] - v[1]) * 500.0f;
- const float b = (v[1] - v[2]) * 200.0f;
- const float Lab_unorm[3] = {
- L * (1 / 100.f),
- (a + 128.0f) * (1 / 255.0f),
- (b + 128.0f) * (1 / 255.0f),
- };
- // This will encode L=1 as 0xFFFF. This matches how skcms will interpret the
- // table, but the spec appears to indicate that the value should be 0xFF00.
- // https://crbug.com/skia/13807
- for (size_t i = 0; i < 3; ++i) {
- reinterpret_cast<uint16_t*>(grid16_lab)[i] =
- Endian_SwapBE16(float_round_to_unorm16(Lab_unorm[i]));
- }
-}
-
-std::string IccHelper::get_desc_string(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut) {
- std::string result;
- switch (gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- result += "sRGB";
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- result += "Display P3";
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- result += "Rec2020";
- break;
- default:
- result += "Unknown";
- break;
- }
- result += " Gamut with ";
- switch (tf) {
- case ULTRAHDR_TF_SRGB:
- result += "sRGB";
- break;
- case ULTRAHDR_TF_LINEAR:
- result += "Linear";
- break;
- case ULTRAHDR_TF_PQ:
- result += "PQ";
- break;
- case ULTRAHDR_TF_HLG:
- result += "HLG";
- break;
- default:
- result += "Unknown";
- break;
- }
- result += " Transfer";
- return result;
-}
-
-sp<DataStruct> IccHelper::write_text_tag(const char* text) {
- uint32_t text_length = strlen(text);
- uint32_t header[] = {
- Endian_SwapBE32(kTAG_TextType), // Type signature
- 0, // Reserved
- Endian_SwapBE32(1), // Number of records
- Endian_SwapBE32(12), // Record size (must be 12)
- Endian_SwapBE32(SetFourByteTag('e', 'n', 'U', 'S')), // English USA
- Endian_SwapBE32(2 * text_length), // Length of string in bytes
- Endian_SwapBE32(28), // Offset of string
- };
-
- uint32_t total_length = text_length * 2 + sizeof(header);
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
-
- if (!dataStruct->write(header, sizeof(header))) {
- ALOGE("write_text_tag(): error in writing data");
- return dataStruct;
- }
-
- for (size_t i = 0; i < text_length; i++) {
- // Convert ASCII to big-endian UTF-16.
- dataStruct->write8(0);
- dataStruct->write8(text[i]);
- }
-
- return dataStruct;
-}
-
-sp<DataStruct> IccHelper::write_xyz_tag(float x, float y, float z) {
- uint32_t data[] = {
- Endian_SwapBE32(kXYZ_PCSSpace),
- 0,
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(x))),
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(y))),
- static_cast<uint32_t>(Endian_SwapBE32(float_round_to_fixed(z))),
- };
- sp<DataStruct> dataStruct = sp<DataStruct>::make(sizeof(data));
- dataStruct->write(&data, sizeof(data));
- return dataStruct;
-}
-
-sp<DataStruct> IccHelper::write_trc_tag(const int table_entries, const void* table_16) {
- int total_length = 4 + 4 + 4 + table_entries * 2;
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_CurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(table_entries)); // Value count
- for (size_t i = 0; i < table_entries; ++i) {
- uint16_t value = reinterpret_cast<const uint16_t*>(table_16)[i];
- dataStruct->write16(value);
- }
- return dataStruct;
-}
-
-sp<DataStruct> IccHelper::write_trc_tag(const TransferFunction& fn) {
- if (fn.a == 1.f && fn.b == 0.f && fn.c == 0.f
- && fn.d == 0.f && fn.e == 0.f && fn.f == 0.f) {
- int total_length = 16;
- sp<DataStruct> dataStruct = new DataStruct(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kExponential_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
- return dataStruct;
- }
-
- int total_length = 40;
- sp<DataStruct> dataStruct = new DataStruct(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_ParaCurveType)); // Type
- dataStruct->write32(0); // Reserved
- dataStruct->write32(Endian_SwapBE16(kGABCDEF_ParaCurveType));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.g)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.a)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.b)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.c)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.d)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.e)));
- dataStruct->write32(Endian_SwapBE32(float_round_to_fixed(fn.f)));
- return dataStruct;
-}
-
-float IccHelper::compute_tone_map_gain(const ultrahdr_transfer_function tf, float L) {
- if (L <= 0.f) {
- return 1.f;
- }
- if (tf == ULTRAHDR_TF_PQ) {
- // The PQ transfer function will map to the range [0, 1]. Linearly scale
- // it up to the range [0, 10,000/203]. We will then tone map that back
- // down to [0, 1].
- constexpr float kInputMaxLuminance = 10000 / 203.f;
- constexpr float kOutputMaxLuminance = 1.0;
- L *= kInputMaxLuminance;
-
- // Compute the tone map gain which will tone map from 10,000/203 to 1.0.
- constexpr float kToneMapA = kOutputMaxLuminance / (kInputMaxLuminance * kInputMaxLuminance);
- constexpr float kToneMapB = 1.f / kOutputMaxLuminance;
- return kInputMaxLuminance * (1.f + kToneMapA * L) / (1.f + kToneMapB * L);
- }
- if (tf == ULTRAHDR_TF_HLG) {
- // Let Lw be the brightness of the display in nits.
- constexpr float Lw = 203.f;
- const float gamma = 1.2f + 0.42f * std::log(Lw / 1000.f) / std::log(10.f);
- return std::pow(L, gamma - 1.f);
- }
- return 1.f;
-}
-
-sp<DataStruct> IccHelper::write_cicp_tag(uint32_t color_primaries,
- uint32_t transfer_characteristics) {
- int total_length = 12; // 4 + 4 + 1 + 1 + 1 + 1
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
- dataStruct->write32(Endian_SwapBE32(kTAG_cicp)); // Type signature
- dataStruct->write32(0); // Reserved
- dataStruct->write8(color_primaries); // Color primaries
- dataStruct->write8(transfer_characteristics); // Transfer characteristics
- dataStruct->write8(0); // RGB matrix
- dataStruct->write8(1); // Full range
- return dataStruct;
-}
-
-void IccHelper::compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]) {
- // Compute the matrices to convert from source to Rec2020, and from Rec2020 to XYZD50.
- Matrix3x3 src_to_rec2020;
- const Matrix3x3 rec2020_to_XYZD50 = kRec2020;
- {
- Matrix3x3 XYZD50_to_rec2020;
- Matrix3x3_invert(&rec2020_to_XYZD50, &XYZD50_to_rec2020);
- src_to_rec2020 = Matrix3x3_concat(&XYZD50_to_rec2020, &src_to_XYZD50);
- }
-
- // Convert the source signal to linear.
- for (size_t i = 0; i < kNumChannels; ++i) {
- rgb[i] = pqOetf(rgb[i]);
- }
-
- // Convert source gamut to Rec2020.
- Matrix3x3_apply(&src_to_rec2020, rgb);
-
- // Compute the luminance of the signal.
- float L = bt2100Luminance({{{rgb[0], rgb[1], rgb[2]}}});
-
- // Compute the tone map gain based on the luminance.
- float tone_map_gain = compute_tone_map_gain(ULTRAHDR_TF_PQ, L);
-
- // Apply the tone map gain.
- for (size_t i = 0; i < kNumChannels; ++i) {
- rgb[i] *= tone_map_gain;
- }
-
- // Convert from Rec2020-linear to XYZD50.
- Matrix3x3_apply(&rec2020_to_XYZD50, rgb);
-}
-
-sp<DataStruct> IccHelper::write_clut(const uint8_t* grid_points, const uint8_t* grid_16) {
- uint32_t value_count = kNumChannels;
- for (uint32_t i = 0; i < kNumChannels; ++i) {
- value_count *= grid_points[i];
- }
-
- int total_length = 20 + 2 * value_count;
- total_length = (((total_length + 2) >> 2) << 2); // 4 aligned
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
-
- for (size_t i = 0; i < 16; ++i) {
- dataStruct->write8(i < kNumChannels ? grid_points[i] : 0); // Grid size
- }
- dataStruct->write8(2); // Grid byte width (always 16-bit)
- dataStruct->write8(0); // Reserved
- dataStruct->write8(0); // Reserved
- dataStruct->write8(0); // Reserved
-
- for (uint32_t i = 0; i < value_count; ++i) {
- uint16_t value = reinterpret_cast<const uint16_t*>(grid_16)[i];
- dataStruct->write16(value);
- }
-
- return dataStruct;
-}
-
-sp<DataStruct> IccHelper::write_mAB_or_mBA_tag(uint32_t type,
- bool has_a_curves,
- const uint8_t* grid_points,
- const uint8_t* grid_16) {
- const size_t b_curves_offset = 32;
- sp<DataStruct> b_curves_data[kNumChannels];
- sp<DataStruct> a_curves_data[kNumChannels];
- size_t clut_offset = 0;
- sp<DataStruct> clut;
- size_t a_curves_offset = 0;
-
- // The "B" curve is required.
- for (size_t i = 0; i < kNumChannels; ++i) {
- b_curves_data[i] = write_trc_tag(kLinear_TransFun);
- }
-
- // The "A" curve and CLUT are optional.
- if (has_a_curves) {
- clut_offset = b_curves_offset;
- for (size_t i = 0; i < kNumChannels; ++i) {
- clut_offset += b_curves_data[i]->getLength();
- }
- clut = write_clut(grid_points, grid_16);
-
- a_curves_offset = clut_offset + clut->getLength();
- for (size_t i = 0; i < kNumChannels; ++i) {
- a_curves_data[i] = write_trc_tag(kLinear_TransFun);
- }
- }
-
- int total_length = b_curves_offset;
- for (size_t i = 0; i < kNumChannels; ++i) {
- total_length += b_curves_data[i]->getLength();
- }
- if (has_a_curves) {
- total_length += clut->getLength();
- for (size_t i = 0; i < kNumChannels; ++i) {
- total_length += a_curves_data[i]->getLength();
- }
- }
- sp<DataStruct> dataStruct = sp<DataStruct>::make(total_length);
- dataStruct->write32(Endian_SwapBE32(type)); // Type signature
- dataStruct->write32(0); // Reserved
- dataStruct->write8(kNumChannels); // Input channels
- dataStruct->write8(kNumChannels); // Output channels
- dataStruct->write16(0); // Reserved
- dataStruct->write32(Endian_SwapBE32(b_curves_offset)); // B curve offset
- dataStruct->write32(Endian_SwapBE32(0)); // Matrix offset (ignored)
- dataStruct->write32(Endian_SwapBE32(0)); // M curve offset (ignored)
- dataStruct->write32(Endian_SwapBE32(clut_offset)); // CLUT offset
- dataStruct->write32(Endian_SwapBE32(a_curves_offset)); // A curve offset
- for (size_t i = 0; i < kNumChannels; ++i) {
- if (dataStruct->write(b_curves_data[i]->getData(), b_curves_data[i]->getLength())) {
- return dataStruct;
- }
- }
- if (has_a_curves) {
- dataStruct->write(clut->getData(), clut->getLength());
- for (size_t i = 0; i < kNumChannels; ++i) {
- dataStruct->write(a_curves_data[i]->getData(), a_curves_data[i]->getLength());
- }
- }
- return dataStruct;
-}
-
-sp<DataStruct> IccHelper::writeIccProfile(ultrahdr_transfer_function tf,
- ultrahdr_color_gamut gamut) {
- ICCHeader header;
-
- std::vector<std::pair<uint32_t, sp<DataStruct>>> tags;
-
- // Compute profile description tag
- std::string desc = get_desc_string(tf, gamut);
-
- tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));
-
- Matrix3x3 toXYZD50;
- switch (gamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- toXYZD50 = kSRGB;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- toXYZD50 = kDisplayP3;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- toXYZD50 = kRec2020;
- break;
- default:
- // Should not fall here.
- return nullptr;
- }
-
- // Compute primaries.
- {
- tags.emplace_back(kTAG_rXYZ,
- write_xyz_tag(toXYZD50.vals[0][0], toXYZD50.vals[1][0], toXYZD50.vals[2][0]));
- tags.emplace_back(kTAG_gXYZ,
- write_xyz_tag(toXYZD50.vals[0][1], toXYZD50.vals[1][1], toXYZD50.vals[2][1]));
- tags.emplace_back(kTAG_bXYZ,
- write_xyz_tag(toXYZD50.vals[0][2], toXYZD50.vals[1][2], toXYZD50.vals[2][2]));
- }
-
- // Compute white point tag (must be D50)
- tags.emplace_back(kTAG_wtpt, write_xyz_tag(kD50_x, kD50_y, kD50_z));
-
- // Compute transfer curves.
- if (tf != ULTRAHDR_TF_PQ) {
- if (tf == ULTRAHDR_TF_HLG) {
- std::vector<uint8_t> trc_table;
- trc_table.resize(kTrcTableSize * 2);
- for (uint32_t i = 0; i < kTrcTableSize; ++i) {
- float x = i / (kTrcTableSize - 1.f);
- float y = hlgOetf(x);
- y *= compute_tone_map_gain(tf, y);
- float_to_table16(y, &trc_table[2 * i]);
- }
-
- tags.emplace_back(kTAG_rTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- tags.emplace_back(kTAG_gTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- tags.emplace_back(kTAG_bTRC,
- write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
- } else {
- tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
- tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
- tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
- }
- }
-
- // Compute CICP.
- if (tf == ULTRAHDR_TF_HLG || tf == ULTRAHDR_TF_PQ) {
- // The CICP tag is present in ICC 4.4, so update the header's version.
- header.version = Endian_SwapBE32(0x04400000);
-
- uint32_t color_primaries = 0;
- if (gamut == ULTRAHDR_COLORGAMUT_BT709) {
- color_primaries = kCICPPrimariesSRGB;
- } else if (gamut == ULTRAHDR_COLORGAMUT_P3) {
- color_primaries = kCICPPrimariesP3;
- }
-
- uint32_t transfer_characteristics = 0;
- if (tf == ULTRAHDR_TF_SRGB) {
- transfer_characteristics = kCICPTrfnSRGB;
- } else if (tf == ULTRAHDR_TF_LINEAR) {
- transfer_characteristics = kCICPTrfnLinear;
- } else if (tf == ULTRAHDR_TF_PQ) {
- transfer_characteristics = kCICPTrfnPQ;
- } else if (tf == ULTRAHDR_TF_HLG) {
- transfer_characteristics = kCICPTrfnHLG;
- }
- tags.emplace_back(kTAG_cicp, write_cicp_tag(color_primaries, transfer_characteristics));
- }
-
- // Compute A2B0.
- if (tf == ULTRAHDR_TF_PQ) {
- std::vector<uint8_t> a2b_grid;
- a2b_grid.resize(kGridSize * kGridSize * kGridSize * kNumChannels * 2);
- size_t a2b_grid_index = 0;
- for (uint32_t r_index = 0; r_index < kGridSize; ++r_index) {
- for (uint32_t g_index = 0; g_index < kGridSize; ++g_index) {
- for (uint32_t b_index = 0; b_index < kGridSize; ++b_index) {
- float rgb[3] = {
- r_index / (kGridSize - 1.f),
- g_index / (kGridSize - 1.f),
- b_index / (kGridSize - 1.f),
- };
- compute_lut_entry(toXYZD50, rgb);
- float_XYZD50_to_grid16_lab(rgb, &a2b_grid[a2b_grid_index]);
- a2b_grid_index += 6;
- }
- }
- }
- const uint8_t* grid_16 = reinterpret_cast<const uint8_t*>(a2b_grid.data());
-
- uint8_t grid_points[kNumChannels];
- for (size_t i = 0; i < kNumChannels; ++i) {
- grid_points[i] = kGridSize;
- }
-
- auto a2b_data = write_mAB_or_mBA_tag(kTAG_mABType,
- /* has_a_curves */ true,
- grid_points,
- grid_16);
- tags.emplace_back(kTAG_A2B0, std::move(a2b_data));
- }
-
- // Compute B2A0.
- if (tf == ULTRAHDR_TF_PQ) {
- auto b2a_data = write_mAB_or_mBA_tag(kTAG_mBAType,
- /* has_a_curves */ false,
- /* grid_points */ nullptr,
- /* grid_16 */ nullptr);
- tags.emplace_back(kTAG_B2A0, std::move(b2a_data));
- }
-
- // Compute copyright tag
- tags.emplace_back(kTAG_cprt, write_text_tag("Google Inc. 2022"));
-
- // Compute the size of the profile.
- size_t tag_data_size = 0;
- for (const auto& tag : tags) {
- tag_data_size += tag.second->getLength();
- }
- size_t tag_table_size = kICCTagTableEntrySize * tags.size();
- size_t profile_size = kICCHeaderSize + tag_table_size + tag_data_size;
-
- sp<DataStruct> dataStruct = sp<DataStruct>::make(profile_size + kICCIdentifierSize);
-
- // Write identifier, chunk count, and chunk ID
- if (!dataStruct->write(kICCIdentifier, sizeof(kICCIdentifier)) ||
- !dataStruct->write8(1) || !dataStruct->write8(1)) {
- ALOGE("writeIccProfile(): error in identifier");
- return dataStruct;
- }
-
- // Write the header.
- header.data_color_space = Endian_SwapBE32(Signature_RGB);
- header.pcs = Endian_SwapBE32(tf == ULTRAHDR_TF_PQ ? Signature_Lab : Signature_XYZ);
- header.size = Endian_SwapBE32(profile_size);
- header.tag_count = Endian_SwapBE32(tags.size());
-
- if (!dataStruct->write(&header, sizeof(header))) {
- ALOGE("writeIccProfile(): error in header");
- return dataStruct;
- }
-
- // Write the tag table. Track the offset and size of the previous tag to
- // compute each tag's offset. An empty SkData indicates that the previous
- // tag is to be reused.
- uint32_t last_tag_offset = sizeof(header) + tag_table_size;
- uint32_t last_tag_size = 0;
- for (const auto& tag : tags) {
- last_tag_offset = last_tag_offset + last_tag_size;
- last_tag_size = tag.second->getLength();
- uint32_t tag_table_entry[3] = {
- Endian_SwapBE32(tag.first),
- Endian_SwapBE32(last_tag_offset),
- Endian_SwapBE32(last_tag_size),
- };
- if (!dataStruct->write(tag_table_entry, sizeof(tag_table_entry))) {
- ALOGE("writeIccProfile(): error in writing tag table");
- return dataStruct;
- }
- }
-
- // Write the tags.
- for (const auto& tag : tags) {
- if (!dataStruct->write(tag.second->getData(), tag.second->getLength())) {
- ALOGE("writeIccProfile(): error in writing tags");
- return dataStruct;
- }
- }
-
- return dataStruct;
-}
-
-bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix,
- const uint8_t* red_tag,
- const uint8_t* green_tag,
- const uint8_t* blue_tag) {
- sp<DataStruct> red_tag_test = write_xyz_tag(matrix.vals[0][0], matrix.vals[1][0],
- matrix.vals[2][0]);
- sp<DataStruct> green_tag_test = write_xyz_tag(matrix.vals[0][1], matrix.vals[1][1],
- matrix.vals[2][1]);
- sp<DataStruct> blue_tag_test = write_xyz_tag(matrix.vals[0][2], matrix.vals[1][2],
- matrix.vals[2][2]);
- return memcmp(red_tag, red_tag_test->getData(), kColorantTagSize) == 0 &&
- memcmp(green_tag, green_tag_test->getData(), kColorantTagSize) == 0 &&
- memcmp(blue_tag, blue_tag_test->getData(), kColorantTagSize) == 0;
-}
-
-ultrahdr_color_gamut IccHelper::readIccColorGamut(void* icc_data, size_t icc_size) {
- // Each tag table entry consists of 3 fields of 4 bytes each.
- static const size_t kTagTableEntrySize = 12;
-
- if (icc_data == nullptr || icc_size < sizeof(ICCHeader) + kICCIdentifierSize) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- if (memcmp(icc_data, kICCIdentifier, sizeof(kICCIdentifier)) != 0) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc_data) + kICCIdentifierSize;
-
- ICCHeader* header = reinterpret_cast<ICCHeader*>(icc_bytes);
-
- // Use 0 to indicate not found, since offsets are always relative to start
- // of ICC data and therefore a tag offset of zero would never be valid.
- size_t red_primary_offset = 0, green_primary_offset = 0, blue_primary_offset = 0;
- size_t red_primary_size = 0, green_primary_size = 0, blue_primary_size = 0;
- for (size_t tag_idx = 0; tag_idx < Endian_SwapBE32(header->tag_count); ++tag_idx) {
- uint32_t* tag_entry_start = reinterpret_cast<uint32_t*>(
- icc_bytes + sizeof(ICCHeader) + tag_idx * kTagTableEntrySize);
- // first 4 bytes are the tag signature, next 4 bytes are the tag offset,
- // last 4 bytes are the tag length in bytes.
- if (red_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_rXYZ)) {
- red_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- red_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- } else if (green_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_gXYZ)) {
- green_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- green_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- } else if (blue_primary_offset == 0 && *tag_entry_start == Endian_SwapBE32(kTAG_bXYZ)) {
- blue_primary_offset = Endian_SwapBE32(*(tag_entry_start+1));
- blue_primary_size = Endian_SwapBE32(*(tag_entry_start+2));
- }
- }
-
- if (red_primary_offset == 0 || red_primary_size != kColorantTagSize ||
- kICCIdentifierSize + red_primary_offset + red_primary_size > icc_size ||
- green_primary_offset == 0 || green_primary_size != kColorantTagSize ||
- kICCIdentifierSize + green_primary_offset + green_primary_size > icc_size ||
- blue_primary_offset == 0 || blue_primary_size != kColorantTagSize ||
- kICCIdentifierSize + blue_primary_offset + blue_primary_size > icc_size) {
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- }
-
- uint8_t* red_tag = icc_bytes + red_primary_offset;
- uint8_t* green_tag = icc_bytes + green_primary_offset;
- uint8_t* blue_tag = icc_bytes + blue_primary_offset;
-
- // Serialize tags as we do on encode and compare what we find to that to
- // determine the gamut (since we don't have a need yet for full deserialize).
- if (tagsEqualToMatrix(kSRGB, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_BT709;
- } else if (tagsEqualToMatrix(kDisplayP3, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_P3;
- } else if (tagsEqualToMatrix(kRec2020, red_tag, green_tag, blue_tag)) {
- return ULTRAHDR_COLORGAMUT_BT2100;
- }
-
- // Didn't find a match to one of the profiles we write; indicate the gamut
- // is unspecified since we don't understand it.
- return ULTRAHDR_COLORGAMUT_UNSPECIFIED;
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/include/ultrahdr/gainmapmath.h b/libs/ultrahdr/include/ultrahdr/gainmapmath.h
deleted file mode 100644
index 9f1238f..0000000
--- a/libs/ultrahdr/include/ultrahdr/gainmapmath.h
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
-#define ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
-
-#include <cmath>
-#include <stdint.h>
-
-#include <ultrahdr/jpegr.h>
-
-namespace android::ultrahdr {
-
-#define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
-
-////////////////////////////////////////////////////////////////////////////////
-// Framework
-
-const float kSdrWhiteNits = 100.0f;
-const float kHlgMaxNits = 1000.0f;
-const float kPqMaxNits = 10000.0f;
-
-struct Color {
- union {
- struct {
- float r;
- float g;
- float b;
- };
- struct {
- float y;
- float u;
- float v;
- };
- };
-};
-
-typedef Color (*ColorTransformFn)(Color);
-typedef float (*ColorCalculationFn)(Color);
-
-// A transfer function mapping encoded values to linear values,
-// represented by this 7-parameter piecewise function:
-//
-// linear = sign(encoded) * (c*|encoded| + f) , 0 <= |encoded| < d
-// = sign(encoded) * ((a*|encoded| + b)^g + e), d <= |encoded|
-//
-// (A simple gamma transfer function sets g to gamma and a to 1.)
-typedef struct TransferFunction {
- float g, a,b,c,d,e,f;
-} TransferFunction;
-
-static constexpr TransferFunction kSRGB_TransFun =
- { 2.4f, (float)(1/1.055), (float)(0.055/1.055), (float)(1/12.92), 0.04045f, 0.0f, 0.0f };
-
-static constexpr TransferFunction kLinear_TransFun =
- { 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
-
-inline Color operator+=(Color& lhs, const Color& rhs) {
- lhs.r += rhs.r;
- lhs.g += rhs.g;
- lhs.b += rhs.b;
- return lhs;
-}
-inline Color operator-=(Color& lhs, const Color& rhs) {
- lhs.r -= rhs.r;
- lhs.g -= rhs.g;
- lhs.b -= rhs.b;
- return lhs;
-}
-
-inline Color operator+(const Color& lhs, const Color& rhs) {
- Color temp = lhs;
- return temp += rhs;
-}
-inline Color operator-(const Color& lhs, const Color& rhs) {
- Color temp = lhs;
- return temp -= rhs;
-}
-
-inline Color operator+=(Color& lhs, const float rhs) {
- lhs.r += rhs;
- lhs.g += rhs;
- lhs.b += rhs;
- return lhs;
-}
-inline Color operator-=(Color& lhs, const float rhs) {
- lhs.r -= rhs;
- lhs.g -= rhs;
- lhs.b -= rhs;
- return lhs;
-}
-inline Color operator*=(Color& lhs, const float rhs) {
- lhs.r *= rhs;
- lhs.g *= rhs;
- lhs.b *= rhs;
- return lhs;
-}
-inline Color operator/=(Color& lhs, const float rhs) {
- lhs.r /= rhs;
- lhs.g /= rhs;
- lhs.b /= rhs;
- return lhs;
-}
-
-inline Color operator+(const Color& lhs, const float rhs) {
- Color temp = lhs;
- return temp += rhs;
-}
-inline Color operator-(const Color& lhs, const float rhs) {
- Color temp = lhs;
- return temp -= rhs;
-}
-inline Color operator*(const Color& lhs, const float rhs) {
- Color temp = lhs;
- return temp *= rhs;
-}
-inline Color operator/(const Color& lhs, const float rhs) {
- Color temp = lhs;
- return temp /= rhs;
-}
-
-inline uint16_t floatToHalf(float f) {
- // round-to-nearest-even: add last bit after truncated mantissa
- const uint32_t b = *((uint32_t*)&f) + 0x00001000;
-
- const uint32_t e = (b & 0x7F800000) >> 23; // exponent
- const uint32_t m = b & 0x007FFFFF; // mantissa
-
- // sign : normalized : denormalized : saturate
- return (b & 0x80000000) >> 16
- | (e > 112) * ((((e - 112) << 10) & 0x7C00) | m >> 13)
- | ((e < 113) & (e > 101)) * ((((0x007FF000 + m) >> (125 - e)) + 1) >> 1)
- | (e > 143) * 0x7FFF;
-}
-
-constexpr size_t kGainFactorPrecision = 10;
-constexpr size_t kGainFactorNumEntries = 1 << kGainFactorPrecision;
-struct GainLUT {
- GainLUT(ultrahdr_metadata_ptr metadata) {
- for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
- + log2(metadata->maxContentBoost) * value;
- mGainTable[idx] = exp2(logBoost);
- }
- }
-
- GainLUT(ultrahdr_metadata_ptr metadata, float displayBoost) {
- float boostFactor = displayBoost > 0 ? displayBoost / metadata->maxContentBoost : 1.0f;
- for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- float logBoost = log2(metadata->minContentBoost) * (1.0f - value)
- + log2(metadata->maxContentBoost) * value;
- mGainTable[idx] = exp2(logBoost * boostFactor);
- }
- }
-
- ~GainLUT() {
- }
-
- float getGainFactor(float gain) {
- uint32_t idx = static_cast<uint32_t>(gain * (kGainFactorNumEntries - 1) + 0.5);
- //TODO() : Remove once conversion modules have appropriate clamping in place
- idx = CLIP3(idx, 0, kGainFactorNumEntries - 1);
- return mGainTable[idx];
- }
-
-private:
- float mGainTable[kGainFactorNumEntries];
-};
-
-struct ShepardsIDW {
- ShepardsIDW(int mapScaleFactor) : mMapScaleFactor{mapScaleFactor} {
- const int size = mMapScaleFactor * mMapScaleFactor * 4;
- mWeights = new float[size];
- mWeightsNR = new float[size];
- mWeightsNB = new float[size];
- mWeightsC = new float[size];
- fillShepardsIDW(mWeights, 1, 1);
- fillShepardsIDW(mWeightsNR, 0, 1);
- fillShepardsIDW(mWeightsNB, 1, 0);
- fillShepardsIDW(mWeightsC, 0, 0);
- }
- ~ShepardsIDW() {
- delete[] mWeights;
- delete[] mWeightsNR;
- delete[] mWeightsNB;
- delete[] mWeightsC;
- }
-
- int mMapScaleFactor;
- // Image :-
- // p00 p01 p02 p03 p04 p05 p06 p07
- // p10 p11 p12 p13 p14 p15 p16 p17
- // p20 p21 p22 p23 p24 p25 p26 p27
- // p30 p31 p32 p33 p34 p35 p36 p37
- // p40 p41 p42 p43 p44 p45 p46 p47
- // p50 p51 p52 p53 p54 p55 p56 p57
- // p60 p61 p62 p63 p64 p65 p66 p67
- // p70 p71 p72 p73 p74 p75 p76 p77
-
- // Gain Map (for 4 scale factor) :-
- // m00 p01
- // m10 m11
-
- // Gain sample of curr 4x4, right 4x4, bottom 4x4, bottom right 4x4 are used during
- // reconstruction. hence table weight size is 4.
- float* mWeights;
- // TODO: check if its ok to mWeights at places
- float* mWeightsNR; // no right
- float* mWeightsNB; // no bottom
- float* mWeightsC; // no right & bottom
-
- float euclideanDistance(float x1, float x2, float y1, float y2);
- void fillShepardsIDW(float *weights, int incR, int incB);
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// sRGB transformations
-// NOTE: sRGB has the same color primaries as BT.709, but different transfer
-// function. For this reason, all sRGB transformations here apply to BT.709,
-// except for those concerning transfer functions.
-
-/*
- * Calculate the luminance of a linear RGB sRGB pixel, according to
- * IEC 61966-2-1/Amd 1:2003.
- *
- * [0.0, 1.0] range in and out.
- */
-float srgbLuminance(Color e);
-
-/*
- * Convert from OETF'd srgb RGB to YUV, according to ITU-R BT.709-6.
- *
- * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color srgbRgbToYuv(Color e_gamma);
-
-
-/*
- * Convert from OETF'd srgb YUV to RGB, according to ITU-R BT.709-6.
- *
- * BT.709 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color srgbYuvToRgb(Color e_gamma);
-
-/*
- * Convert from srgb to linear, according to IEC 61966-2-1/Amd 1:2003.
- *
- * [0.0, 1.0] range in and out.
- */
-float srgbInvOetf(float e_gamma);
-Color srgbInvOetf(Color e_gamma);
-float srgbInvOetfLUT(float e_gamma);
-Color srgbInvOetfLUT(Color e_gamma);
-
-constexpr size_t kSrgbInvOETFPrecision = 10;
-constexpr size_t kSrgbInvOETFNumEntries = 1 << kSrgbInvOETFPrecision;
-
-////////////////////////////////////////////////////////////////////////////////
-// Display-P3 transformations
-
-/*
- * Calculated the luminance of a linear RGB P3 pixel, according to SMPTE EG 432-1.
- *
- * [0.0, 1.0] range in and out.
- */
-float p3Luminance(Color e);
-
-/*
- * Convert from OETF'd P3 RGB to YUV, according to ITU-R BT.601-7.
- *
- * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color p3RgbToYuv(Color e_gamma);
-
-/*
- * Convert from OETF'd P3 YUV to RGB, according to ITU-R BT.601-7.
- *
- * BT.601 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color p3YuvToRgb(Color e_gamma);
-
-
-////////////////////////////////////////////////////////////////////////////////
-// BT.2100 transformations - according to ITU-R BT.2100-2
-
-/*
- * Calculate the luminance of a linear RGB BT.2100 pixel.
- *
- * [0.0, 1.0] range in and out.
- */
-float bt2100Luminance(Color e);
-
-/*
- * Convert from OETF'd BT.2100 RGB to YUV, according to ITU-R BT.2100-2.
- *
- * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color bt2100RgbToYuv(Color e_gamma);
-
-/*
- * Convert from OETF'd BT.2100 YUV to RGB, according to ITU-R BT.2100-2.
- *
- * BT.2100 YUV<->RGB matrix is used to match expectations for DataSpace.
- */
-Color bt2100YuvToRgb(Color e_gamma);
-
-/*
- * Convert from scene luminance to HLG.
- *
- * [0.0, 1.0] range in and out.
- */
-float hlgOetf(float e);
-Color hlgOetf(Color e);
-float hlgOetfLUT(float e);
-Color hlgOetfLUT(Color e);
-
-constexpr size_t kHlgOETFPrecision = 16;
-constexpr size_t kHlgOETFNumEntries = 1 << kHlgOETFPrecision;
-
-/*
- * Convert from HLG to scene luminance.
- *
- * [0.0, 1.0] range in and out.
- */
-float hlgInvOetf(float e_gamma);
-Color hlgInvOetf(Color e_gamma);
-float hlgInvOetfLUT(float e_gamma);
-Color hlgInvOetfLUT(Color e_gamma);
-
-constexpr size_t kHlgInvOETFPrecision = 12;
-constexpr size_t kHlgInvOETFNumEntries = 1 << kHlgInvOETFPrecision;
-
-/*
- * Convert from scene luminance to PQ.
- *
- * [0.0, 1.0] range in and out.
- */
-float pqOetf(float e);
-Color pqOetf(Color e);
-float pqOetfLUT(float e);
-Color pqOetfLUT(Color e);
-
-constexpr size_t kPqOETFPrecision = 16;
-constexpr size_t kPqOETFNumEntries = 1 << kPqOETFPrecision;
-
-/*
- * Convert from PQ to scene luminance in nits.
- *
- * [0.0, 1.0] range in and out.
- */
-float pqInvOetf(float e_gamma);
-Color pqInvOetf(Color e_gamma);
-float pqInvOetfLUT(float e_gamma);
-Color pqInvOetfLUT(Color e_gamma);
-
-constexpr size_t kPqInvOETFPrecision = 12;
-constexpr size_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Color space conversions
-
-/*
- * Convert between color spaces with linear RGB data, according to ITU-R BT.2407 and EG 432-1.
- *
- * All conversions are derived from multiplying the matrix for XYZ to output RGB color gamut by the
- * matrix for input RGB color gamut to XYZ. The matrix for converting from XYZ to an RGB gamut is
- * always the inverse of the RGB gamut to XYZ matrix.
- */
-Color bt709ToP3(Color e);
-Color bt709ToBt2100(Color e);
-Color p3ToBt709(Color e);
-Color p3ToBt2100(Color e);
-Color bt2100ToBt709(Color e);
-Color bt2100ToP3(Color e);
-
-/*
- * Identity conversion.
- */
-inline Color identityConversion(Color e) { return e; }
-
-/*
- * Get the conversion to apply to the HDR image for gain map generation
- */
-ColorTransformFn getHdrConversionFn(ultrahdr_color_gamut sdr_gamut, ultrahdr_color_gamut hdr_gamut);
-
-/*
- * Convert between YUV encodings, according to ITU-R BT.709-6, ITU-R BT.601-7, and ITU-R BT.2100-2.
- *
- * Bt.709 and Bt.2100 have well-defined YUV encodings; Display-P3's is less well defined, but is
- * treated as Bt.601 by DataSpace, hence we do the same.
- */
-Color yuv709To601(Color e_gamma);
-Color yuv709To2100(Color e_gamma);
-Color yuv601To709(Color e_gamma);
-Color yuv601To2100(Color e_gamma);
-Color yuv2100To709(Color e_gamma);
-Color yuv2100To601(Color e_gamma);
-
-/*
- * Performs a transformation at the chroma x and y coordinates provided on a YUV420 image.
- *
- * Apply the transformation by determining transformed YUV for each of the 4 Y + 1 UV; each Y gets
- * this result, and UV gets the averaged result.
- *
- * x_chroma and y_chroma should be less than or equal to half the image's width and height
- * respecitively, since input is 4:2:0 subsampled.
- */
-void transformYuv420(jr_uncompressed_ptr image, size_t x_chroma, size_t y_chroma,
- ColorTransformFn fn);
-
-
-////////////////////////////////////////////////////////////////////////////////
-// Gain map calculations
-
-/*
- * Calculate the 8-bit unsigned integer gain value for the given SDR and HDR
- * luminances in linear space, and the hdr ratio to encode against.
- *
- * Note: since this library always uses gamma of 1.0, offsetSdr of 0.0, and
- * offsetHdr of 0.0, this function doesn't handle different metadata values for
- * these fields.
- */
-uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata);
-uint8_t encodeGain(float y_sdr, float y_hdr, ultrahdr_metadata_ptr metadata,
- float log2MinContentBoost, float log2MaxContentBoost);
-
-/*
- * Calculates the linear luminance in nits after applying the given gain
- * value, with the given hdr ratio, to the given sdr input in the range [0, 1].
- *
- * Note: similar to encodeGain(), this function only supports gamma 1.0,
- * offsetSdr 0.0, offsetHdr 0.0, hdrCapacityMin 1.0, and hdrCapacityMax equal to
- * gainMapMax, as this library encodes.
- */
-Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata);
-Color applyGain(Color e, float gain, ultrahdr_metadata_ptr metadata, float displayBoost);
-Color applyGainLUT(Color e, float gain, GainLUT& gainLUT);
-
-/*
- * Helper for sampling from YUV 420 images.
- */
-Color getYuv420Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
-
-/*
- * Helper for sampling from P010 images.
- *
- * Expect narrow-range image data for P010.
- */
-Color getP010Pixel(jr_uncompressed_ptr image, size_t x, size_t y);
-
-/*
- * Sample the image at the provided location, with a weighting based on nearby
- * pixels and the map scale factor.
- */
-Color sampleYuv420(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-
-/*
- * Sample the image at the provided location, with a weighting based on nearby
- * pixels and the map scale factor.
- *
- * Expect narrow-range image data for P010.
- */
-Color sampleP010(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y);
-
-/*
- * Sample the gain value for the map from a given x,y coordinate on a scale
- * that is map scale factor larger than the map size.
- */
-float sampleMap(jr_uncompressed_ptr map, float map_scale_factor, size_t x, size_t y);
-float sampleMap(jr_uncompressed_ptr map, size_t map_scale_factor, size_t x, size_t y,
- ShepardsIDW& weightTables);
-
-/*
- * Convert from Color to RGBA1010102.
- *
- * Alpha always set to 1.0.
- */
-uint32_t colorToRgba1010102(Color e_gamma);
-
-/*
- * Convert from Color to F16.
- *
- * Alpha always set to 1.0.
- */
-uint64_t colorToRgbaF16(Color e_gamma);
-
-} // namespace android::ultrahdr
-
-#endif // ANDROID_ULTRAHDR_RECOVERYMAPMATH_H
diff --git a/libs/ultrahdr/include/ultrahdr/icc.h b/libs/ultrahdr/include/ultrahdr/icc.h
deleted file mode 100644
index 971b267..0000000
--- a/libs/ultrahdr/include/ultrahdr/icc.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_ICC_H
-#define ANDROID_ULTRAHDR_ICC_H
-
-#include <ultrahdr/gainmapmath.h>
-#include <ultrahdr/jpegr.h>
-#include <ultrahdr/jpegrutils.h>
-#include <utils/RefBase.h>
-#include <cmath>
-#include <string>
-
-#ifdef USE_BIG_ENDIAN
-#undef USE_BIG_ENDIAN
-#define USE_BIG_ENDIAN true
-#endif
-
-namespace android::ultrahdr {
-
-typedef int32_t Fixed;
-#define Fixed1 (1 << 16)
-#define MaxS32FitsInFloat 2147483520
-#define MinS32FitsInFloat (-MaxS32FitsInFloat)
-#define FixedToFloat(x) ((x) * 1.52587890625e-5f)
-
-typedef struct Matrix3x3 {
- float vals[3][3];
-} Matrix3x3;
-
-// The D50 illuminant.
-constexpr float kD50_x = 0.9642f;
-constexpr float kD50_y = 1.0000f;
-constexpr float kD50_z = 0.8249f;
-
-enum {
- // data_color_space
- Signature_CMYK = 0x434D594B,
- Signature_Gray = 0x47524159,
- Signature_RGB = 0x52474220,
-
- // pcs
- Signature_Lab = 0x4C616220,
- Signature_XYZ = 0x58595A20,
-};
-
-typedef uint32_t FourByteTag;
-static inline constexpr FourByteTag SetFourByteTag(char a, char b, char c, char d) {
- return (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d);
-}
-
-static constexpr char kICCIdentifier[] = "ICC_PROFILE";
-// 12 for the actual identifier, +2 for the chunk count and chunk index which
-// will always follow.
-static constexpr size_t kICCIdentifierSize = 14;
-
-// This is equal to the header size according to the ICC specification (128)
-// plus the size of the tag count (4). We include the tag count since we
-// always require it to be present anyway.
-static constexpr size_t kICCHeaderSize = 132;
-
-// Contains a signature (4), offset (4), and size (4).
-static constexpr size_t kICCTagTableEntrySize = 12;
-
-// size should be 20; 4 bytes for type descriptor, 4 bytes reserved, 12
-// bytes for a single XYZ number type (4 bytes per coordinate).
-static constexpr size_t kColorantTagSize = 20;
-
-static constexpr uint32_t kDisplay_Profile = SetFourByteTag('m', 'n', 't', 'r');
-static constexpr uint32_t kRGB_ColorSpace = SetFourByteTag('R', 'G', 'B', ' ');
-static constexpr uint32_t kXYZ_PCSSpace = SetFourByteTag('X', 'Y', 'Z', ' ');
-static constexpr uint32_t kACSP_Signature = SetFourByteTag('a', 'c', 's', 'p');
-
-static constexpr uint32_t kTAG_desc = SetFourByteTag('d', 'e', 's', 'c');
-static constexpr uint32_t kTAG_TextType = SetFourByteTag('m', 'l', 'u', 'c');
-static constexpr uint32_t kTAG_rXYZ = SetFourByteTag('r', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_gXYZ = SetFourByteTag('g', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_bXYZ = SetFourByteTag('b', 'X', 'Y', 'Z');
-static constexpr uint32_t kTAG_wtpt = SetFourByteTag('w', 't', 'p', 't');
-static constexpr uint32_t kTAG_rTRC = SetFourByteTag('r', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_gTRC = SetFourByteTag('g', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_bTRC = SetFourByteTag('b', 'T', 'R', 'C');
-static constexpr uint32_t kTAG_cicp = SetFourByteTag('c', 'i', 'c', 'p');
-static constexpr uint32_t kTAG_cprt = SetFourByteTag('c', 'p', 'r', 't');
-static constexpr uint32_t kTAG_A2B0 = SetFourByteTag('A', '2', 'B', '0');
-static constexpr uint32_t kTAG_B2A0 = SetFourByteTag('B', '2', 'A', '0');
-
-static constexpr uint32_t kTAG_CurveType = SetFourByteTag('c', 'u', 'r', 'v');
-static constexpr uint32_t kTAG_mABType = SetFourByteTag('m', 'A', 'B', ' ');
-static constexpr uint32_t kTAG_mBAType = SetFourByteTag('m', 'B', 'A', ' ');
-static constexpr uint32_t kTAG_ParaCurveType = SetFourByteTag('p', 'a', 'r', 'a');
-
-
-static constexpr Matrix3x3 kSRGB = {{
- // ICC fixed-point (16.16) representation, taken from skcms. Please keep them exactly in sync.
- // 0.436065674f, 0.385147095f, 0.143066406f,
- // 0.222488403f, 0.716873169f, 0.060607910f,
- // 0.013916016f, 0.097076416f, 0.714096069f,
- { FixedToFloat(0x6FA2), FixedToFloat(0x6299), FixedToFloat(0x24A0) },
- { FixedToFloat(0x38F5), FixedToFloat(0xB785), FixedToFloat(0x0F84) },
- { FixedToFloat(0x0390), FixedToFloat(0x18DA), FixedToFloat(0xB6CF) },
-}};
-
-static constexpr Matrix3x3 kDisplayP3 = {{
- { 0.515102f, 0.291965f, 0.157153f },
- { 0.241182f, 0.692236f, 0.0665819f },
- { -0.00104941f, 0.0418818f, 0.784378f },
-}};
-
-static constexpr Matrix3x3 kRec2020 = {{
- { 0.673459f, 0.165661f, 0.125100f },
- { 0.279033f, 0.675338f, 0.0456288f },
- { -0.00193139f, 0.0299794f, 0.797162f },
-}};
-
-static constexpr uint32_t kCICPPrimariesSRGB = 1;
-static constexpr uint32_t kCICPPrimariesP3 = 12;
-static constexpr uint32_t kCICPPrimariesRec2020 = 9;
-
-static constexpr uint32_t kCICPTrfnSRGB = 1;
-static constexpr uint32_t kCICPTrfnLinear = 8;
-static constexpr uint32_t kCICPTrfnPQ = 16;
-static constexpr uint32_t kCICPTrfnHLG = 18;
-
-enum ParaCurveType {
- kExponential_ParaCurveType = 0,
- kGAB_ParaCurveType = 1,
- kGABC_ParaCurveType = 2,
- kGABDE_ParaCurveType = 3,
- kGABCDEF_ParaCurveType = 4,
-};
-
-/**
- * Return the closest int for the given float. Returns MaxS32FitsInFloat for NaN.
- */
-static inline int float_saturate2int(float x) {
- x = x < MaxS32FitsInFloat ? x : MaxS32FitsInFloat;
- x = x > MinS32FitsInFloat ? x : MinS32FitsInFloat;
- return (int)x;
-}
-
-static Fixed float_round_to_fixed(float x) {
- return float_saturate2int((float)floor((double)x * Fixed1 + 0.5));
-}
-
-static uint16_t float_round_to_unorm16(float x) {
- x = x * 65535.f + 0.5;
- if (x > 65535) return 65535;
- if (x < 0) return 0;
- return static_cast<uint16_t>(x);
-}
-
-static void float_to_table16(const float f, uint8_t* table_16) {
- *reinterpret_cast<uint16_t*>(table_16) = Endian_SwapBE16(float_round_to_unorm16(f));
-}
-
-static bool isfinitef_(float x) { return 0 == x*0; }
-
-struct ICCHeader {
- // Size of the profile (computed)
- uint32_t size;
- // Preferred CMM type (ignored)
- uint32_t cmm_type = 0;
- // Version 4.3 or 4.4 if CICP is included.
- uint32_t version = Endian_SwapBE32(0x04300000);
- // Display device profile
- uint32_t profile_class = Endian_SwapBE32(kDisplay_Profile);
- // RGB input color space;
- uint32_t data_color_space = Endian_SwapBE32(kRGB_ColorSpace);
- // Profile connection space.
- uint32_t pcs = Endian_SwapBE32(kXYZ_PCSSpace);
- // Date and time (ignored)
- uint8_t creation_date_time[12] = {0};
- // Profile signature
- uint32_t signature = Endian_SwapBE32(kACSP_Signature);
- // Platform target (ignored)
- uint32_t platform = 0;
- // Flags: not embedded, can be used independently
- uint32_t flags = 0x00000000;
- // Device manufacturer (ignored)
- uint32_t device_manufacturer = 0;
- // Device model (ignored)
- uint32_t device_model = 0;
- // Device attributes (ignored)
- uint8_t device_attributes[8] = {0};
- // Relative colorimetric rendering intent
- uint32_t rendering_intent = Endian_SwapBE32(1);
- // D50 standard illuminant (X, Y, Z)
- uint32_t illuminant_X = Endian_SwapBE32(float_round_to_fixed(kD50_x));
- uint32_t illuminant_Y = Endian_SwapBE32(float_round_to_fixed(kD50_y));
- uint32_t illuminant_Z = Endian_SwapBE32(float_round_to_fixed(kD50_z));
- // Profile creator (ignored)
- uint32_t creator = 0;
- // Profile id checksum (ignored)
- uint8_t profile_id[16] = {0};
- // Reserved (ignored)
- uint8_t reserved[28] = {0};
- // Technically not part of header, but required
- uint32_t tag_count = 0;
-};
-
-class IccHelper {
-private:
- static constexpr uint32_t kTrcTableSize = 65;
- static constexpr uint32_t kGridSize = 17;
- static constexpr size_t kNumChannels = 3;
-
- static sp<DataStruct> write_text_tag(const char* text);
- static std::string get_desc_string(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut);
- static sp<DataStruct> write_xyz_tag(float x, float y, float z);
- static sp<DataStruct> write_trc_tag(const int table_entries, const void* table_16);
- static sp<DataStruct> write_trc_tag(const TransferFunction& fn);
- static float compute_tone_map_gain(const ultrahdr_transfer_function tf, float L);
- static sp<DataStruct> write_cicp_tag(uint32_t color_primaries,
- uint32_t transfer_characteristics);
- static sp<DataStruct> write_mAB_or_mBA_tag(uint32_t type,
- bool has_a_curves,
- const uint8_t* grid_points,
- const uint8_t* grid_16);
- static void compute_lut_entry(const Matrix3x3& src_to_XYZD50, float rgb[3]);
- static sp<DataStruct> write_clut(const uint8_t* grid_points, const uint8_t* grid_16);
-
- // Checks if a set of xyz tags is equivalent to a 3x3 Matrix. Each input
- // tag buffer assumed to be at least kColorantTagSize in size.
- static bool tagsEqualToMatrix(const Matrix3x3& matrix,
- const uint8_t* red_tag,
- const uint8_t* green_tag,
- const uint8_t* blue_tag);
-
-public:
- // Output includes JPEG embedding identifier and chunk information, but not
- // APPx information.
- static sp<DataStruct> writeIccProfile(const ultrahdr_transfer_function tf,
- const ultrahdr_color_gamut gamut);
- // NOTE: this function is not robust; it can infer gamuts that IccHelper
- // writes out but should not be considered a reference implementation for
- // robust parsing of ICC profiles or their gamuts.
- static ultrahdr_color_gamut readIccColorGamut(void* icc_data, size_t icc_size);
-};
-} // namespace android::ultrahdr
-
-#endif //ANDROID_ULTRAHDR_ICC_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
deleted file mode 100644
index b86ce5f4..0000000
--- a/libs/ultrahdr/include/ultrahdr/jpegdecoderhelper.h
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
-#define ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
-
-// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
-#include <cstdio>
-extern "C" {
-#include <jerror.h>
-#include <jpeglib.h>
-}
-#include <utils/Errors.h>
-#include <vector>
-
-// constraint on max width and max height is only due to device alloc constraints
-// Can tune these values basing on the target device
-static const int kMaxWidth = 8192;
-static const int kMaxHeight = 8192;
-
-namespace android::ultrahdr {
-/*
- * Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
- * This class is not thread-safe.
- */
-class JpegDecoderHelper {
-public:
- JpegDecoderHelper();
- ~JpegDecoderHelper();
- /*
- * Decompresses JPEG image to raw image (YUV420planer, grey-scale or RGBA) format. After
- * calling this method, call getDecompressedImage() to get the image.
- * Returns false if decompressing the image fails.
- */
- bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
- /*
- * Returns the decompressed raw image buffer pointer. This method must be called only after
- * calling decompressImage().
- */
- void* getDecompressedImagePtr();
- /*
- * Returns the decompressed raw image buffer size. This method must be called only after
- * calling decompressImage().
- */
- size_t getDecompressedImageSize();
- /*
- * Returns the image width in pixels. This method must be called only after calling
- * decompressImage().
- */
- size_t getDecompressedImageWidth();
- /*
- * Returns the image width in pixels. This method must be called only after calling
- * decompressImage().
- */
- size_t getDecompressedImageHeight();
- /*
- * Returns the XMP data from the image.
- */
- void* getXMPPtr();
- /*
- * Returns the decompressed XMP buffer size. This method must be called only after
- * calling decompressImage() or getCompressedImageParameters().
- */
- size_t getXMPSize();
- /*
- * Extracts EXIF package and updates the EXIF position / length without decoding the image.
- */
- bool extractEXIF(const void* image, int length);
- /*
- * Returns the EXIF data from the image.
- * This method must be called after extractEXIF() or decompressImage().
- */
- void* getEXIFPtr();
- /*
- * Returns the decompressed EXIF buffer size. This method must be called only after
- * calling decompressImage(), extractEXIF() or getCompressedImageParameters().
- */
- size_t getEXIFSize();
- /*
- * Returns the position offset of EXIF package
- * (4 bypes offset to FF sign, the byte after FF E1 XX XX <this byte>),
- * or -1 if no EXIF exists.
- * This method must be called after extractEXIF() or decompressImage().
- */
- int getEXIFPos() { return mExifPos; }
- /*
- * Returns the ICC data from the image.
- */
- void* getICCPtr();
- /*
- * Returns the decompressed ICC buffer size. This method must be called only after
- * calling decompressImage() or getCompressedImageParameters().
- */
- size_t getICCSize();
- /*
- * Decompresses metadata of the image. All vectors are owned by the caller.
- */
- bool getCompressedImageParameters(const void* image, int length, size_t* pWidth,
- size_t* pHeight, std::vector<uint8_t>* iccData,
- std::vector<uint8_t>* exifData);
-
-private:
- bool decode(const void* image, int length, bool decodeToRGBA);
- // Returns false if errors occur.
- bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
- bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- bool decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- bool decompressSingleChannel(jpeg_decompress_struct* cinfo, const uint8_t* dest);
- // Process 16 lines of Y and 16 lines of U/V each time.
- // We must pass at least 16 scanlines according to libjpeg documentation.
- static const int kCompressBatchSize = 16;
- // The buffer that holds the decompressed result.
- std::vector<JOCTET> mResultBuffer;
- // The buffer that holds XMP Data.
- std::vector<JOCTET> mXMPBuffer;
- // The buffer that holds EXIF Data.
- std::vector<JOCTET> mEXIFBuffer;
- // The buffer that holds ICC Data.
- std::vector<JOCTET> mICCBuffer;
-
- // Resolution of the decompressed image.
- size_t mWidth;
- size_t mHeight;
-
- // Position of EXIF package, default value is -1 which means no EXIF package appears.
- ssize_t mExifPos = -1;
-};
-} /* namespace android::ultrahdr */
-
-#endif // ANDROID_ULTRAHDR_JPEGDECODERHELPER_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h b/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
deleted file mode 100644
index 9d06415..0000000
--- a/libs/ultrahdr/include/ultrahdr/jpegencoderhelper.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
-#define ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
-
-// We must include cstdio before jpeglib.h. It is a requirement of libjpeg.
-#include <cstdio>
-#include <vector>
-
-extern "C" {
-#include <jerror.h>
-#include <jpeglib.h>
-}
-
-#include <utils/Errors.h>
-
-namespace android::ultrahdr {
-
-#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
-
-/*
- * Encapsulates a converter from raw image (YUV420planer or grey-scale) to JPEG format.
- * This class is not thread-safe.
- */
-class JpegEncoderHelper {
-public:
- JpegEncoderHelper();
- ~JpegEncoderHelper();
-
- /*
- * Compresses YUV420Planer image to JPEG format. After calling this method, call
- * getCompressedImage() to get the image. |quality| is the jpeg image quality parameter to use.
- * It ranges from 1 (poorest quality) to 100 (highest quality). |iccBuffer| is the buffer of
- * ICC segment which will be added to the compressed image.
- * Returns false if errors occur during compression.
- */
- bool compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
- int lumaStride, int chromaStride, int quality, const void* iccBuffer,
- unsigned int iccSize);
-
- /*
- * Returns the compressed JPEG buffer pointer. This method must be called only after calling
- * compressImage().
- */
- void* getCompressedImagePtr();
-
- /*
- * Returns the compressed JPEG buffer size. This method must be called only after calling
- * compressImage().
- */
- size_t getCompressedImageSize();
-
- /*
- * Process 16 lines of Y and 16 lines of U/V each time.
- * We must pass at least 16 scanlines according to libjpeg documentation.
- */
- static const int kCompressBatchSize = 16;
-
-private:
- // initDestination(), emptyOutputBuffer() and emptyOutputBuffer() are callback functions to be
- // passed into jpeg library.
- static void initDestination(j_compress_ptr cinfo);
- static boolean emptyOutputBuffer(j_compress_ptr cinfo);
- static void terminateDestination(j_compress_ptr cinfo);
- static void outputErrorMessage(j_common_ptr cinfo);
-
- // Returns false if errors occur.
- bool encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width, int height,
- int lumaStride, int chromaStride, int quality, const void* iccBuffer,
- unsigned int iccSize);
- void setJpegDestination(jpeg_compress_struct* cinfo);
- void setJpegCompressStruct(int width, int height, int quality, jpeg_compress_struct* cinfo,
- bool isSingleChannel);
- // Returns false if errors occur.
- bool compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, const uint8_t* uvBuffer,
- int lumaStride, int chromaStride);
- bool compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer, int lumaStride);
-
- // The block size for encoded jpeg image buffer.
- static const int kBlockSize = 16384;
-
- // The buffer that holds the compressed result.
- std::vector<JOCTET> mResultBuffer;
-};
-
-} /* namespace android::ultrahdr */
-
-#endif // ANDROID_ULTRAHDR_JPEGENCODERHELPER_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegr.h b/libs/ultrahdr/include/ultrahdr/jpegr.h
deleted file mode 100644
index 114c81d..0000000
--- a/libs/ultrahdr/include/ultrahdr/jpegr.h
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_JPEGR_H
-#define ANDROID_ULTRAHDR_JPEGR_H
-
-#include <cstdint>
-#include <vector>
-
-#include "ultrahdr/jpegdecoderhelper.h"
-#include "ultrahdr/jpegencoderhelper.h"
-#include "ultrahdr/jpegrerrorcode.h"
-#include "ultrahdr/ultrahdr.h"
-
-#ifndef FLT_MAX
-#define FLT_MAX 0x1.fffffep127f
-#endif
-
-namespace android::ultrahdr {
-
-// The current JPEGR version that we encode to
-static const char* const kJpegrVersion = "1.0";
-
-// Map is quarter res / sixteenth size
-static const size_t kMapDimensionScaleFactor = 4;
-
-// Gain Map width is (image_width / kMapDimensionScaleFactor). If we were to
-// compress 420 GainMap in jpeg, then we need at least 2 samples. For Grayscale
-// 1 sample is sufficient. We are using 2 here anyways
-static const int kMinWidth = 2 * kMapDimensionScaleFactor;
-static const int kMinHeight = 2 * kMapDimensionScaleFactor;
-
-// Minimum Codec Unit(MCU) for 420 sub-sampling is decided by JPEG encoder by parameter
-// JpegEncoderHelper::kCompressBatchSize.
-// The width and height of image under compression is expected to be a multiple of MCU size.
-// If this criteria is not satisfied, padding is done.
-static const size_t kJpegBlock = JpegEncoderHelper::kCompressBatchSize;
-
-/*
- * Holds information of jpegr image
- */
-struct jpegr_info_struct {
- size_t width;
- size_t height;
- std::vector<uint8_t>* iccData;
- std::vector<uint8_t>* exifData;
-};
-
-/*
- * Holds information for uncompressed image or gain map.
- */
-struct jpegr_uncompressed_struct {
- // Pointer to the data location.
- void* data;
- // Width of the gain map or the luma plane of the image in pixels.
- int width;
- // Height of the gain map or the luma plane of the image in pixels.
- int height;
- // Color gamut.
- ultrahdr_color_gamut colorGamut;
-
- // Values below are optional
- // Pointer to chroma data, if it's NULL, chroma plane is considered to be immediately
- // after the luma plane.
- void* chroma_data = nullptr;
- // Stride of Y plane in number of pixels. 0 indicates the member is uninitialized. If
- // non-zero this value must be larger than or equal to luma width. If stride is
- // uninitialized then it is assumed to be equal to luma width.
- int luma_stride = 0;
- // Stride of UV plane in number of pixels.
- // 1. If this handle points to P010 image then this value must be larger than
- // or equal to luma width.
- // 2. If this handle points to 420 image then this value must be larger than
- // or equal to (luma width / 2).
- // NOTE: if chroma_data is nullptr, chroma_stride is irrelevant. Just as the way,
- // chroma_data is derived from luma ptr, chroma stride is derived from luma stride.
- int chroma_stride = 0;
-};
-
-/*
- * Holds information for compressed image or gain map.
- */
-struct jpegr_compressed_struct {
- // Pointer to the data location.
- void* data;
- // Used data length in bytes.
- int length;
- // Maximum available data length in bytes.
- int maxLength;
- // Color gamut.
- ultrahdr_color_gamut colorGamut;
-};
-
-/*
- * Holds information for EXIF metadata.
- */
-struct jpegr_exif_struct {
- // Pointer to the data location.
- void* data;
- // Data length;
- int length;
-};
-
-typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
-typedef struct jpegr_compressed_struct* jr_compressed_ptr;
-typedef struct jpegr_exif_struct* jr_exif_ptr;
-typedef struct jpegr_info_struct* jr_info_ptr;
-
-class JpegR {
-public:
- /*
- * Experimental only
- *
- * Encode API-0
- * Compress JPEGR image from 10-bit HDR YUV.
- *
- * Tonemap the HDR input to a SDR image, generate gain map from the HDR and SDR images,
- * compress SDR YUV to 8-bit JPEG and append the gain map to the end of the compressed
- * JPEG.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the destination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @param exif pointer to the exif metadata.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest, int quality, jr_exif_ptr exif);
-
- /*
- * Encode API-1
- * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
- *
- * Generate gain map from the HDR and SDR inputs, compress SDR YUV to 8-bit JPEG and append
- * the gain map to the end of the compressed JPEG. HDR and SDR inputs must be the same
- * resolution. SDR input is assumed to use the sRGB transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @param exif pointer to the exif metadata.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest, int quality,
- jr_exif_ptr exif);
-
- /*
- * Encode API-2
- * Compress JPEGR image from 10-bit HDR YUV, 8-bit SDR YUV and compressed 8-bit JPEG.
- *
- * This method requires HAL Hardware JPEG encoder.
- *
- * Generate gain map from the HDR and SDR inputs, append the gain map to the end of the
- * compressed JPEG. Adds an ICC profile if one isn't present in the input JPEG image. HDR and
- * SDR inputs must be the same resolution and color space. SDR image is assumed to use the sRGB
- * transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * Note: the compressed SDR image must be the compressed
- * yuv420_image_ptr image in JPEG format.
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_uncompressed_ptr yuv420_image_ptr,
- jr_compressed_ptr yuv420jpg_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest);
-
- /*
- * Encode API-3
- * Compress JPEGR image from 10-bit HDR YUV and 8-bit SDR YUV.
- *
- * This method requires HAL Hardware JPEG encoder.
- *
- * Decode the compressed 8-bit JPEG image to YUV SDR, generate gain map from the HDR input
- * and the decoded SDR result, append the gain map to the end of the compressed JPEG. Adds an
- * ICC profile if one isn't present in the input JPEG image. HDR and SDR inputs must be the same
- * resolution. JPEG image is assumed to use the sRGB transfer function.
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, jr_compressed_ptr yuv420jpg_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest);
-
- /*
- * Encode API-4
- * Assemble JPEGR image from SDR JPEG and gainmap JPEG.
- *
- * Assemble the primary JPEG image, the gain map and the metadata to JPEG/R format. Adds an ICC
- * profile if one isn't present in the input JPEG image.
- * @param yuv420jpg_image_ptr SDR image compressed in jpeg format
- * @param gainmapjpg_image_ptr gain map image compressed in jpeg format
- * @param metadata metadata to be written in XMP of the primary jpeg
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
- jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
- jr_compressed_ptr dest);
-
- /*
- * Decode API
- * Decompress JPEGR image.
- *
- * This method assumes that the JPEGR image contains an ICC profile with primaries that match
- * those of a color gamut that this library is aware of; Bt.709, Display-P3, or Bt.2100. It also
- * assumes the base image uses the sRGB transfer function.
- *
- * This method only supports single gain map metadata values for fields that allow multi-channel
- * metadata values.
- * @param jpegr_image_ptr compressed JPEGR image.
- * @param dest destination of the uncompressed JPEGR image.
- * @param max_display_boost (optional) the maximum available boost supported by a display,
- * the value must be greater than or equal to 1.0.
- * @param exif destination of the decoded EXIF metadata. The default value is NULL where the
- decoder will do nothing about it. If configured not NULL the decoder will write
- EXIF data into this structure. The format is defined in {@code jpegr_exif_struct}
- * @param output_format flag for setting output color format. Its value configures the output
- color format. The default value is {@code JPEGR_OUTPUT_HDR_LINEAR}.
- ----------------------------------------------------------------------
- | output_format | decoded color format to be written |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_SDR | RGBA_8888 |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_LINEAR | (default)RGBA_F16 linear |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_PQ | RGBA_1010102 PQ |
- ----------------------------------------------------------------------
- | JPEGR_OUTPUT_HDR_HLG | RGBA_1010102 HLG |
- ----------------------------------------------------------------------
- * @param gainmap_image_ptr destination of the decoded gain map. The default value is NULL
- where the decoder will do nothing about it. If configured not NULL
- the decoder will write the decoded gain_map data into this
- structure. The format is defined in
- {@code jpegr_uncompressed_struct}.
- * @param metadata destination of the decoded metadata. The default value is NULL where the
- decoder will do nothing about it. If configured not NULL the decoder will
- write metadata into this structure. the format of metadata is defined in
- {@code ultrahdr_metadata_struct}.
- * @return NO_ERROR if decoding succeeds, error code if error occurs.
- */
- status_t decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
- float max_display_boost = FLT_MAX, jr_exif_ptr exif = nullptr,
- ultrahdr_output_format output_format = ULTRAHDR_OUTPUT_HDR_LINEAR,
- jr_uncompressed_ptr gainmap_image_ptr = nullptr,
- ultrahdr_metadata_ptr metadata = nullptr);
-
- /*
- * Gets Info from JPEGR file without decoding it.
- *
- * This method only supports single gain map metadata values for fields that allow multi-channel
- * metadata values.
- *
- * The output is filled jpegr_info structure
- * @param jpegr_image_ptr compressed JPEGR image
- * @param jpeg_image_info_ptr pointer to jpegr info struct. Members of jpegr_info
- * are owned by the caller
- * @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
- */
- status_t getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr);
-
-protected:
- /*
- * This method is called in the encoding pipeline. It will take the uncompressed 8-bit and
- * 10-bit yuv images as input, and calculate the uncompressed gain map. The input images
- * must be the same resolution. The SDR input is assumed to use the sRGB transfer function.
- *
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param hdr_tf transfer function of the HDR image
- * @param metadata everything but "version" is filled in this struct
- * @param dest location at which gain map image is stored (caller responsible for memory
- of data).
- * @param sdr_is_601 if true, then use BT.601 decoding of YUV regardless of SDR image gamut
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest,
- bool sdr_is_601 = false);
-
- /*
- * This method is called in the decoding pipeline. It will take the uncompressed (decoded)
- * 8-bit yuv image, the uncompressed (decoded) gain map, and extracted JPEG/R metadata as
- * input, and calculate the 10-bit recovered image. The recovered output image is the same
- * color gamut as the SDR image, with HLG transfer function, and is in RGBA1010102 data format.
- * The SDR image is assumed to use the sRGB transfer function. The SDR image is also assumed to
- * be a decoded JPEG for the purpose of YUV interpration.
- *
- * @param yuv420_image_ptr uncompressed SDR image in YUV_420 color format
- * @param gainmap_image_ptr pointer to uncompressed gain map image struct.
- * @param metadata JPEG/R metadata extracted from XMP.
- * @param output_format flag for setting output color format. if set to
- * {@code JPEGR_OUTPUT_SDR}, decoder will only decode the primary image
- * which is SDR. Default value is JPEGR_OUTPUT_HDR_LINEAR.
- * @param max_display_boost the maximum available boost supported by a display
- * @param dest reconstructed HDR image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata,
- ultrahdr_output_format output_format, float max_display_boost,
- jr_uncompressed_ptr dest);
-
-private:
- /*
- * This method is called in the encoding pipeline. It will encode the gain map.
- *
- * @param gainmap_image_ptr pointer to uncompressed gain map image struct
- * @param jpeg_enc_obj_ptr helper resource to compress gain map
- * @return NO_ERROR if encoding succeeds, error code if error occurs.
- */
- status_t compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
- JpegEncoderHelper* jpeg_enc_obj_ptr);
-
- /*
- * This method is called to separate primary image and gain map image from JPEGR
- *
- * @param jpegr_image_ptr pointer to compressed JPEGR image.
- * @param primary_jpg_image_ptr destination of primary image
- * @param gainmap_jpg_image_ptr destination of compressed gain map image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
- jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr);
-
- /*
- * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
- * the compressed gain map and optionally the exif package as inputs, and generate the XMP
- * metadata, and finally append everything in the order of:
- * SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, gain map
- *
- * Note that in the final JPEG/R output, EXIF package will appear if ONLY ONE of the following
- * conditions is fulfilled:
- * (1) EXIF package is available from outside input. I.e. pExif != nullptr.
- * (2) Input JPEG has EXIF.
- * If both conditions are fulfilled, this method will return ERROR_JPEGR_INVALID_INPUT_TYPE
- *
- * @param primary_jpg_image_ptr destination of primary image
- * @param gainmap_jpg_image_ptr destination of compressed gain map image
- * @param (nullable) pExif EXIF package
- * @param (nullable) pIcc ICC package
- * @param icc_size length in bytes of ICC package
- * @param metadata JPEG/R metadata to encode in XMP of the jpeg
- * @param dest compressed JPEGR image
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr pExif, void* pIcc,
- size_t icc_size, ultrahdr_metadata_ptr metadata, jr_compressed_ptr dest);
-
- /*
- * This method will tone map a HDR image to an SDR image.
- *
- * @param src pointer to uncompressed HDR image struct. HDR image is expected to be
- * in p010 color format
- * @param dest pointer to store tonemapped SDR image
- */
- status_t toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest);
-
- /*
- * This method will convert a YUV420 image from one YUV encoding to another in-place (eg.
- * Bt.709 to Bt.601 YUV encoding).
- *
- * src_encoding and dest_encoding indicate the encoding via the YUV conversion defined for that
- * gamut. P3 indicates Rec.601, since this is how DataSpace encodes Display-P3 YUV data.
- *
- * @param image the YUV420 image to convert
- * @param src_encoding input YUV encoding
- * @param dest_encoding output YUV encoding
- * @return NO_ERROR if calculation succeeds, error code if error occurs.
- */
- status_t convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
- ultrahdr_color_gamut dest_encoding);
-
- /*
- * This method will check the validity of the input arguments.
- *
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
- * be in 420p color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the desitination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @return NO_ERROR if the input args are valid, error code is not valid.
- */
- status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest_ptr);
-
- /*
- * This method will check the validity of the input arguments.
- *
- * @param p010_image_ptr uncompressed HDR image in P010 color format
- * @param yuv420_image_ptr pointer to uncompressed SDR image struct. HDR image is expected to
- * be in 420p color format
- * @param hdr_tf transfer function of the HDR image
- * @param dest destination of the compressed JPEGR image. Please note that {@code maxLength}
- * represents the maximum available size of the destination buffer, and it must be
- * set before calling this method. If the encoded JPEGR size exceeds
- * {@code maxLength}, this method will return {@code ERROR_JPEGR_BUFFER_TOO_SMALL}.
- * @param quality target quality of the JPEG encoding, must be in range of 0-100 where 100 is
- * the highest quality
- * @return NO_ERROR if the input args are valid, error code is not valid.
- */
- status_t areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest,
- int quality);
-};
-} // namespace android::ultrahdr
-
-#endif // ANDROID_ULTRAHDR_JPEGR_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h b/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
deleted file mode 100644
index 5420e1c..0000000
--- a/libs/ultrahdr/include/ultrahdr/jpegrerrorcode.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_JPEGRERRORCODE_H
-#define ANDROID_ULTRAHDR_JPEGRERRORCODE_H
-
-#include <utils/Errors.h>
-
-namespace android::ultrahdr {
-
-enum {
- // status_t map for errors in the media framework
- // OK or NO_ERROR or 0 represents no error.
-
- // See system/core/include/utils/Errors.h
- // System standard errors from -1 through (possibly) -133
- //
- // Errors with special meanings and side effects.
- // INVALID_OPERATION: Operation attempted in an illegal state (will try to signal to app).
- // DEAD_OBJECT: Signal from CodecBase to MediaCodec that MediaServer has died.
- // NAME_NOT_FOUND: Signal from CodecBase to MediaCodec that the component was not found.
-
- // JPEGR errors
- JPEGR_IO_ERROR_BASE = -10000,
- ERROR_JPEGR_INVALID_INPUT_TYPE = JPEGR_IO_ERROR_BASE,
- ERROR_JPEGR_INVALID_OUTPUT_TYPE = JPEGR_IO_ERROR_BASE - 1,
- ERROR_JPEGR_INVALID_NULL_PTR = JPEGR_IO_ERROR_BASE - 2,
- ERROR_JPEGR_RESOLUTION_MISMATCH = JPEGR_IO_ERROR_BASE - 3,
- ERROR_JPEGR_BUFFER_TOO_SMALL = JPEGR_IO_ERROR_BASE - 4,
- ERROR_JPEGR_INVALID_COLORGAMUT = JPEGR_IO_ERROR_BASE - 5,
- ERROR_JPEGR_INVALID_TRANS_FUNC = JPEGR_IO_ERROR_BASE - 6,
- ERROR_JPEGR_INVALID_METADATA = JPEGR_IO_ERROR_BASE - 7,
- ERROR_JPEGR_UNSUPPORTED_METADATA = JPEGR_IO_ERROR_BASE - 8,
- ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND = JPEGR_IO_ERROR_BASE - 9,
-
- JPEGR_RUNTIME_ERROR_BASE = -20000,
- ERROR_JPEGR_ENCODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 1,
- ERROR_JPEGR_DECODE_ERROR = JPEGR_RUNTIME_ERROR_BASE - 2,
- ERROR_JPEGR_CALCULATION_ERROR = JPEGR_RUNTIME_ERROR_BASE - 3,
- ERROR_JPEGR_METADATA_ERROR = JPEGR_RUNTIME_ERROR_BASE - 4,
- ERROR_JPEGR_TONEMAP_ERROR = JPEGR_RUNTIME_ERROR_BASE - 5,
-
- ERROR_JPEGR_UNSUPPORTED_FEATURE = -20000,
-};
-
-} // namespace android::ultrahdr
-
-#endif // ANDROID_ULTRAHDR_JPEGRERRORCODE_H
diff --git a/libs/ultrahdr/include/ultrahdr/jpegrutils.h b/libs/ultrahdr/include/ultrahdr/jpegrutils.h
deleted file mode 100644
index 4ab664e..0000000
--- a/libs/ultrahdr/include/ultrahdr/jpegrutils.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_JPEGRUTILS_H
-#define ANDROID_ULTRAHDR_JPEGRUTILS_H
-
-#include <ultrahdr/jpegr.h>
-#include <utils/RefBase.h>
-
-#include <sstream>
-#include <stdint.h>
-#include <string>
-#include <cstdio>
-
-namespace android::ultrahdr {
-
-static constexpr uint32_t EndianSwap32(uint32_t value) {
- return ((value & 0xFF) << 24) |
- ((value & 0xFF00) << 8) |
- ((value & 0xFF0000) >> 8) |
- (value >> 24);
-}
-static inline uint16_t EndianSwap16(uint16_t value) {
- return static_cast<uint16_t>((value >> 8) | ((value & 0xFF) << 8));
-}
-
-#if USE_BIG_ENDIAN
- #define Endian_SwapBE32(n) EndianSwap32(n)
- #define Endian_SwapBE16(n) EndianSwap16(n)
-#else
- #define Endian_SwapBE32(n) (n)
- #define Endian_SwapBE16(n) (n)
-#endif
-
-struct ultrahdr_metadata_struct;
-/*
- * Mutable data structure. Holds information for metadata.
- */
-class DataStruct : public RefBase {
-private:
- void* data;
- int writePos;
- int length;
- ~DataStruct();
-
-public:
- DataStruct(int s);
- void* getData();
- int getLength();
- int getBytesWritten();
- bool write8(uint8_t value);
- bool write16(uint16_t value);
- bool write32(uint32_t value);
- bool write(const void* src, int size);
-};
-
-/*
- * Helper function used for writing data to destination.
- *
- * @param destination destination of the data to be written.
- * @param source source of data being written.
- * @param length length of the data to be written.
- * @param position cursor in desitination where the data is to be written.
- * @return status of succeed or error code.
- */
-status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position);
-
-
-/*
- * Parses XMP packet and fills metadata with data from XMP
- *
- * @param xmp_data pointer to XMP packet
- * @param xmp_size size of XMP packet
- * @param metadata place to store HDR metadata values
- * @return true if metadata is successfully retrieved, false otherwise
-*/
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata);
-
-/*
- * This method generates XMP metadata for the primary image.
- *
- * below is an example of the XMP metadata that this function generates where
- * secondary_image_length = 1000
- *
- * <x:xmpmeta
- * xmlns:x="adobe:ns:meta/"
- * x:xmptk="Adobe XMP Core 5.1.2">
- * <rdf:RDF
- * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- * <rdf:Description
- * xmlns:Container="http://ns.google.com/photos/1.0/container/"
- * xmlns:Item="http://ns.google.com/photos/1.0/container/item/"
- * xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
- * hdrgm:Version="1">
- * <Container:Directory>
- * <rdf:Seq>
- * <rdf:li
- * rdf:parseType="Resource">
- * <Container:Item
- * Item:Semantic="Primary"
- * Item:Mime="image/jpeg"/>
- * </rdf:li>
- * <rdf:li
- * rdf:parseType="Resource">
- * <Container:Item
- * Item:Semantic="GainMap"
- * Item:Mime="image/jpeg"
- * Item:Length="1000"/>
- * </rdf:li>
- * </rdf:Seq>
- * </Container:Directory>
- * </rdf:Description>
- * </rdf:RDF>
- * </x:xmpmeta>
- *
- * @param secondary_image_length length of secondary image
- * @return XMP metadata in type of string
- */
-std::string generateXmpForPrimaryImage(int secondary_image_length,
- ultrahdr_metadata_struct& metadata);
-
-/*
- * This method generates XMP metadata for the recovery map image.
- *
- * below is an example of the XMP metadata that this function generates where
- * max_content_boost = 8.0
- * min_content_boost = 0.5
- *
- * <x:xmpmeta
- * xmlns:x="adobe:ns:meta/"
- * x:xmptk="Adobe XMP Core 5.1.2">
- * <rdf:RDF
- * xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
- * <rdf:Description
- * xmlns:hdrgm="http://ns.adobe.com/hdr-gain-map/1.0/"
- * hdrgm:Version="1"
- * hdrgm:GainMapMin="-1"
- * hdrgm:GainMapMax="3"
- * hdrgm:Gamma="1"
- * hdrgm:OffsetSDR="0"
- * hdrgm:OffsetHDR="0"
- * hdrgm:HDRCapacityMin="0"
- * hdrgm:HDRCapacityMax="3"
- * hdrgm:BaseRenditionIsHDR="False"/>
- * </rdf:RDF>
- * </x:xmpmeta>
- *
- * @param metadata JPEG/R metadata to encode as XMP
- * @return XMP metadata in type of string
- */
- std::string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata);
-} // namespace android::ultrahdr
-
-#endif //ANDROID_ULTRAHDR_JPEGRUTILS_H
diff --git a/libs/ultrahdr/include/ultrahdr/multipictureformat.h b/libs/ultrahdr/include/ultrahdr/multipictureformat.h
deleted file mode 100644
index c5bd09d..0000000
--- a/libs/ultrahdr/include/ultrahdr/multipictureformat.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
-#define ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
-
-#include <ultrahdr/jpegrutils.h>
-
-#ifdef USE_BIG_ENDIAN
-#undef USE_BIG_ENDIAN
-#define USE_BIG_ENDIAN true
-#endif
-
-namespace android::ultrahdr {
-
-constexpr size_t kNumPictures = 2;
-constexpr size_t kMpEndianSize = 4;
-constexpr uint16_t kTagSerializedCount = 3;
-constexpr uint32_t kTagSize = 12;
-
-constexpr uint16_t kTypeLong = 0x4;
-constexpr uint16_t kTypeUndefined = 0x7;
-
-static constexpr uint8_t kMpfSig[] = {'M', 'P', 'F', '\0'};
-constexpr uint8_t kMpLittleEndian[kMpEndianSize] = {0x49, 0x49, 0x2A, 0x00};
-constexpr uint8_t kMpBigEndian[kMpEndianSize] = {0x4D, 0x4D, 0x00, 0x2A};
-
-constexpr uint16_t kVersionTag = 0xB000;
-constexpr uint16_t kVersionType = kTypeUndefined;
-constexpr uint32_t kVersionCount = 4;
-constexpr size_t kVersionSize = 4;
-constexpr uint8_t kVersionExpected[kVersionSize] = {'0', '1', '0', '0'};
-
-constexpr uint16_t kNumberOfImagesTag = 0xB001;
-constexpr uint16_t kNumberOfImagesType = kTypeLong;
-constexpr uint32_t kNumberOfImagesCount = 1;
-
-constexpr uint16_t kMPEntryTag = 0xB002;
-constexpr uint16_t kMPEntryType = kTypeUndefined;
-constexpr uint32_t kMPEntrySize = 16;
-
-constexpr uint32_t kMPEntryAttributeFormatJpeg = 0x0000000;
-constexpr uint32_t kMPEntryAttributeTypePrimary = 0x030000;
-
-size_t calculateMpfSize();
-sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
- int secondary_image_size, int secondary_image_offset);
-
-} // namespace android::ultrahdr
-
-#endif //ANDROID_ULTRAHDR_MULTIPICTUREFORMAT_H
diff --git a/libs/ultrahdr/include/ultrahdr/ultrahdr.h b/libs/ultrahdr/include/ultrahdr/ultrahdr.h
deleted file mode 100644
index 0252391..0000000
--- a/libs/ultrahdr/include/ultrahdr/ultrahdr.h
+++ /dev/null
@@ -1,82 +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.
- */
-
-#ifndef ANDROID_ULTRAHDR_ULTRAHDR_H
-#define ANDROID_ULTRAHDR_ULTRAHDR_H
-
-#include <string>
-
-namespace android::ultrahdr {
-// Color gamuts for image data
-typedef enum {
- ULTRAHDR_COLORGAMUT_UNSPECIFIED = -1,
- ULTRAHDR_COLORGAMUT_BT709,
- ULTRAHDR_COLORGAMUT_P3,
- ULTRAHDR_COLORGAMUT_BT2100,
- ULTRAHDR_COLORGAMUT_MAX = ULTRAHDR_COLORGAMUT_BT2100,
-} ultrahdr_color_gamut;
-
-// Transfer functions for image data
-// TODO: TF LINEAR is deprecated, remove this enum and the code surrounding it.
-typedef enum {
- ULTRAHDR_TF_UNSPECIFIED = -1,
- ULTRAHDR_TF_LINEAR = 0,
- ULTRAHDR_TF_HLG = 1,
- ULTRAHDR_TF_PQ = 2,
- ULTRAHDR_TF_SRGB = 3,
- ULTRAHDR_TF_MAX = ULTRAHDR_TF_SRGB,
-} ultrahdr_transfer_function;
-
-// Target output formats for decoder
-typedef enum {
- ULTRAHDR_OUTPUT_UNSPECIFIED = -1,
- ULTRAHDR_OUTPUT_SDR, // SDR in RGBA_8888 color format
- ULTRAHDR_OUTPUT_HDR_LINEAR, // HDR in F16 color format (linear)
- ULTRAHDR_OUTPUT_HDR_PQ, // HDR in RGBA_1010102 color format (PQ transfer function)
- ULTRAHDR_OUTPUT_HDR_HLG, // HDR in RGBA_1010102 color format (HLG transfer function)
- ULTRAHDR_OUTPUT_MAX = ULTRAHDR_OUTPUT_HDR_HLG,
-} ultrahdr_output_format;
-
-/*
- * Holds information for gain map related metadata.
- *
- * Not: all values stored in linear. This differs from the metadata encoding in XMP, where
- * maxContentBoost (aka gainMapMax), minContentBoost (aka gainMapMin), hdrCapacityMin, and
- * hdrCapacityMax are stored in log2 space.
- */
-struct ultrahdr_metadata_struct {
- // Ultra HDR format version
- std::string version;
- // Max Content Boost for the map
- float maxContentBoost;
- // Min Content Boost for the map
- float minContentBoost;
- // Gamma of the map data
- float gamma;
- // Offset for SDR data in map calculations
- float offsetSdr;
- // Offset for HDR data in map calculations
- float offsetHdr;
- // HDR capacity to apply the map at all
- float hdrCapacityMin;
- // HDR capacity to apply the map completely
- float hdrCapacityMax;
-};
-typedef struct ultrahdr_metadata_struct* ultrahdr_metadata_ptr;
-
-} // namespace android::ultrahdr
-
-#endif //ANDROID_ULTRAHDR_ULTRAHDR_H
diff --git a/libs/ultrahdr/jpegdecoderhelper.cpp b/libs/ultrahdr/jpegdecoderhelper.cpp
deleted file mode 100644
index 2e7940c..0000000
--- a/libs/ultrahdr/jpegdecoderhelper.cpp
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ultrahdr/jpegdecoderhelper.h>
-
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <setjmp.h>
-#include <string>
-
-using namespace std;
-
-namespace android::ultrahdr {
-
-#define ALIGNM(x, m) ((((x) + ((m)-1)) / (m)) * (m))
-
-const uint32_t kAPP0Marker = JPEG_APP0; // JFIF
-const uint32_t kAPP1Marker = JPEG_APP0 + 1; // EXIF, XMP
-const uint32_t kAPP2Marker = JPEG_APP0 + 2; // ICC
-
-constexpr uint32_t kICCMarkerHeaderSize = 14;
-constexpr uint8_t kICCSig[] = {
- 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0',
-};
-constexpr uint8_t kXmpNameSpace[] = {
- 'h', 't', 't', 'p', ':', '/', '/', 'n', 's', '.', 'a', 'd', 'o', 'b', 'e',
- '.', 'c', 'o', 'm', '/', 'x', 'a', 'p', '/', '1', '.', '0', '/', '\0',
-};
-constexpr uint8_t kExifIdCode[] = {
- 'E', 'x', 'i', 'f', '\0', '\0',
-};
-
-struct jpegr_source_mgr : jpeg_source_mgr {
- jpegr_source_mgr(const uint8_t* ptr, int len);
- ~jpegr_source_mgr();
-
- const uint8_t* mBufferPtr;
- size_t mBufferLength;
-};
-
-struct jpegrerror_mgr {
- struct jpeg_error_mgr pub;
- jmp_buf setjmp_buffer;
-};
-
-static void jpegr_init_source(j_decompress_ptr cinfo) {
- jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
- src->next_input_byte = static_cast<const JOCTET*>(src->mBufferPtr);
- src->bytes_in_buffer = src->mBufferLength;
-}
-
-static boolean jpegr_fill_input_buffer(j_decompress_ptr /* cinfo */) {
- ALOGE("%s : should not get here", __func__);
- return FALSE;
-}
-
-static void jpegr_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
- jpegr_source_mgr* src = static_cast<jpegr_source_mgr*>(cinfo->src);
-
- if (num_bytes > static_cast<long>(src->bytes_in_buffer)) {
- ALOGE("jpegr_skip_input_data - num_bytes > (long)src->bytes_in_buffer");
- } else {
- src->next_input_byte += num_bytes;
- src->bytes_in_buffer -= num_bytes;
- }
-}
-
-static void jpegr_term_source(j_decompress_ptr /*cinfo*/) {}
-
-jpegr_source_mgr::jpegr_source_mgr(const uint8_t* ptr, int len)
- : mBufferPtr(ptr), mBufferLength(len) {
- init_source = jpegr_init_source;
- fill_input_buffer = jpegr_fill_input_buffer;
- skip_input_data = jpegr_skip_input_data;
- resync_to_restart = jpeg_resync_to_restart;
- term_source = jpegr_term_source;
-}
-
-jpegr_source_mgr::~jpegr_source_mgr() {}
-
-static void jpegrerror_exit(j_common_ptr cinfo) {
- jpegrerror_mgr* err = reinterpret_cast<jpegrerror_mgr*>(cinfo->err);
- longjmp(err->setjmp_buffer, 1);
-}
-
-JpegDecoderHelper::JpegDecoderHelper() {}
-
-JpegDecoderHelper::~JpegDecoderHelper() {}
-
-bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
- if (image == nullptr || length <= 0) {
- ALOGE("Image size can not be handled: %d", length);
- return false;
- }
- mResultBuffer.clear();
- mXMPBuffer.clear();
- return decode(image, length, decodeToRGBA);
-}
-
-void* JpegDecoderHelper::getDecompressedImagePtr() {
- return mResultBuffer.data();
-}
-
-size_t JpegDecoderHelper::getDecompressedImageSize() {
- return mResultBuffer.size();
-}
-
-void* JpegDecoderHelper::getXMPPtr() {
- return mXMPBuffer.data();
-}
-
-size_t JpegDecoderHelper::getXMPSize() {
- return mXMPBuffer.size();
-}
-
-void* JpegDecoderHelper::getEXIFPtr() {
- return mEXIFBuffer.data();
-}
-
-size_t JpegDecoderHelper::getEXIFSize() {
- return mEXIFBuffer.size();
-}
-
-void* JpegDecoderHelper::getICCPtr() {
- return mICCBuffer.data();
-}
-
-size_t JpegDecoderHelper::getICCSize() {
- return mICCBuffer.size();
-}
-
-size_t JpegDecoderHelper::getDecompressedImageWidth() {
- return mWidth;
-}
-
-size_t JpegDecoderHelper::getDecompressedImageHeight() {
- return mHeight;
-}
-
-// Here we only handle the first EXIF package, and in theary EXIF (or JFIF) must be the first
-// in the image file.
-// We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
-// two bytes of package length which is stored in marker->original_length, and the real data
-// which is stored in marker->data.
-bool JpegDecoderHelper::extractEXIF(const void* image, int length) {
- jpeg_decompress_struct cinfo;
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- jpegrerror_mgr myerr;
-
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
-
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
-
- cinfo.src = &mgr;
- jpeg_read_header(&cinfo, TRUE);
-
- size_t pos = 2; // position after SOI
- for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker;
- marker = marker->next) {
-
- pos += 4;
- pos += marker->original_length;
-
- if (marker->marker != kAPP1Marker) {
- continue;
- }
-
- const unsigned int len = marker->data_length;
-
- if (len > sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- mEXIFBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
- mExifPos = pos - marker->original_length;
- break;
- }
- }
-
- jpeg_destroy_decompress(&cinfo);
- return true;
-}
-
-bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
- bool status = true;
- jpeg_decompress_struct cinfo;
- jpegrerror_mgr myerr;
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- cinfo.src = &mgr;
- if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- // Save XMP data, EXIF data, and ICC data.
- // Here we only handle the first XMP / EXIF / ICC package.
- // We assume that all packages are starting with two bytes marker (eg FF E1 for EXIF package),
- // two bytes of package length which is stored in marker->original_length, and the real data
- // which is stored in marker->data.
- bool exifAppears = false;
- bool xmpAppears = false;
- bool iccAppears = false;
- size_t pos = 2; // position after SOI
- for (jpeg_marker_struct* marker = cinfo.marker_list;
- marker && !(exifAppears && xmpAppears && iccAppears);
- marker = marker->next) {
- pos += 4;
- pos += marker->original_length;
- if (marker->marker != kAPP1Marker && marker->marker != kAPP2Marker) {
- continue;
- }
- const unsigned int len = marker->data_length;
- if (!xmpAppears &&
- len > sizeof(kXmpNameSpace) &&
- !memcmp(marker->data, kXmpNameSpace, sizeof(kXmpNameSpace))) {
- mXMPBuffer.resize(len+1, 0);
- memcpy(static_cast<void*>(mXMPBuffer.data()), marker->data, len);
- xmpAppears = true;
- } else if (!exifAppears &&
- len > sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- mEXIFBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
- exifAppears = true;
- mExifPos = pos - marker->original_length;
- } else if (!iccAppears &&
- len > sizeof(kICCSig) &&
- !memcmp(marker->data, kICCSig, sizeof(kICCSig))) {
- mICCBuffer.resize(len, 0);
- memcpy(static_cast<void*>(mICCBuffer.data()), marker->data, len);
- iccAppears = true;
- }
- }
-
- mWidth = cinfo.image_width;
- mHeight = cinfo.image_height;
- if (mWidth > kMaxWidth || mHeight > kMaxHeight) {
- status = false;
- goto CleanUp;
- }
-
- if (decodeToRGBA) {
- // The primary image is expected to be yuv420 sampling
- if (cinfo.jpeg_color_space != JCS_YCbCr) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected jpeg color space ", __func__);
- goto CleanUp;
- }
- if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
- status = false;
- ALOGE("%s: decodeToRGBA unexpected primary image sub-sampling", __func__);
- goto CleanUp;
- }
- // 4 bytes per pixel
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
- cinfo.out_color_space = JCS_EXT_RGBA;
- } else {
- if (cinfo.jpeg_color_space == JCS_YCbCr) {
- if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
- cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
- cinfo.comp_info[2].h_samp_factor != 1 || cinfo.comp_info[2].v_samp_factor != 1) {
- status = false;
- ALOGE("%s: decoding to YUV only supports 4:2:0 subsampling", __func__);
- goto CleanUp;
- }
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 3 / 2, 0);
- } else if (cinfo.jpeg_color_space == JCS_GRAYSCALE) {
- mResultBuffer.resize(cinfo.image_width * cinfo.image_height, 0);
- } else {
- status = false;
- ALOGE("%s: decodeToYUV unexpected jpeg color space", __func__);
- goto CleanUp;
- }
- cinfo.out_color_space = cinfo.jpeg_color_space;
- cinfo.raw_data_out = TRUE;
- }
-
- cinfo.dct_method = JDCT_ISLOW;
- jpeg_start_decompress(&cinfo);
- if (!decompress(&cinfo, static_cast<const uint8_t*>(mResultBuffer.data()),
- cinfo.jpeg_color_space == JCS_GRAYSCALE)) {
- status = false;
- goto CleanUp;
- }
-
-CleanUp:
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
-
- return status;
-}
-
-bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
- bool isSingleChannel) {
- return isSingleChannel
- ? decompressSingleChannel(cinfo, dest)
- : ((cinfo->out_color_space == JCS_EXT_RGBA) ? decompressRGBA(cinfo, dest)
- : decompressYUV(cinfo, dest));
-}
-
-bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth,
- size_t* pHeight, std::vector<uint8_t>* iccData,
- std::vector<uint8_t>* exifData) {
- jpeg_decompress_struct cinfo;
- jpegrerror_mgr myerr;
- cinfo.err = jpeg_std_error(&myerr.pub);
- myerr.pub.error_exit = jpegrerror_exit;
- if (setjmp(myerr.setjmp_buffer)) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
- jpeg_create_decompress(&cinfo);
-
- jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
- jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);
-
- jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
- cinfo.src = &mgr;
- if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
- jpeg_destroy_decompress(&cinfo);
- return false;
- }
-
- if (pWidth != nullptr) {
- *pWidth = cinfo.image_width;
- }
- if (pHeight != nullptr) {
- *pHeight = cinfo.image_height;
- }
-
- if (iccData != nullptr) {
- for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
- if (marker->marker != kAPP2Marker) {
- continue;
- }
- if (marker->data_length <= kICCMarkerHeaderSize ||
- memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
- continue;
- }
-
- iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
- }
- }
-
- if (exifData != nullptr) {
- bool exifAppears = false;
- for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
- marker = marker->next) {
- if (marker->marker != kAPP1Marker) {
- continue;
- }
-
- const unsigned int len = marker->data_length;
- if (len >= sizeof(kExifIdCode) &&
- !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
- exifData->resize(len, 0);
- memcpy(static_cast<void*>(exifData->data()), marker->data, len);
- exifAppears = true;
- }
- }
- }
-
- jpeg_destroy_decompress(&cinfo);
- return true;
-}
-
-bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
- JSAMPLE* out = (JSAMPLE*)dest;
-
- while (cinfo->output_scanline < cinfo->image_height) {
- if (1 != jpeg_read_scanlines(cinfo, &out, 1)) return false;
- out += cinfo->image_width * 4;
- }
- return true;
-}
-
-bool JpegDecoderHelper::decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
- JSAMPROW y[kCompressBatchSize];
- JSAMPROW cb[kCompressBatchSize / 2];
- JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3]{y, cb, cr};
-
- size_t y_plane_size = cinfo->image_width * cinfo->image_height;
- size_t uv_plane_size = y_plane_size / 4;
- uint8_t* y_plane = const_cast<uint8_t*>(dest);
- uint8_t* u_plane = const_cast<uint8_t*>(dest + y_plane_size);
- uint8_t* v_plane = const_cast<uint8_t*>(dest + y_plane_size + uv_plane_size);
- std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
- memset(empty.get(), 0, cinfo->image_width);
-
- const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- bool is_width_aligned = (aligned_width == cinfo->image_width);
- std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
- uint8_t* y_plane_intrm = nullptr;
- uint8_t* u_plane_intrm = nullptr;
- uint8_t* v_plane_intrm = nullptr;
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPROW cb_intrm[kCompressBatchSize / 2];
- JSAMPROW cr_intrm[kCompressBatchSize / 2];
- JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
- if (!is_width_aligned) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
- buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = buffer_intrm.get();
- u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
- v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- }
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- int offset_intrm = i * (aligned_width / 2);
- cb_intrm[i] = u_plane_intrm + offset_intrm;
- cr_intrm[i] = v_plane_intrm + offset_intrm;
- }
- }
-
- while (cinfo->output_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->output_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
- } else {
- y[i] = empty.get();
- }
- }
- // cb, cr only have half scanlines
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- size_t scanline = cinfo->output_scanline / 2 + i;
- if (scanline < cinfo->image_height / 2) {
- int offset = scanline * (cinfo->image_width / 2);
- cb[i] = u_plane + offset;
- cr[i] = v_plane + offset;
- } else {
- cb[i] = cr[i] = empty.get();
- }
- }
-
- int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
- kCompressBatchSize);
- if (processed != kCompressBatchSize) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- if (!is_width_aligned) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- memcpy(y[i], y_intrm[i], cinfo->image_width);
- }
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- memcpy(cb[i], cb_intrm[i], cinfo->image_width / 2);
- memcpy(cr[i], cr_intrm[i], cinfo->image_width / 2);
- }
- }
- }
- return true;
-}
-
-bool JpegDecoderHelper::decompressSingleChannel(jpeg_decompress_struct* cinfo,
- const uint8_t* dest) {
- JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1]{y};
-
- uint8_t* y_plane = const_cast<uint8_t*>(dest);
- std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
- memset(empty.get(), 0, cinfo->image_width);
-
- int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- bool is_width_aligned = (aligned_width == cinfo->image_width);
- std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
- uint8_t* y_plane_intrm = nullptr;
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPARRAY planes_intrm[1]{y_intrm};
- if (!is_width_aligned) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize;
- buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = buffer_intrm.get();
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- }
- }
-
- while (cinfo->output_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->output_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * cinfo->image_width;
- } else {
- y[i] = empty.get();
- }
- }
-
- int processed = jpeg_read_raw_data(cinfo, is_width_aligned ? planes : planes_intrm,
- kCompressBatchSize);
- if (processed != kCompressBatchSize / 2) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- if (!is_width_aligned) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- memcpy(y[i], y_intrm[i], cinfo->image_width);
- }
- }
- }
- return true;
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/jpegencoderhelper.cpp b/libs/ultrahdr/jpegencoderhelper.cpp
deleted file mode 100644
index 13ae742..0000000
--- a/libs/ultrahdr/jpegencoderhelper.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cstring>
-#include <memory>
-#include <vector>
-
-#include <ultrahdr/jpegencoderhelper.h>
-#include <utils/Log.h>
-
-namespace android::ultrahdr {
-
-// The destination manager that can access |mResultBuffer| in JpegEncoderHelper.
-struct destination_mgr {
- struct jpeg_destination_mgr mgr;
- JpegEncoderHelper* encoder;
-};
-
-JpegEncoderHelper::JpegEncoderHelper() {}
-
-JpegEncoderHelper::~JpegEncoderHelper() {}
-
-bool JpegEncoderHelper::compressImage(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
- int height, int lumaStride, int chromaStride, int quality,
- const void* iccBuffer, unsigned int iccSize) {
- mResultBuffer.clear();
- if (!encode(yBuffer, uvBuffer, width, height, lumaStride, chromaStride, quality, iccBuffer,
- iccSize)) {
- return false;
- }
- ALOGI("Compressed JPEG: %d[%dx%d] -> %zu bytes", (width * height * 12) / 8, width, height,
- mResultBuffer.size());
- return true;
-}
-
-void* JpegEncoderHelper::getCompressedImagePtr() {
- return mResultBuffer.data();
-}
-
-size_t JpegEncoderHelper::getCompressedImageSize() {
- return mResultBuffer.size();
-}
-
-void JpegEncoderHelper::initDestination(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- buffer.resize(kBlockSize);
- dest->mgr.next_output_byte = &buffer[0];
- dest->mgr.free_in_buffer = buffer.size();
-}
-
-boolean JpegEncoderHelper::emptyOutputBuffer(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- size_t oldsize = buffer.size();
- buffer.resize(oldsize + kBlockSize);
- dest->mgr.next_output_byte = &buffer[oldsize];
- dest->mgr.free_in_buffer = kBlockSize;
- return true;
-}
-
-void JpegEncoderHelper::terminateDestination(j_compress_ptr cinfo) {
- destination_mgr* dest = reinterpret_cast<destination_mgr*>(cinfo->dest);
- std::vector<JOCTET>& buffer = dest->encoder->mResultBuffer;
- buffer.resize(buffer.size() - dest->mgr.free_in_buffer);
-}
-
-void JpegEncoderHelper::outputErrorMessage(j_common_ptr cinfo) {
- char buffer[JMSG_LENGTH_MAX];
-
- /* Create the message */
- (*cinfo->err->format_message)(cinfo, buffer);
- ALOGE("%s\n", buffer);
-}
-
-bool JpegEncoderHelper::encode(const uint8_t* yBuffer, const uint8_t* uvBuffer, int width,
- int height, int lumaStride, int chromaStride, int quality,
- const void* iccBuffer, unsigned int iccSize) {
- jpeg_compress_struct cinfo;
- jpeg_error_mgr jerr;
-
- cinfo.err = jpeg_std_error(&jerr);
- cinfo.err->output_message = &outputErrorMessage;
- jpeg_create_compress(&cinfo);
- setJpegDestination(&cinfo);
- setJpegCompressStruct(width, height, quality, &cinfo, uvBuffer == nullptr);
- jpeg_start_compress(&cinfo, TRUE);
- if (iccBuffer != nullptr && iccSize > 0) {
- jpeg_write_marker(&cinfo, JPEG_APP0 + 2, static_cast<const JOCTET*>(iccBuffer), iccSize);
- }
- bool status = cinfo.num_components == 1
- ? compressY(&cinfo, yBuffer, lumaStride)
- : compressYuv(&cinfo, yBuffer, uvBuffer, lumaStride, chromaStride);
- jpeg_finish_compress(&cinfo);
- jpeg_destroy_compress(&cinfo);
-
- return status;
-}
-
-void JpegEncoderHelper::setJpegDestination(jpeg_compress_struct* cinfo) {
- destination_mgr* dest = static_cast<struct destination_mgr*>(
- (*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_PERMANENT,
- sizeof(destination_mgr)));
- dest->encoder = this;
- dest->mgr.init_destination = &initDestination;
- dest->mgr.empty_output_buffer = &emptyOutputBuffer;
- dest->mgr.term_destination = &terminateDestination;
- cinfo->dest = reinterpret_cast<struct jpeg_destination_mgr*>(dest);
-}
-
-void JpegEncoderHelper::setJpegCompressStruct(int width, int height, int quality,
- jpeg_compress_struct* cinfo, bool isSingleChannel) {
- cinfo->image_width = width;
- cinfo->image_height = height;
- cinfo->input_components = isSingleChannel ? 1 : 3;
- cinfo->in_color_space = isSingleChannel ? JCS_GRAYSCALE : JCS_YCbCr;
- jpeg_set_defaults(cinfo);
- jpeg_set_quality(cinfo, quality, TRUE);
- cinfo->raw_data_in = TRUE;
- cinfo->dct_method = JDCT_ISLOW;
- cinfo->comp_info[0].h_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
- cinfo->comp_info[0].v_samp_factor = cinfo->in_color_space == JCS_GRAYSCALE ? 1 : 2;
- for (int i = 1; i < cinfo->num_components; i++) {
- cinfo->comp_info[i].h_samp_factor = 1;
- cinfo->comp_info[i].v_samp_factor = 1;
- }
-}
-
-bool JpegEncoderHelper::compressYuv(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
- const uint8_t* uvBuffer, int lumaStride, int chromaStride) {
- JSAMPROW y[kCompressBatchSize];
- JSAMPROW cb[kCompressBatchSize / 2];
- JSAMPROW cr[kCompressBatchSize / 2];
- JSAMPARRAY planes[3]{y, cb, cr};
-
- size_t y_plane_size = lumaStride * cinfo->image_height;
- size_t u_plane_size = chromaStride * cinfo->image_height / 2;
- uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
- uint8_t* u_plane = const_cast<uint8_t*>(uvBuffer);
- uint8_t* v_plane = const_cast<uint8_t*>(u_plane + u_plane_size);
- std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
- memset(empty.get(), 0, cinfo->image_width);
-
- const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool need_padding = (lumaStride < aligned_width);
- std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
- uint8_t* y_plane_intrm = nullptr;
- uint8_t* u_plane_intrm = nullptr;
- uint8_t* v_plane_intrm = nullptr;
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPROW cb_intrm[kCompressBatchSize / 2];
- JSAMPROW cr_intrm[kCompressBatchSize / 2];
- JSAMPARRAY planes_intrm[3]{y_intrm, cb_intrm, cr_intrm};
- if (need_padding) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize * 3 / 2;
- buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = buffer_intrm.get();
- u_plane_intrm = y_plane_intrm + (aligned_width * kCompressBatchSize);
- v_plane_intrm = u_plane_intrm + (aligned_width * kCompressBatchSize) / 4;
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
- }
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- int offset_intrm = i * (aligned_width / 2);
- cb_intrm[i] = u_plane_intrm + offset_intrm;
- cr_intrm[i] = v_plane_intrm + offset_intrm;
- memset(cb_intrm[i] + cinfo->image_width / 2, 0,
- (aligned_width - cinfo->image_width) / 2);
- memset(cr_intrm[i] + cinfo->image_width / 2, 0,
- (aligned_width - cinfo->image_width) / 2);
- }
- }
-
- while (cinfo->next_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->next_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * lumaStride;
- } else {
- y[i] = empty.get();
- }
- if (need_padding) {
- memcpy(y_intrm[i], y[i], cinfo->image_width);
- }
- }
- // cb, cr only have half scanlines
- for (int i = 0; i < kCompressBatchSize / 2; ++i) {
- size_t scanline = cinfo->next_scanline / 2 + i;
- if (scanline < cinfo->image_height / 2) {
- int offset = scanline * chromaStride;
- cb[i] = u_plane + offset;
- cr[i] = v_plane + offset;
- } else {
- cb[i] = cr[i] = empty.get();
- }
- if (need_padding) {
- memcpy(cb_intrm[i], cb[i], cinfo->image_width / 2);
- memcpy(cr_intrm[i], cr[i], cinfo->image_width / 2);
- }
- }
- int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
- kCompressBatchSize);
- if (processed != kCompressBatchSize) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- }
- return true;
-}
-
-bool JpegEncoderHelper::compressY(jpeg_compress_struct* cinfo, const uint8_t* yBuffer,
- int lumaStride) {
- JSAMPROW y[kCompressBatchSize];
- JSAMPARRAY planes[1]{y};
-
- uint8_t* y_plane = const_cast<uint8_t*>(yBuffer);
- std::unique_ptr<uint8_t[]> empty = std::make_unique<uint8_t[]>(cinfo->image_width);
- memset(empty.get(), 0, cinfo->image_width);
-
- const int aligned_width = ALIGNM(cinfo->image_width, kCompressBatchSize);
- const bool need_padding = (lumaStride < aligned_width);
- std::unique_ptr<uint8_t[]> buffer_intrm = nullptr;
- uint8_t* y_plane_intrm = nullptr;
- uint8_t* u_plane_intrm = nullptr;
- JSAMPROW y_intrm[kCompressBatchSize];
- JSAMPARRAY planes_intrm[]{y_intrm};
- if (need_padding) {
- size_t mcu_row_size = aligned_width * kCompressBatchSize;
- buffer_intrm = std::make_unique<uint8_t[]>(mcu_row_size);
- y_plane_intrm = buffer_intrm.get();
- for (int i = 0; i < kCompressBatchSize; ++i) {
- y_intrm[i] = y_plane_intrm + i * aligned_width;
- memset(y_intrm[i] + cinfo->image_width, 0, aligned_width - cinfo->image_width);
- }
- }
-
- while (cinfo->next_scanline < cinfo->image_height) {
- for (int i = 0; i < kCompressBatchSize; ++i) {
- size_t scanline = cinfo->next_scanline + i;
- if (scanline < cinfo->image_height) {
- y[i] = y_plane + scanline * lumaStride;
- } else {
- y[i] = empty.get();
- }
- if (need_padding) {
- memcpy(y_intrm[i], y[i], cinfo->image_width);
- }
- }
- int processed = jpeg_write_raw_data(cinfo, need_padding ? planes_intrm : planes,
- kCompressBatchSize);
- if (processed != kCompressBatchSize / 2) {
- ALOGE("Number of processed lines does not equal input lines.");
- return false;
- }
- }
- return true;
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/jpegr.cpp b/libs/ultrahdr/jpegr.cpp
deleted file mode 100644
index 3d70fce..0000000
--- a/libs/ultrahdr/jpegr.cpp
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cmath>
-#include <condition_variable>
-#include <deque>
-#include <memory>
-#include <mutex>
-#include <thread>
-
-#include <ultrahdr/gainmapmath.h>
-#include <ultrahdr/icc.h>
-#include <ultrahdr/jpegr.h>
-#include <ultrahdr/jpegrutils.h>
-#include <ultrahdr/multipictureformat.h>
-
-#include <image_io/base/data_segment_data_source.h>
-#include <image_io/jpeg/jpeg_info.h>
-#include <image_io/jpeg/jpeg_info_builder.h>
-#include <image_io/jpeg/jpeg_marker.h>
-#include <image_io/jpeg/jpeg_scanner.h>
-
-#include <utils/Log.h>
-
-using namespace std;
-using namespace photos_editing_formats::image_io;
-
-namespace android::ultrahdr {
-
-#define USE_SRGB_INVOETF_LUT 1
-#define USE_HLG_OETF_LUT 1
-#define USE_PQ_OETF_LUT 1
-#define USE_HLG_INVOETF_LUT 1
-#define USE_PQ_INVOETF_LUT 1
-#define USE_APPLY_GAIN_LUT 1
-
-#define JPEGR_CHECK(x) \
- { \
- status_t status = (x); \
- if ((status) != NO_ERROR) { \
- return status; \
- } \
- }
-
-// JPEG compress quality (0 ~ 100) for gain map
-static const int kMapCompressQuality = 85;
-
-#define CONFIG_MULTITHREAD 1
-int GetCPUCoreCount() {
- int cpuCoreCount = 1;
-#if CONFIG_MULTITHREAD
-#if defined(_SC_NPROCESSORS_ONLN)
- cpuCoreCount = sysconf(_SC_NPROCESSORS_ONLN);
-#else
- // _SC_NPROC_ONLN must be defined...
- cpuCoreCount = sysconf(_SC_NPROC_ONLN);
-#endif
-#endif
- return cpuCoreCount;
-}
-
-/*
- * Helper function copies the JPEG image from without EXIF.
- *
- * @param pDest destination of the data to be written.
- * @param pSource source of data being written.
- * @param exif_pos position of the EXIF package, which is aligned with jpegdecoder.getEXIFPos().
- * (4 bytes offset to FF sign, the byte after FF E1 XX XX <this byte>).
- * @param exif_size exif size without the initial 4 bytes, aligned with jpegdecoder.getEXIFSize().
- */
-static void copyJpegWithoutExif(jr_compressed_ptr pDest,
- jr_compressed_ptr pSource,
- size_t exif_pos,
- size_t exif_size) {
- const size_t exif_offset = 4; //exif_pos has 4 bytes offset to the FF sign
- pDest->length = pSource->length - exif_size - exif_offset;
- pDest->data = new uint8_t[pDest->length];
- pDest->maxLength = pDest->length;
- pDest->colorGamut = pSource->colorGamut;
- memcpy(pDest->data, pSource->data, exif_pos - exif_offset);
- memcpy((uint8_t*)pDest->data + exif_pos - exif_offset,
- (uint8_t*)pSource->data + exif_pos + exif_size,
- pSource->length - exif_pos - exif_size);
-}
-
-status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest_ptr) {
- if (p010_image_ptr == nullptr || p010_image_ptr->data == nullptr) {
- ALOGE("Received nullptr for input p010 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (p010_image_ptr->width % 2 != 0 || p010_image_ptr->height % 2 != 0) {
- ALOGE("Image dimensions cannot be odd, image dimensions %dx%d", p010_image_ptr->width,
- p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->width < kMinWidth || p010_image_ptr->height < kMinHeight) {
- ALOGE("Image dimensions cannot be less than %dx%d, image dimensions %dx%d", kMinWidth,
- kMinHeight, p010_image_ptr->width, p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->width > kMaxWidth || p010_image_ptr->height > kMaxHeight) {
- ALOGE("Image dimensions cannot be larger than %dx%d, image dimensions %dx%d", kMaxWidth,
- kMaxHeight, p010_image_ptr->width, p010_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
- p010_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
- ALOGE("Unrecognized p010 color gamut %d", p010_image_ptr->colorGamut);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->luma_stride != 0 && p010_image_ptr->luma_stride < p010_image_ptr->width) {
- ALOGE("Luma stride must not be smaller than width, stride=%d, width=%d",
- p010_image_ptr->luma_stride, p010_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->chroma_data != nullptr &&
- p010_image_ptr->chroma_stride < p010_image_ptr->width) {
- ALOGE("Chroma stride must not be smaller than width, stride=%d, width=%d",
- p010_image_ptr->chroma_stride, p010_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (dest_ptr == nullptr || dest_ptr->data == nullptr) {
- ALOGE("Received nullptr for destination");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (hdr_tf <= ULTRAHDR_TF_UNSPECIFIED || hdr_tf > ULTRAHDR_TF_MAX || hdr_tf == ULTRAHDR_TF_SRGB) {
- ALOGE("Invalid hdr transfer function %d", hdr_tf);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (yuv420_image_ptr == nullptr) {
- return NO_ERROR;
- }
- if (yuv420_image_ptr->data == nullptr) {
- ALOGE("Received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (yuv420_image_ptr->luma_stride != 0 &&
- yuv420_image_ptr->luma_stride < yuv420_image_ptr->width) {
- ALOGE("Luma stride must not be smaller than width, stride=%d, width=%d",
- yuv420_image_ptr->luma_stride, yuv420_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (yuv420_image_ptr->chroma_data != nullptr &&
- yuv420_image_ptr->chroma_stride < yuv420_image_ptr->width / 2) {
- ALOGE("Chroma stride must not be smaller than (width / 2), stride=%d, width=%d",
- yuv420_image_ptr->chroma_stride, yuv420_image_ptr->width);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (p010_image_ptr->width != yuv420_image_ptr->width ||
- p010_image_ptr->height != yuv420_image_ptr->height) {
- ALOGE("Image resolutions mismatch: P010: %dx%d, YUV420: %dx%d", p010_image_ptr->width,
- p010_image_ptr->height, yuv420_image_ptr->width, yuv420_image_ptr->height);
- return ERROR_JPEGR_RESOLUTION_MISMATCH;
- }
- if (yuv420_image_ptr->colorGamut <= ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
- yuv420_image_ptr->colorGamut > ULTRAHDR_COLORGAMUT_MAX) {
- ALOGE("Unrecognized 420 color gamut %d", yuv420_image_ptr->colorGamut);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- return NO_ERROR;
-}
-
-status_t JpegR::areInputArgumentsValid(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest_ptr, int quality) {
- if (quality < 0 || quality > 100) {
- ALOGE("quality factor is out side range [0-100], quality factor : %d", quality);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- return areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest_ptr);
-}
-
-/* Encode API-0 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
- // validate input arguments
- if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest, quality);
- ret != NO_ERROR) {
- return ret;
- }
- if (exif != nullptr && exif->data == nullptr) {
- ALOGE("received nullptr for exif metadata");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- // clean up input structure for later usage
- jpegr_uncompressed_struct p010_image = *p010_image_ptr;
- if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
- if (!p010_image.chroma_data) {
- uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
- p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
- p010_image.chroma_stride = p010_image.luma_stride;
- }
-
- const int yu420_luma_stride = ALIGNM(p010_image.width, kJpegBlock);
- unique_ptr<uint8_t[]> yuv420_image_data =
- make_unique<uint8_t[]>(yu420_luma_stride * p010_image.height * 3 / 2);
- jpegr_uncompressed_struct yuv420_image = {.data = yuv420_image_data.get(),
- .width = p010_image.width,
- .height = p010_image.height,
- .colorGamut = p010_image.colorGamut,
- .luma_stride = yu420_luma_stride,
- .chroma_data = nullptr,
- .chroma_stride = yu420_luma_stride >> 1};
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
- yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
-
- // tone map
- JPEGR_CHECK(toneMap(&p010_image, &yuv420_image));
-
- // gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
- jpegr_uncompressed_struct gainmap_image;
- JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
-
- // compress gain map
- JpegEncoderHelper jpeg_enc_obj_gm;
- JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
-
- // convert to Bt601 YUV encoding for JPEG encode
- if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
- JPEGR_CHECK(convertYuv(&yuv420_image, yuv420_image.colorGamut, ULTRAHDR_COLORGAMUT_P3));
- }
-
- // compress 420 image
- JpegEncoderHelper jpeg_enc_obj_yuv420;
- if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_image.data),
- reinterpret_cast<uint8_t*>(yuv420_image.chroma_data),
- yuv420_image.width, yuv420_image.height,
- yuv420_image.luma_stride, yuv420_image.chroma_stride,
- quality, icc->getData(), icc->getLength())) {
- return ERROR_JPEGR_ENCODE_ERROR;
- }
- jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .colorGamut = yuv420_image.colorGamut};
-
- // append gain map, no ICC since JPEG encode already did it
- JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
- &metadata, dest));
-
- return NO_ERROR;
-}
-
-/* Encode API-1 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr, ultrahdr_transfer_function hdr_tf,
- jr_compressed_ptr dest, int quality, jr_exif_ptr exif) {
- // validate input arguments
- if (yuv420_image_ptr == nullptr) {
- ALOGE("received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (exif != nullptr && exif->data == nullptr) {
- ALOGE("received nullptr for exif metadata");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest, quality);
- ret != NO_ERROR) {
- return ret;
- }
-
- // clean up input structure for later usage
- jpegr_uncompressed_struct p010_image = *p010_image_ptr;
- if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
- if (!p010_image.chroma_data) {
- uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
- p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
- p010_image.chroma_stride = p010_image.luma_stride;
- }
- jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
- if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
- if (!yuv420_image.chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
- yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
- yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
- }
-
- // gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
- jpegr_uncompressed_struct gainmap_image;
- JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
-
- // compress gain map
- JpegEncoderHelper jpeg_enc_obj_gm;
- JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct compressed_map = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420_image.colorGamut);
-
- jpegr_uncompressed_struct yuv420_bt601_image = yuv420_image;
- unique_ptr<uint8_t[]> yuv_420_bt601_data;
- // Convert to bt601 YUV encoding for JPEG encode
- if (yuv420_image.colorGamut != ULTRAHDR_COLORGAMUT_P3) {
- const int yuv_420_bt601_luma_stride = ALIGNM(yuv420_image.width, kJpegBlock);
- yuv_420_bt601_data =
- make_unique<uint8_t[]>(yuv_420_bt601_luma_stride * yuv420_image.height * 3 / 2);
- yuv420_bt601_image.data = yuv_420_bt601_data.get();
- yuv420_bt601_image.colorGamut = yuv420_image.colorGamut;
- yuv420_bt601_image.luma_stride = yuv_420_bt601_luma_stride;
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_bt601_image.data);
- yuv420_bt601_image.chroma_data = data + yuv_420_bt601_luma_stride * yuv420_image.height;
- yuv420_bt601_image.chroma_stride = yuv_420_bt601_luma_stride >> 1;
-
- {
- // copy luma
- uint8_t* y_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.data);
- uint8_t* y_src = reinterpret_cast<uint8_t*>(yuv420_image.data);
- if (yuv420_bt601_image.luma_stride == yuv420_image.luma_stride) {
- memcpy(y_dst, y_src, yuv420_bt601_image.luma_stride * yuv420_image.height);
- } else {
- for (size_t i = 0; i < yuv420_image.height; i++) {
- memcpy(y_dst, y_src, yuv420_image.width);
- if (yuv420_image.width != yuv420_bt601_image.luma_stride) {
- memset(y_dst + yuv420_image.width, 0,
- yuv420_bt601_image.luma_stride - yuv420_image.width);
- }
- y_dst += yuv420_bt601_image.luma_stride;
- y_src += yuv420_image.luma_stride;
- }
- }
- }
-
- if (yuv420_bt601_image.chroma_stride == yuv420_image.chroma_stride) {
- // copy luma
- uint8_t* ch_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data);
- uint8_t* ch_src = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
- memcpy(ch_dst, ch_src, yuv420_bt601_image.chroma_stride * yuv420_image.height);
- } else {
- // copy cb & cr
- uint8_t* cb_dst = reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data);
- uint8_t* cb_src = reinterpret_cast<uint8_t*>(yuv420_image.chroma_data);
- uint8_t* cr_dst = cb_dst + (yuv420_bt601_image.chroma_stride * yuv420_bt601_image.height / 2);
- uint8_t* cr_src = cb_src + (yuv420_image.chroma_stride * yuv420_image.height / 2);
- for (size_t i = 0; i < yuv420_image.height / 2; i++) {
- memcpy(cb_dst, cb_src, yuv420_image.width / 2);
- memcpy(cr_dst, cr_src, yuv420_image.width / 2);
- if (yuv420_bt601_image.width / 2 != yuv420_bt601_image.chroma_stride) {
- memset(cb_dst + yuv420_image.width / 2, 0,
- yuv420_bt601_image.chroma_stride - yuv420_image.width / 2);
- memset(cr_dst + yuv420_image.width / 2, 0,
- yuv420_bt601_image.chroma_stride - yuv420_image.width / 2);
- }
- cb_dst += yuv420_bt601_image.chroma_stride;
- cb_src += yuv420_image.chroma_stride;
- cr_dst += yuv420_bt601_image.chroma_stride;
- cr_src += yuv420_image.chroma_stride;
- }
- }
- JPEGR_CHECK(convertYuv(&yuv420_bt601_image, yuv420_image.colorGamut, ULTRAHDR_COLORGAMUT_P3));
- }
-
- // compress 420 image
- JpegEncoderHelper jpeg_enc_obj_yuv420;
- if (!jpeg_enc_obj_yuv420.compressImage(reinterpret_cast<uint8_t*>(yuv420_bt601_image.data),
- reinterpret_cast<uint8_t*>(yuv420_bt601_image.chroma_data),
- yuv420_bt601_image.width, yuv420_bt601_image.height,
- yuv420_bt601_image.luma_stride,
- yuv420_bt601_image.chroma_stride, quality, icc->getData(),
- icc->getLength())) {
- return ERROR_JPEGR_ENCODE_ERROR;
- }
-
- jpegr_compressed_struct jpeg = {.data = jpeg_enc_obj_yuv420.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_yuv420.getCompressedImageSize()),
- .colorGamut = yuv420_image.colorGamut};
-
- // append gain map, no ICC since JPEG encode already did it
- JPEGR_CHECK(appendGainMap(&jpeg, &compressed_map, exif, /* icc */ nullptr, /* icc size */ 0,
- &metadata, dest));
- return NO_ERROR;
-}
-
-/* Encode API-2 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
- jr_uncompressed_ptr yuv420_image_ptr,
- jr_compressed_ptr yuv420jpg_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
- // validate input arguments
- if (yuv420_image_ptr == nullptr) {
- ALOGE("received nullptr for uncompressed 420 image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, yuv420_image_ptr, hdr_tf, dest);
- ret != NO_ERROR) {
- return ret;
- }
-
- // clean up input structure for later usage
- jpegr_uncompressed_struct p010_image = *p010_image_ptr;
- if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
- if (!p010_image.chroma_data) {
- uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
- p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
- p010_image.chroma_stride = p010_image.luma_stride;
- }
- jpegr_uncompressed_struct yuv420_image = *yuv420_image_ptr;
- if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
- if (!yuv420_image.chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
- yuv420_image.chroma_data = data + yuv420_image.luma_stride * p010_image.height;
- yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
- }
-
- // gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
- jpegr_uncompressed_struct gainmap_image;
- JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image));
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
-
- // compress gain map
- JpegEncoderHelper jpeg_enc_obj_gm;
- JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
-}
-
-/* Encode API-3 */
-status_t JpegR::encodeJPEGR(jr_uncompressed_ptr p010_image_ptr,
- jr_compressed_ptr yuv420jpg_image_ptr,
- ultrahdr_transfer_function hdr_tf, jr_compressed_ptr dest) {
- // validate input arguments
- if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (auto ret = areInputArgumentsValid(p010_image_ptr, nullptr, hdr_tf, dest); ret != NO_ERROR) {
- return ret;
- }
-
- // clean up input structure for later usage
- jpegr_uncompressed_struct p010_image = *p010_image_ptr;
- if (p010_image.luma_stride == 0) p010_image.luma_stride = p010_image.width;
- if (!p010_image.chroma_data) {
- uint16_t* data = reinterpret_cast<uint16_t*>(p010_image.data);
- p010_image.chroma_data = data + p010_image.luma_stride * p010_image.height;
- p010_image.chroma_stride = p010_image.luma_stride;
- }
-
- // decode input jpeg, gamut is going to be bt601.
- JpegDecoderHelper jpeg_dec_obj_yuv420;
- if (!jpeg_dec_obj_yuv420.decompressImage(yuv420jpg_image_ptr->data,
- yuv420jpg_image_ptr->length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
- jpegr_uncompressed_struct yuv420_image{};
- yuv420_image.data = jpeg_dec_obj_yuv420.getDecompressedImagePtr();
- yuv420_image.width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
- yuv420_image.height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
- yuv420_image.colorGamut = yuv420jpg_image_ptr->colorGamut;
- if (yuv420_image.luma_stride == 0) yuv420_image.luma_stride = yuv420_image.width;
- if (!yuv420_image.chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
- yuv420_image.chroma_data = data + yuv420_image.luma_stride * p010_image.height;
- yuv420_image.chroma_stride = yuv420_image.luma_stride >> 1;
- }
-
- if (p010_image_ptr->width != yuv420_image.width ||
- p010_image_ptr->height != yuv420_image.height) {
- return ERROR_JPEGR_RESOLUTION_MISMATCH;
- }
-
- // gain map
- ultrahdr_metadata_struct metadata = {.version = kJpegrVersion};
- jpegr_uncompressed_struct gainmap_image;
- JPEGR_CHECK(generateGainMap(&yuv420_image, &p010_image, hdr_tf, &metadata, &gainmap_image,
- true /* sdr_is_601 */));
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(gainmap_image.data));
-
- // compress gain map
- JpegEncoderHelper jpeg_enc_obj_gm;
- JPEGR_CHECK(compressGainMap(&gainmap_image, &jpeg_enc_obj_gm));
- jpegr_compressed_struct gainmapjpg_image = {.data = jpeg_enc_obj_gm.getCompressedImagePtr(),
- .length = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .maxLength = static_cast<int>(
- jpeg_enc_obj_gm.getCompressedImageSize()),
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- return encodeJPEGR(yuv420jpg_image_ptr, &gainmapjpg_image, &metadata, dest);
-}
-
-/* Encode API-4 */
-status_t JpegR::encodeJPEGR(jr_compressed_ptr yuv420jpg_image_ptr,
- jr_compressed_ptr gainmapjpg_image_ptr, ultrahdr_metadata_ptr metadata,
- jr_compressed_ptr dest) {
- if (yuv420jpg_image_ptr == nullptr || yuv420jpg_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed jpeg image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (gainmapjpg_image_ptr == nullptr || gainmapjpg_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed gain map");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (dest == nullptr || dest->data == nullptr) {
- ALOGE("received nullptr for destination");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- // We just want to check if ICC is present, so don't do a full decode. Note,
- // this doesn't verify that the ICC is valid.
- JpegDecoderHelper decoder;
- std::vector<uint8_t> icc;
- decoder.getCompressedImageParameters(yuv420jpg_image_ptr->data, yuv420jpg_image_ptr->length,
- /* pWidth */ nullptr, /* pHeight */ nullptr, &icc,
- /* exifData */ nullptr);
-
- // Add ICC if not already present.
- if (icc.size() > 0) {
- JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr,
- /* icc */ nullptr, /* icc size */ 0, metadata, dest));
- } else {
- sp<DataStruct> newIcc =
- IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, yuv420jpg_image_ptr->colorGamut);
- JPEGR_CHECK(appendGainMap(yuv420jpg_image_ptr, gainmapjpg_image_ptr, /* exif */ nullptr,
- newIcc->getData(), newIcc->getLength(), metadata, dest));
- }
-
- return NO_ERROR;
-}
-
-status_t JpegR::getJPEGRInfo(jr_compressed_ptr jpegr_image_ptr, jr_info_ptr jpeg_image_info_ptr) {
- if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed jpegr image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (jpeg_image_info_ptr == nullptr) {
- ALOGE("received nullptr for compressed jpegr info struct");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- jpegr_compressed_struct primary_image, gainmap_image;
- status_t status = extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_image, &gainmap_image);
- if (status != NO_ERROR && status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
- return status;
- }
-
- JpegDecoderHelper jpeg_dec_obj_hdr;
- if (!jpeg_dec_obj_hdr.getCompressedImageParameters(primary_image.data, primary_image.length,
- &jpeg_image_info_ptr->width,
- &jpeg_image_info_ptr->height,
- jpeg_image_info_ptr->iccData,
- jpeg_image_info_ptr->exifData)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- return status;
-}
-
-/* Decode API */
-status_t JpegR::decodeJPEGR(jr_compressed_ptr jpegr_image_ptr, jr_uncompressed_ptr dest,
- float max_display_boost, jr_exif_ptr exif,
- ultrahdr_output_format output_format,
- jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata) {
- if (jpegr_image_ptr == nullptr || jpegr_image_ptr->data == nullptr) {
- ALOGE("received nullptr for compressed jpegr image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (dest == nullptr || dest->data == nullptr) {
- ALOGE("received nullptr for dest image");
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (max_display_boost < 1.0f) {
- ALOGE("received bad value for max_display_boost %f", max_display_boost);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (exif != nullptr && exif->data == nullptr) {
- ALOGE("received nullptr address for exif data");
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (output_format <= ULTRAHDR_OUTPUT_UNSPECIFIED || output_format > ULTRAHDR_OUTPUT_MAX) {
- ALOGE("received bad value for output format %d", output_format);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- jpegr_compressed_struct primary_jpeg_image, gainmap_jpeg_image;
- status_t status =
- extractPrimaryImageAndGainMap(jpegr_image_ptr, &primary_jpeg_image, &gainmap_jpeg_image);
- if (status != NO_ERROR) {
- if (output_format != ULTRAHDR_OUTPUT_SDR || status != ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND) {
- ALOGE("received invalid compressed jpegr image");
- return status;
- }
- }
-
- JpegDecoderHelper jpeg_dec_obj_yuv420;
- if (!jpeg_dec_obj_yuv420.decompressImage(primary_jpeg_image.data, primary_jpeg_image.length,
- (output_format == ULTRAHDR_OUTPUT_SDR))) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
-
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
- jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 4) >
- jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
- }
- } else {
- if ((jpeg_dec_obj_yuv420.getDecompressedImageWidth() *
- jpeg_dec_obj_yuv420.getDecompressedImageHeight() * 3 / 2) >
- jpeg_dec_obj_yuv420.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
- }
- }
-
- if (exif != nullptr) {
- if (exif->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (exif->length < jpeg_dec_obj_yuv420.getEXIFSize()) {
- return ERROR_JPEGR_BUFFER_TOO_SMALL;
- }
- memcpy(exif->data, jpeg_dec_obj_yuv420.getEXIFPtr(), jpeg_dec_obj_yuv420.getEXIFSize());
- exif->length = jpeg_dec_obj_yuv420.getEXIFSize();
- }
-
- if (output_format == ULTRAHDR_OUTPUT_SDR) {
- dest->width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
- dest->height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
- memcpy(dest->data, jpeg_dec_obj_yuv420.getDecompressedImagePtr(),
- dest->width * dest->height * 4);
- return NO_ERROR;
- }
-
- JpegDecoderHelper jpeg_dec_obj_gm;
- if (!jpeg_dec_obj_gm.decompressImage(gainmap_jpeg_image.data, gainmap_jpeg_image.length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
- if ((jpeg_dec_obj_gm.getDecompressedImageWidth() * jpeg_dec_obj_gm.getDecompressedImageHeight()) >
- jpeg_dec_obj_gm.getDecompressedImageSize()) {
- return ERROR_JPEGR_CALCULATION_ERROR;
- }
-
- jpegr_uncompressed_struct gainmap_image;
- gainmap_image.data = jpeg_dec_obj_gm.getDecompressedImagePtr();
- gainmap_image.width = jpeg_dec_obj_gm.getDecompressedImageWidth();
- gainmap_image.height = jpeg_dec_obj_gm.getDecompressedImageHeight();
-
- if (gainmap_image_ptr != nullptr) {
- gainmap_image_ptr->width = gainmap_image.width;
- gainmap_image_ptr->height = gainmap_image.height;
- int size = gainmap_image_ptr->width * gainmap_image_ptr->height;
- gainmap_image_ptr->data = malloc(size);
- memcpy(gainmap_image_ptr->data, gainmap_image.data, size);
- }
-
- ultrahdr_metadata_struct uhdr_metadata;
- if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_dec_obj_gm.getXMPPtr()),
- jpeg_dec_obj_gm.getXMPSize(), &uhdr_metadata)) {
- return ERROR_JPEGR_INVALID_METADATA;
- }
-
- if (metadata != nullptr) {
- metadata->version = uhdr_metadata.version;
- metadata->minContentBoost = uhdr_metadata.minContentBoost;
- metadata->maxContentBoost = uhdr_metadata.maxContentBoost;
- metadata->gamma = uhdr_metadata.gamma;
- metadata->offsetSdr = uhdr_metadata.offsetSdr;
- metadata->offsetHdr = uhdr_metadata.offsetHdr;
- metadata->hdrCapacityMin = uhdr_metadata.hdrCapacityMin;
- metadata->hdrCapacityMax = uhdr_metadata.hdrCapacityMax;
- }
-
- jpegr_uncompressed_struct yuv420_image;
- yuv420_image.data = jpeg_dec_obj_yuv420.getDecompressedImagePtr();
- yuv420_image.width = jpeg_dec_obj_yuv420.getDecompressedImageWidth();
- yuv420_image.height = jpeg_dec_obj_yuv420.getDecompressedImageHeight();
- yuv420_image.colorGamut = IccHelper::readIccColorGamut(jpeg_dec_obj_yuv420.getICCPtr(),
- jpeg_dec_obj_yuv420.getICCSize());
- yuv420_image.luma_stride = yuv420_image.width;
- uint8_t* data = reinterpret_cast<uint8_t*>(yuv420_image.data);
- yuv420_image.chroma_data = data + yuv420_image.luma_stride * yuv420_image.height;
- yuv420_image.chroma_stride = yuv420_image.width >> 1;
-
- JPEGR_CHECK(applyGainMap(&yuv420_image, &gainmap_image, &uhdr_metadata, output_format,
- max_display_boost, dest));
- return NO_ERROR;
-}
-
-status_t JpegR::compressGainMap(jr_uncompressed_ptr gainmap_image_ptr,
- JpegEncoderHelper* jpeg_enc_obj_ptr) {
- if (gainmap_image_ptr == nullptr || jpeg_enc_obj_ptr == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- // Don't need to convert YUV to Bt601 since single channel
- if (!jpeg_enc_obj_ptr->compressImage(reinterpret_cast<uint8_t*>(gainmap_image_ptr->data), nullptr,
- gainmap_image_ptr->width, gainmap_image_ptr->height,
- gainmap_image_ptr->luma_stride, 0, kMapCompressQuality,
- nullptr, 0)) {
- return ERROR_JPEGR_ENCODE_ERROR;
- }
-
- return NO_ERROR;
-}
-
-const int kJobSzInRows = 16;
-static_assert(kJobSzInRows > 0 && kJobSzInRows % kMapDimensionScaleFactor == 0,
- "align job size to kMapDimensionScaleFactor");
-
-class JobQueue {
-public:
- bool dequeueJob(size_t& rowStart, size_t& rowEnd);
- void enqueueJob(size_t rowStart, size_t rowEnd);
- void markQueueForEnd();
- void reset();
-
-private:
- bool mQueuedAllJobs = false;
- std::deque<std::tuple<size_t, size_t>> mJobs;
- std::mutex mMutex;
- std::condition_variable mCv;
-};
-
-bool JobQueue::dequeueJob(size_t& rowStart, size_t& rowEnd) {
- std::unique_lock<std::mutex> lock{mMutex};
- while (true) {
- if (mJobs.empty()) {
- if (mQueuedAllJobs) {
- return false;
- } else {
- mCv.wait_for(lock, std::chrono::milliseconds(100));
- }
- } else {
- auto it = mJobs.begin();
- rowStart = std::get<0>(*it);
- rowEnd = std::get<1>(*it);
- mJobs.erase(it);
- return true;
- }
- }
- return false;
-}
-
-void JobQueue::enqueueJob(size_t rowStart, size_t rowEnd) {
- std::unique_lock<std::mutex> lock{mMutex};
- mJobs.push_back(std::make_tuple(rowStart, rowEnd));
- lock.unlock();
- mCv.notify_one();
-}
-
-void JobQueue::markQueueForEnd() {
- std::unique_lock<std::mutex> lock{mMutex};
- mQueuedAllJobs = true;
- lock.unlock();
- mCv.notify_all();
-}
-
-void JobQueue::reset() {
- std::unique_lock<std::mutex> lock{mMutex};
- mJobs.clear();
- mQueuedAllJobs = false;
-}
-
-status_t JpegR::generateGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr p010_image_ptr,
- ultrahdr_transfer_function hdr_tf, ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest, bool sdr_is_601) {
- if (yuv420_image_ptr == nullptr || p010_image_ptr == nullptr || metadata == nullptr ||
- dest == nullptr || yuv420_image_ptr->data == nullptr ||
- yuv420_image_ptr->chroma_data == nullptr || p010_image_ptr->data == nullptr ||
- p010_image_ptr->chroma_data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (yuv420_image_ptr->width != p010_image_ptr->width ||
- yuv420_image_ptr->height != p010_image_ptr->height) {
- return ERROR_JPEGR_RESOLUTION_MISMATCH;
- }
- if (yuv420_image_ptr->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
- p010_image_ptr->colorGamut == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
-
- size_t image_width = yuv420_image_ptr->width;
- size_t image_height = yuv420_image_ptr->height;
- size_t map_width = image_width / kMapDimensionScaleFactor;
- size_t map_height = image_height / kMapDimensionScaleFactor;
-
- dest->data = new uint8_t[map_width * map_height];
- dest->width = map_width;
- dest->height = map_height;
- dest->colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- dest->luma_stride = map_width;
- dest->chroma_data = nullptr;
- dest->chroma_stride = 0;
- std::unique_ptr<uint8_t[]> map_data;
- map_data.reset(reinterpret_cast<uint8_t*>(dest->data));
-
- ColorTransformFn hdrInvOetf = nullptr;
- float hdr_white_nits;
- switch (hdr_tf) {
- case ULTRAHDR_TF_LINEAR:
- hdrInvOetf = identityConversion;
- // Note: this will produce clipping if the input exceeds kHlgMaxNits.
- // TODO: TF LINEAR will be deprecated.
- hdr_white_nits = kHlgMaxNits;
- break;
- case ULTRAHDR_TF_HLG:
-#if USE_HLG_INVOETF_LUT
- hdrInvOetf = hlgInvOetfLUT;
-#else
- hdrInvOetf = hlgInvOetf;
-#endif
- hdr_white_nits = kHlgMaxNits;
- break;
- case ULTRAHDR_TF_PQ:
-#if USE_PQ_INVOETF_LUT
- hdrInvOetf = pqInvOetfLUT;
-#else
- hdrInvOetf = pqInvOetf;
-#endif
- hdr_white_nits = kPqMaxNits;
- break;
- default:
- // Should be impossible to hit after input validation.
- return ERROR_JPEGR_INVALID_TRANS_FUNC;
- }
-
- metadata->maxContentBoost = hdr_white_nits / kSdrWhiteNits;
- metadata->minContentBoost = 1.0f;
- metadata->gamma = 1.0f;
- metadata->offsetSdr = 0.0f;
- metadata->offsetHdr = 0.0f;
- metadata->hdrCapacityMin = 1.0f;
- metadata->hdrCapacityMax = metadata->maxContentBoost;
-
- float log2MinBoost = log2(metadata->minContentBoost);
- float log2MaxBoost = log2(metadata->maxContentBoost);
-
- ColorTransformFn hdrGamutConversionFn =
- getHdrConversionFn(yuv420_image_ptr->colorGamut, p010_image_ptr->colorGamut);
-
- ColorCalculationFn luminanceFn = nullptr;
- ColorTransformFn sdrYuvToRgbFn = nullptr;
- switch (yuv420_image_ptr->colorGamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- luminanceFn = srgbLuminance;
- sdrYuvToRgbFn = srgbYuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- luminanceFn = p3Luminance;
- sdrYuvToRgbFn = p3YuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- luminanceFn = bt2100Luminance;
- sdrYuvToRgbFn = bt2100YuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- // Should be impossible to hit after input validation.
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
- if (sdr_is_601) {
- sdrYuvToRgbFn = p3YuvToRgb;
- }
-
- ColorTransformFn hdrYuvToRgbFn = nullptr;
- switch (p010_image_ptr->colorGamut) {
- case ULTRAHDR_COLORGAMUT_BT709:
- hdrYuvToRgbFn = srgbYuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- hdrYuvToRgbFn = p3YuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- hdrYuvToRgbFn = bt2100YuvToRgb;
- break;
- case ULTRAHDR_COLORGAMUT_UNSPECIFIED:
- // Should be impossible to hit after input validation.
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
-
- std::mutex mutex;
- const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
- size_t rowStep = threads == 1 ? image_height : kJobSzInRows;
- JobQueue jobQueue;
-
- std::function<void()> generateMap = [yuv420_image_ptr, p010_image_ptr, metadata, dest, hdrInvOetf,
- hdrGamutConversionFn, luminanceFn, sdrYuvToRgbFn,
- hdrYuvToRgbFn, hdr_white_nits, log2MinBoost, log2MaxBoost,
- &jobQueue]() -> void {
- size_t rowStart, rowEnd;
- while (jobQueue.dequeueJob(rowStart, rowEnd)) {
- for (size_t y = rowStart; y < rowEnd; ++y) {
- for (size_t x = 0; x < dest->width; ++x) {
- Color sdr_yuv_gamma = sampleYuv420(yuv420_image_ptr, kMapDimensionScaleFactor, x, y);
- Color sdr_rgb_gamma = sdrYuvToRgbFn(sdr_yuv_gamma);
- // We are assuming the SDR input is always sRGB transfer.
-#if USE_SRGB_INVOETF_LUT
- Color sdr_rgb = srgbInvOetfLUT(sdr_rgb_gamma);
-#else
- Color sdr_rgb = srgbInvOetf(sdr_rgb_gamma);
-#endif
- float sdr_y_nits = luminanceFn(sdr_rgb) * kSdrWhiteNits;
-
- Color hdr_yuv_gamma = sampleP010(p010_image_ptr, kMapDimensionScaleFactor, x, y);
- Color hdr_rgb_gamma = hdrYuvToRgbFn(hdr_yuv_gamma);
- Color hdr_rgb = hdrInvOetf(hdr_rgb_gamma);
- hdr_rgb = hdrGamutConversionFn(hdr_rgb);
- float hdr_y_nits = luminanceFn(hdr_rgb) * hdr_white_nits;
-
- size_t pixel_idx = x + y * dest->width;
- reinterpret_cast<uint8_t*>(dest->data)[pixel_idx] =
- encodeGain(sdr_y_nits, hdr_y_nits, metadata, log2MinBoost, log2MaxBoost);
- }
- }
- }
- };
-
- // generate map
- std::vector<std::thread> workers;
- for (int th = 0; th < threads - 1; th++) {
- workers.push_back(std::thread(generateMap));
- }
-
- rowStep = (threads == 1 ? image_height : kJobSzInRows) / kMapDimensionScaleFactor;
- for (size_t rowStart = 0; rowStart < map_height;) {
- size_t rowEnd = std::min(rowStart + rowStep, map_height);
- jobQueue.enqueueJob(rowStart, rowEnd);
- rowStart = rowEnd;
- }
- jobQueue.markQueueForEnd();
- generateMap();
- std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
-
- map_data.release();
- return NO_ERROR;
-}
-
-status_t JpegR::applyGainMap(jr_uncompressed_ptr yuv420_image_ptr,
- jr_uncompressed_ptr gainmap_image_ptr, ultrahdr_metadata_ptr metadata,
- ultrahdr_output_format output_format, float max_display_boost,
- jr_uncompressed_ptr dest) {
- if (yuv420_image_ptr == nullptr || gainmap_image_ptr == nullptr || metadata == nullptr ||
- dest == nullptr || yuv420_image_ptr->data == nullptr ||
- yuv420_image_ptr->chroma_data == nullptr || gainmap_image_ptr->data == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (metadata->version.compare(kJpegrVersion)) {
- ALOGE("Unsupported metadata version: %s", metadata->version.c_str());
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
- }
- if (metadata->gamma != 1.0f) {
- ALOGE("Unsupported metadata gamma: %f", metadata->gamma);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
- }
- if (metadata->offsetSdr != 0.0f || metadata->offsetHdr != 0.0f) {
- ALOGE("Unsupported metadata offset sdr, hdr: %f, %f", metadata->offsetSdr, metadata->offsetHdr);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
- }
- if (metadata->hdrCapacityMin != metadata->minContentBoost ||
- metadata->hdrCapacityMax != metadata->maxContentBoost) {
- ALOGE("Unsupported metadata hdr capacity min, max: %f, %f", metadata->hdrCapacityMin,
- metadata->hdrCapacityMax);
- return ERROR_JPEGR_UNSUPPORTED_METADATA;
- }
-
- // TODO: remove once map scaling factor is computed based on actual map dims
- size_t image_width = yuv420_image_ptr->width;
- size_t image_height = yuv420_image_ptr->height;
- size_t map_width = image_width / kMapDimensionScaleFactor;
- size_t map_height = image_height / kMapDimensionScaleFactor;
- if (map_width != gainmap_image_ptr->width || map_height != gainmap_image_ptr->height) {
- ALOGE("gain map dimensions and primary image dimensions are not to scale, computed gain map "
- "resolution is %dx%d, received gain map resolution is %dx%d",
- (int)map_width, (int)map_height, gainmap_image_ptr->width, gainmap_image_ptr->height);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- dest->width = yuv420_image_ptr->width;
- dest->height = yuv420_image_ptr->height;
- ShepardsIDW idwTable(kMapDimensionScaleFactor);
- float display_boost = std::min(max_display_boost, metadata->maxContentBoost);
- GainLUT gainLUT(metadata, display_boost);
-
- JobQueue jobQueue;
- std::function<void()> applyRecMap = [yuv420_image_ptr, gainmap_image_ptr, metadata, dest,
- &jobQueue, &idwTable, output_format, &gainLUT,
- display_boost]() -> void {
- size_t width = yuv420_image_ptr->width;
- size_t height = yuv420_image_ptr->height;
-
- size_t rowStart, rowEnd;
- while (jobQueue.dequeueJob(rowStart, rowEnd)) {
- for (size_t y = rowStart; y < rowEnd; ++y) {
- for (size_t x = 0; x < width; ++x) {
- Color yuv_gamma_sdr = getYuv420Pixel(yuv420_image_ptr, x, y);
- // Assuming the sdr image is a decoded JPEG, we should always use Rec.601 YUV coefficients
- Color rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
- // We are assuming the SDR base image is always sRGB transfer.
-#if USE_SRGB_INVOETF_LUT
- Color rgb_sdr = srgbInvOetfLUT(rgb_gamma_sdr);
-#else
- Color rgb_sdr = srgbInvOetf(rgb_gamma_sdr);
-#endif
- float gain;
- // TODO: determine map scaling factor based on actual map dims
- size_t map_scale_factor = kMapDimensionScaleFactor;
- // TODO: If map_scale_factor is guaranteed to be an integer, then remove the following.
- // Currently map_scale_factor is of type size_t, but it could be changed to a float
- // later.
- if (map_scale_factor != floorf(map_scale_factor)) {
- gain = sampleMap(gainmap_image_ptr, map_scale_factor, x, y);
- } else {
- gain = sampleMap(gainmap_image_ptr, map_scale_factor, x, y, idwTable);
- }
-
-#if USE_APPLY_GAIN_LUT
- Color rgb_hdr = applyGainLUT(rgb_sdr, gain, gainLUT);
-#else
- Color rgb_hdr = applyGain(rgb_sdr, gain, metadata, display_boost);
-#endif
- rgb_hdr = rgb_hdr / display_boost;
- size_t pixel_idx = x + y * width;
-
- switch (output_format) {
- case ULTRAHDR_OUTPUT_HDR_LINEAR: {
- uint64_t rgba_f16 = colorToRgbaF16(rgb_hdr);
- reinterpret_cast<uint64_t*>(dest->data)[pixel_idx] = rgba_f16;
- break;
- }
- case ULTRAHDR_OUTPUT_HDR_HLG: {
-#if USE_HLG_OETF_LUT
- ColorTransformFn hdrOetf = hlgOetfLUT;
-#else
- ColorTransformFn hdrOetf = hlgOetf;
-#endif
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
- uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
- reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
- break;
- }
- case ULTRAHDR_OUTPUT_HDR_PQ: {
-#if USE_PQ_OETF_LUT
- ColorTransformFn hdrOetf = pqOetfLUT;
-#else
- ColorTransformFn hdrOetf = pqOetf;
-#endif
- Color rgb_gamma_hdr = hdrOetf(rgb_hdr);
- uint32_t rgba_1010102 = colorToRgba1010102(rgb_gamma_hdr);
- reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba_1010102;
- break;
- }
- default: {
- }
- // Should be impossible to hit after input validation.
- }
- }
- }
- }
- };
-
- const int threads = std::clamp(GetCPUCoreCount(), 1, 4);
- std::vector<std::thread> workers;
- for (int th = 0; th < threads - 1; th++) {
- workers.push_back(std::thread(applyRecMap));
- }
- const int rowStep = threads == 1 ? yuv420_image_ptr->height : kJobSzInRows;
- for (int rowStart = 0; rowStart < yuv420_image_ptr->height;) {
- int rowEnd = std::min(rowStart + rowStep, yuv420_image_ptr->height);
- jobQueue.enqueueJob(rowStart, rowEnd);
- rowStart = rowEnd;
- }
- jobQueue.markQueueForEnd();
- applyRecMap();
- std::for_each(workers.begin(), workers.end(), [](std::thread& t) { t.join(); });
- return NO_ERROR;
-}
-
-status_t JpegR::extractPrimaryImageAndGainMap(jr_compressed_ptr jpegr_image_ptr,
- jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr) {
- if (jpegr_image_ptr == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
-
- MessageHandler msg_handler;
- std::shared_ptr<DataSegment> seg =
- DataSegment::Create(DataRange(0, jpegr_image_ptr->length),
- static_cast<const uint8_t*>(jpegr_image_ptr->data),
- DataSegment::BufferDispositionPolicy::kDontDelete);
- DataSegmentDataSource data_source(seg);
- JpegInfoBuilder jpeg_info_builder;
- jpeg_info_builder.SetImageLimit(2);
- JpegScanner jpeg_scanner(&msg_handler);
- jpeg_scanner.Run(&data_source, &jpeg_info_builder);
- data_source.Reset();
-
- if (jpeg_scanner.HasError()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- const auto& jpeg_info = jpeg_info_builder.GetInfo();
- const auto& image_ranges = jpeg_info.GetImageRanges();
-
- if (image_ranges.empty()) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- if (primary_jpg_image_ptr != nullptr) {
- primary_jpg_image_ptr->data =
- static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[0].GetBegin();
- primary_jpg_image_ptr->length = image_ranges[0].GetLength();
- }
-
- if (image_ranges.size() == 1) {
- return ERROR_JPEGR_GAIN_MAP_IMAGE_NOT_FOUND;
- }
-
- if (gainmap_jpg_image_ptr != nullptr) {
- gainmap_jpg_image_ptr->data =
- static_cast<uint8_t*>(jpegr_image_ptr->data) + image_ranges[1].GetBegin();
- gainmap_jpg_image_ptr->length = image_ranges[1].GetLength();
- }
-
- // TODO: choose primary image and gain map image carefully
- if (image_ranges.size() > 2) {
- ALOGW("Number of jpeg images present %d, primary, gain map images may not be correctly chosen",
- (int)image_ranges.size());
- }
-
- return NO_ERROR;
-}
-
-// JPEG/R structure:
-// SOI (ff d8)
-//
-// (Optional, if EXIF package is from outside (Encode API-0 API-1), or if EXIF package presents
-// in the JPEG input (Encode API-2, API-3, API-4))
-// APP1 (ff e1)
-// 2 bytes of length (2 + length of exif package)
-// EXIF package (this includes the first two bytes representing the package length)
-//
-// (Required, XMP package) APP1 (ff e1)
-// 2 bytes of length (2 + 29 + length of xmp package)
-// name space ("http://ns.adobe.com/xap/1.0/\0")
-// XMP
-//
-// (Required, MPF package) APP2 (ff e2)
-// 2 bytes of length
-// MPF
-//
-// (Required) primary image (without the first two bytes (SOI) and EXIF, may have other packages)
-//
-// SOI (ff d8)
-//
-// (Required, XMP package) APP1 (ff e1)
-// 2 bytes of length (2 + 29 + length of xmp package)
-// name space ("http://ns.adobe.com/xap/1.0/\0")
-// XMP
-//
-// (Required) secondary image (the gain map, without the first two bytes (SOI))
-//
-// Metadata versions we are using:
-// ECMA TR-98 for JFIF marker
-// Exif 2.2 spec for EXIF marker
-// Adobe XMP spec part 3 for XMP marker
-// ICC v4.3 spec for ICC
-status_t JpegR::appendGainMap(jr_compressed_ptr primary_jpg_image_ptr,
- jr_compressed_ptr gainmap_jpg_image_ptr, jr_exif_ptr pExif,
- void* pIcc, size_t icc_size, ultrahdr_metadata_ptr metadata,
- jr_compressed_ptr dest) {
- if (primary_jpg_image_ptr == nullptr || gainmap_jpg_image_ptr == nullptr || metadata == nullptr ||
- dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (metadata->version.compare("1.0")) {
- ALOGE("received bad value for version: %s", metadata->version.c_str());
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (metadata->maxContentBoost < metadata->minContentBoost) {
- ALOGE("received bad value for content boost min %f, max %f", metadata->minContentBoost,
- metadata->maxContentBoost);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (metadata->hdrCapacityMax < metadata->hdrCapacityMin || metadata->hdrCapacityMin < 1.0f) {
- ALOGE("received bad value for hdr capacity min %f, max %f", metadata->hdrCapacityMin,
- metadata->hdrCapacityMax);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (metadata->offsetSdr < 0.0f || metadata->offsetHdr < 0.0f) {
- ALOGE("received bad value for offset sdr %f, hdr %f", metadata->offsetSdr, metadata->offsetHdr);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- if (metadata->gamma <= 0.0f) {
- ALOGE("received bad value for gamma %f", metadata->gamma);
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
-
- const string nameSpace = "http://ns.adobe.com/xap/1.0/";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
-
- // calculate secondary image length first, because the length will be written into the primary
- // image xmp
- const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
- const int xmp_secondary_length = 2 /* 2 bytes representing the length of the package */
- + nameSpaceLength /* 29 bytes length of name space including \0 */
- + xmp_secondary.size(); /* length of xmp packet */
- const int secondary_image_size = 2 /* 2 bytes length of APP1 sign */
- + xmp_secondary_length + gainmap_jpg_image_ptr->length;
- // primary image
- const string xmp_primary = generateXmpForPrimaryImage(secondary_image_size, *metadata);
- // same as primary
- const int xmp_primary_length = 2 + nameSpaceLength + xmp_primary.size();
-
- // Check if EXIF package presents in the JPEG input.
- // If so, extract and remove the EXIF package.
- JpegDecoderHelper decoder;
- if (!decoder.extractEXIF(primary_jpg_image_ptr->data, primary_jpg_image_ptr->length)) {
- return ERROR_JPEGR_DECODE_ERROR;
- }
- jpegr_exif_struct exif_from_jpg = {.data = nullptr, .length = 0};
- jpegr_compressed_struct new_jpg_image = {.data = nullptr,
- .length = 0,
- .maxLength = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- std::unique_ptr<uint8_t[]> dest_data;
- if (decoder.getEXIFPos() >= 0) {
- if (pExif != nullptr) {
- ALOGE("received EXIF from outside while the primary image already contains EXIF");
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- copyJpegWithoutExif(&new_jpg_image,
- primary_jpg_image_ptr,
- decoder.getEXIFPos(),
- decoder.getEXIFSize());
- dest_data.reset(reinterpret_cast<uint8_t*>(new_jpg_image.data));
- exif_from_jpg.data = decoder.getEXIFPtr();
- exif_from_jpg.length = decoder.getEXIFSize();
- pExif = &exif_from_jpg;
- }
-
- jr_compressed_ptr final_primary_jpg_image_ptr =
- new_jpg_image.length == 0 ? primary_jpg_image_ptr : &new_jpg_image;
-
- int pos = 0;
- // Begin primary image
- // Write SOI
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
-
- // Write EXIF
- if (pExif != nullptr) {
- const int length = 2 + pExif->length;
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, pExif->data, pExif->length, pos));
- }
-
- // Prepare and write XMP
- {
- const int length = xmp_primary_length;
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
- JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
- }
-
- // Write ICC
- if (pIcc != nullptr && icc_size > 0) {
- const int length = icc_size + 2;
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, pIcc, icc_size, pos));
- }
-
- // Prepare and write MPF
- {
- const int length = 2 + calculateMpfSize();
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- int primary_image_size = pos + length + final_primary_jpg_image_ptr->length;
- // between APP2 + package size + signature
- // ff e2 00 58 4d 50 46 00
- // 2 + 2 + 4 = 8 (bytes)
- // and ff d8 sign of the secondary image
- int secondary_image_offset = primary_image_size - pos - 8;
- sp<DataStruct> mpf = generateMpf(primary_image_size, 0, /* primary_image_offset */
- secondary_image_size, secondary_image_offset);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
- }
-
- // Write primary image
- JPEGR_CHECK(Write(dest, (uint8_t*)final_primary_jpg_image_ptr->data + 2,
- final_primary_jpg_image_ptr->length - 2, pos));
- // Finish primary image
-
- // Begin secondary image (gain map)
- // Write SOI
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
-
- // Prepare and write XMP
- {
- const int length = xmp_secondary_length;
- const uint8_t lengthH = ((length >> 8) & 0xff);
- const uint8_t lengthL = (length & 0xff);
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
- JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
- JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
- JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
- JPEGR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
- }
-
- // Write secondary image
- JPEGR_CHECK(Write(dest, (uint8_t*)gainmap_jpg_image_ptr->data + 2,
- gainmap_jpg_image_ptr->length - 2, pos));
-
- // Set back length
- dest->length = pos;
-
- // Done!
- return NO_ERROR;
-}
-
-status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
- if (src == nullptr || dest == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (src->width != dest->width || src->height != dest->height) {
- return ERROR_JPEGR_INVALID_INPUT_TYPE;
- }
- uint16_t* src_y_data = reinterpret_cast<uint16_t*>(src->data);
- uint8_t* dst_y_data = reinterpret_cast<uint8_t*>(dest->data);
- for (size_t y = 0; y < src->height; ++y) {
- uint16_t* src_y_row = src_y_data + y * src->luma_stride;
- uint8_t* dst_y_row = dst_y_data + y * dest->luma_stride;
- for (size_t x = 0; x < src->width; ++x) {
- uint16_t y_uint = src_y_row[x] >> 6;
- dst_y_row[x] = static_cast<uint8_t>((y_uint >> 2) & 0xff);
- }
- if (dest->width != dest->luma_stride) {
- memset(dst_y_row + dest->width, 0, dest->luma_stride - dest->width);
- }
- }
- uint16_t* src_uv_data = reinterpret_cast<uint16_t*>(src->chroma_data);
- uint8_t* dst_u_data = reinterpret_cast<uint8_t*>(dest->chroma_data);
- size_t dst_v_offset = (dest->chroma_stride * dest->height / 2);
- uint8_t* dst_v_data = dst_u_data + dst_v_offset;
- for (size_t y = 0; y < src->height / 2; ++y) {
- uint16_t* src_uv_row = src_uv_data + y * src->chroma_stride;
- uint8_t* dst_u_row = dst_u_data + y * dest->chroma_stride;
- uint8_t* dst_v_row = dst_v_data + y * dest->chroma_stride;
- for (size_t x = 0; x < src->width / 2; ++x) {
- uint16_t u_uint = src_uv_row[x << 1] >> 6;
- uint16_t v_uint = src_uv_row[(x << 1) + 1] >> 6;
- dst_u_row[x] = static_cast<uint8_t>((u_uint >> 2) & 0xff);
- dst_v_row[x] = static_cast<uint8_t>((v_uint >> 2) & 0xff);
- }
- if (dest->width / 2 != dest->chroma_stride) {
- memset(dst_u_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2);
- memset(dst_v_row + dest->width / 2, 0, dest->chroma_stride - dest->width / 2);
- }
- }
- dest->colorGamut = src->colorGamut;
- return NO_ERROR;
-}
-
-status_t JpegR::convertYuv(jr_uncompressed_ptr image, ultrahdr_color_gamut src_encoding,
- ultrahdr_color_gamut dest_encoding) {
- if (image == nullptr) {
- return ERROR_JPEGR_INVALID_NULL_PTR;
- }
- if (src_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED ||
- dest_encoding == ULTRAHDR_COLORGAMUT_UNSPECIFIED) {
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
-
- ColorTransformFn conversionFn = nullptr;
- switch (src_encoding) {
- case ULTRAHDR_COLORGAMUT_BT709:
- switch (dest_encoding) {
- case ULTRAHDR_COLORGAMUT_BT709:
- return NO_ERROR;
- case ULTRAHDR_COLORGAMUT_P3:
- conversionFn = yuv709To601;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- conversionFn = yuv709To2100;
- break;
- default:
- // Should be impossible to hit after input validation
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- switch (dest_encoding) {
- case ULTRAHDR_COLORGAMUT_BT709:
- conversionFn = yuv601To709;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- return NO_ERROR;
- case ULTRAHDR_COLORGAMUT_BT2100:
- conversionFn = yuv601To2100;
- break;
- default:
- // Should be impossible to hit after input validation
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- switch (dest_encoding) {
- case ULTRAHDR_COLORGAMUT_BT709:
- conversionFn = yuv2100To709;
- break;
- case ULTRAHDR_COLORGAMUT_P3:
- conversionFn = yuv2100To601;
- break;
- case ULTRAHDR_COLORGAMUT_BT2100:
- return NO_ERROR;
- default:
- // Should be impossible to hit after input validation
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
- break;
- default:
- // Should be impossible to hit after input validation
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
-
- if (conversionFn == nullptr) {
- // Should be impossible to hit after input validation
- return ERROR_JPEGR_INVALID_COLORGAMUT;
- }
-
- for (size_t y = 0; y < image->height / 2; ++y) {
- for (size_t x = 0; x < image->width / 2; ++x) {
- transformYuv420(image, x, y, conversionFn);
- }
- }
-
- return NO_ERROR;
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/jpegrutils.cpp b/libs/ultrahdr/jpegrutils.cpp
deleted file mode 100644
index c434eb6..0000000
--- a/libs/ultrahdr/jpegrutils.cpp
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ultrahdr/jpegrutils.h>
-
-#include <algorithm>
-#include <cmath>
-
-#include <image_io/xml/xml_reader.h>
-#include <image_io/xml/xml_writer.h>
-#include <image_io/base/message_handler.h>
-#include <image_io/xml/xml_element_rules.h>
-#include <image_io/xml/xml_handler.h>
-#include <image_io/xml/xml_rule.h>
-#include <utils/Log.h>
-
-using namespace photos_editing_formats::image_io;
-using namespace std;
-
-namespace android::ultrahdr {
-/*
- * Helper function used for generating XMP metadata.
- *
- * @param prefix The prefix part of the name.
- * @param suffix The suffix part of the name.
- * @return A name of the form "prefix:suffix".
- */
-static inline string Name(const string &prefix, const string &suffix) {
- std::stringstream ss;
- ss << prefix << ":" << suffix;
- return ss.str();
-}
-
-DataStruct::DataStruct(int s) {
- data = malloc(s);
- length = s;
- memset(data, 0, s);
- writePos = 0;
-}
-
-DataStruct::~DataStruct() {
- if (data != nullptr) {
- free(data);
- }
-}
-
-void* DataStruct::getData() {
- return data;
-}
-
-int DataStruct::getLength() {
- return length;
-}
-
-int DataStruct::getBytesWritten() {
- return writePos;
-}
-
-bool DataStruct::write8(uint8_t value) {
- uint8_t v = value;
- return write(&v, 1);
-}
-
-bool DataStruct::write16(uint16_t value) {
- uint16_t v = value;
- return write(&v, 2);
-}
-bool DataStruct::write32(uint32_t value) {
- uint32_t v = value;
- return write(&v, 4);
-}
-
-bool DataStruct::write(const void* src, int size) {
- if (writePos + size > length) {
- ALOGE("Writing out of boundary: write position: %d, size: %d, capacity: %d",
- writePos, size, length);
- return false;
- }
- memcpy((uint8_t*) data + writePos, src, size);
- writePos += size;
- return true;
-}
-
-/*
- * Helper function used for writing data to destination.
- */
-status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position) {
- if (position + length > destination->maxLength) {
- return ERROR_JPEGR_BUFFER_TOO_SMALL;
- }
-
- memcpy((uint8_t*)destination->data + sizeof(uint8_t) * position, source, length);
- position += length;
- return NO_ERROR;
-}
-
-// Extremely simple XML Handler - just searches for interesting elements
-class XMPXmlHandler : public XmlHandler {
-public:
-
- XMPXmlHandler() : XmlHandler() {
- state = NotStrarted;
- versionFound = false;
- minContentBoostFound = false;
- maxContentBoostFound = false;
- gammaFound = false;
- offsetSdrFound = false;
- offsetHdrFound = false;
- hdrCapacityMinFound = false;
- hdrCapacityMaxFound = false;
- baseRenditionIsHdrFound = false;
- }
-
- enum ParseState {
- NotStrarted,
- Started,
- Done
- };
-
- virtual DataMatchResult StartElement(const XmlTokenContext& context) {
- string val;
- if (context.BuildTokenValue(&val)) {
- if (!val.compare(containerName)) {
- state = Started;
- } else {
- if (state != Done) {
- state = NotStrarted;
- }
- }
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult FinishElement(const XmlTokenContext& context) {
- if (state == Started) {
- state = Done;
- lastAttributeName = "";
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult AttributeName(const XmlTokenContext& context) {
- string val;
- if (state == Started) {
- if (context.BuildTokenValue(&val)) {
- if (!val.compare(versionAttrName)) {
- lastAttributeName = versionAttrName;
- } else if (!val.compare(maxContentBoostAttrName)) {
- lastAttributeName = maxContentBoostAttrName;
- } else if (!val.compare(minContentBoostAttrName)) {
- lastAttributeName = minContentBoostAttrName;
- } else if (!val.compare(gammaAttrName)) {
- lastAttributeName = gammaAttrName;
- } else if (!val.compare(offsetSdrAttrName)) {
- lastAttributeName = offsetSdrAttrName;
- } else if (!val.compare(offsetHdrAttrName)) {
- lastAttributeName = offsetHdrAttrName;
- } else if (!val.compare(hdrCapacityMinAttrName)) {
- lastAttributeName = hdrCapacityMinAttrName;
- } else if (!val.compare(hdrCapacityMaxAttrName)) {
- lastAttributeName = hdrCapacityMaxAttrName;
- } else if (!val.compare(baseRenditionIsHdrAttrName)) {
- lastAttributeName = baseRenditionIsHdrAttrName;
- } else {
- lastAttributeName = "";
- }
- }
- }
- return context.GetResult();
- }
-
- virtual DataMatchResult AttributeValue(const XmlTokenContext& context) {
- string val;
- if (state == Started) {
- if (context.BuildTokenValue(&val, true)) {
- if (!lastAttributeName.compare(versionAttrName)) {
- versionStr = val;
- versionFound = true;
- } else if (!lastAttributeName.compare(maxContentBoostAttrName)) {
- maxContentBoostStr = val;
- maxContentBoostFound = true;
- } else if (!lastAttributeName.compare(minContentBoostAttrName)) {
- minContentBoostStr = val;
- minContentBoostFound = true;
- } else if (!lastAttributeName.compare(gammaAttrName)) {
- gammaStr = val;
- gammaFound = true;
- } else if (!lastAttributeName.compare(offsetSdrAttrName)) {
- offsetSdrStr = val;
- offsetSdrFound = true;
- } else if (!lastAttributeName.compare(offsetHdrAttrName)) {
- offsetHdrStr = val;
- offsetHdrFound = true;
- } else if (!lastAttributeName.compare(hdrCapacityMinAttrName)) {
- hdrCapacityMinStr = val;
- hdrCapacityMinFound = true;
- } else if (!lastAttributeName.compare(hdrCapacityMaxAttrName)) {
- hdrCapacityMaxStr = val;
- hdrCapacityMaxFound = true;
- } else if (!lastAttributeName.compare(baseRenditionIsHdrAttrName)) {
- baseRenditionIsHdrStr = val;
- baseRenditionIsHdrFound = true;
- }
- }
- }
- return context.GetResult();
- }
-
- bool getVersion(string* version, bool* present) {
- if (state == Done) {
- *version = versionStr;
- *present = versionFound;
- return true;
- } else {
- return false;
- }
- }
-
- bool getMaxContentBoost(float* max_content_boost, bool* present) {
- if (state == Done) {
- *present = maxContentBoostFound;
- stringstream ss(maxContentBoostStr);
- float val;
- if (ss >> val) {
- *max_content_boost = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- bool getMinContentBoost(float* min_content_boost, bool* present) {
- if (state == Done) {
- *present = minContentBoostFound;
- stringstream ss(minContentBoostStr);
- float val;
- if (ss >> val) {
- *min_content_boost = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
- bool getGamma(float* gamma, bool* present) {
- if (state == Done) {
- *present = gammaFound;
- stringstream ss(gammaStr);
- float val;
- if (ss >> val) {
- *gamma = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getOffsetSdr(float* offset_sdr, bool* present) {
- if (state == Done) {
- *present = offsetSdrFound;
- stringstream ss(offsetSdrStr);
- float val;
- if (ss >> val) {
- *offset_sdr = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getOffsetHdr(float* offset_hdr, bool* present) {
- if (state == Done) {
- *present = offsetHdrFound;
- stringstream ss(offsetHdrStr);
- float val;
- if (ss >> val) {
- *offset_hdr = val;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getHdrCapacityMin(float* hdr_capacity_min, bool* present) {
- if (state == Done) {
- *present = hdrCapacityMinFound;
- stringstream ss(hdrCapacityMinStr);
- float val;
- if (ss >> val) {
- *hdr_capacity_min = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getHdrCapacityMax(float* hdr_capacity_max, bool* present) {
- if (state == Done) {
- *present = hdrCapacityMaxFound;
- stringstream ss(hdrCapacityMaxStr);
- float val;
- if (ss >> val) {
- *hdr_capacity_max = exp2(val);
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
- bool getBaseRenditionIsHdr(bool* base_rendition_is_hdr, bool* present) {
- if (state == Done) {
- *present = baseRenditionIsHdrFound;
- if (!baseRenditionIsHdrStr.compare("False")) {
- *base_rendition_is_hdr = false;
- return true;
- } else if (!baseRenditionIsHdrStr.compare("True")) {
- *base_rendition_is_hdr = true;
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
-
-private:
- static const string containerName;
-
- static const string versionAttrName;
- string versionStr;
- bool versionFound;
- static const string maxContentBoostAttrName;
- string maxContentBoostStr;
- bool maxContentBoostFound;
- static const string minContentBoostAttrName;
- string minContentBoostStr;
- bool minContentBoostFound;
- static const string gammaAttrName;
- string gammaStr;
- bool gammaFound;
- static const string offsetSdrAttrName;
- string offsetSdrStr;
- bool offsetSdrFound;
- static const string offsetHdrAttrName;
- string offsetHdrStr;
- bool offsetHdrFound;
- static const string hdrCapacityMinAttrName;
- string hdrCapacityMinStr;
- bool hdrCapacityMinFound;
- static const string hdrCapacityMaxAttrName;
- string hdrCapacityMaxStr;
- bool hdrCapacityMaxFound;
- static const string baseRenditionIsHdrAttrName;
- string baseRenditionIsHdrStr;
- bool baseRenditionIsHdrFound;
-
- string lastAttributeName;
- ParseState state;
-};
-
-// GContainer XMP constants - URI and namespace prefix
-const string kContainerUri = "http://ns.google.com/photos/1.0/container/";
-const string kContainerPrefix = "Container";
-
-// GContainer XMP constants - element and attribute names
-const string kConDirectory = Name(kContainerPrefix, "Directory");
-const string kConItem = Name(kContainerPrefix, "Item");
-
-// GContainer XMP constants - names for XMP handlers
-const string XMPXmlHandler::containerName = "rdf:Description";
-// Item XMP constants - URI and namespace prefix
-const string kItemUri = "http://ns.google.com/photos/1.0/container/item/";
-const string kItemPrefix = "Item";
-
-// Item XMP constants - element and attribute names
-const string kItemLength = Name(kItemPrefix, "Length");
-const string kItemMime = Name(kItemPrefix, "Mime");
-const string kItemSemantic = Name(kItemPrefix, "Semantic");
-
-// Item XMP constants - element and attribute values
-const string kSemanticPrimary = "Primary";
-const string kSemanticGainMap = "GainMap";
-const string kMimeImageJpeg = "image/jpeg";
-
-// GainMap XMP constants - URI and namespace prefix
-const string kGainMapUri = "http://ns.adobe.com/hdr-gain-map/1.0/";
-const string kGainMapPrefix = "hdrgm";
-
-// GainMap XMP constants - element and attribute names
-const string kMapVersion = Name(kGainMapPrefix, "Version");
-const string kMapGainMapMin = Name(kGainMapPrefix, "GainMapMin");
-const string kMapGainMapMax = Name(kGainMapPrefix, "GainMapMax");
-const string kMapGamma = Name(kGainMapPrefix, "Gamma");
-const string kMapOffsetSdr = Name(kGainMapPrefix, "OffsetSDR");
-const string kMapOffsetHdr = Name(kGainMapPrefix, "OffsetHDR");
-const string kMapHDRCapacityMin = Name(kGainMapPrefix, "HDRCapacityMin");
-const string kMapHDRCapacityMax = Name(kGainMapPrefix, "HDRCapacityMax");
-const string kMapBaseRenditionIsHDR = Name(kGainMapPrefix, "BaseRenditionIsHDR");
-
-// GainMap XMP constants - names for XMP handlers
-const string XMPXmlHandler::versionAttrName = kMapVersion;
-const string XMPXmlHandler::minContentBoostAttrName = kMapGainMapMin;
-const string XMPXmlHandler::maxContentBoostAttrName = kMapGainMapMax;
-const string XMPXmlHandler::gammaAttrName = kMapGamma;
-const string XMPXmlHandler::offsetSdrAttrName = kMapOffsetSdr;
-const string XMPXmlHandler::offsetHdrAttrName = kMapOffsetHdr;
-const string XMPXmlHandler::hdrCapacityMinAttrName = kMapHDRCapacityMin;
-const string XMPXmlHandler::hdrCapacityMaxAttrName = kMapHDRCapacityMax;
-const string XMPXmlHandler::baseRenditionIsHdrAttrName = kMapBaseRenditionIsHDR;
-
-bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, ultrahdr_metadata_struct* metadata) {
- string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
-
- if (xmp_size < nameSpace.size()+2) {
- // Data too short
- return false;
- }
-
- if (strncmp(reinterpret_cast<char*>(xmp_data), nameSpace.c_str(), nameSpace.size())) {
- // Not correct namespace
- return false;
- }
-
- // Position the pointers to the start of XMP XML portion
- xmp_data += nameSpace.size()+1;
- xmp_size -= nameSpace.size()+1;
- XMPXmlHandler handler;
-
- // We need to remove tail data until the closing tag. Otherwise parser will throw an error.
- while(xmp_data[xmp_size-1]!='>' && xmp_size > 1) {
- xmp_size--;
- }
-
- string str(reinterpret_cast<const char*>(xmp_data), xmp_size);
- MessageHandler msg_handler;
- unique_ptr<XmlRule> rule(new XmlElementRule);
- XmlReader reader(&handler, &msg_handler);
- reader.StartParse(std::move(rule));
- reader.Parse(str);
- reader.FinishParse();
- if (reader.HasErrors()) {
- // Parse error
- return false;
- }
-
- // Apply default values to any not-present fields, except for Version,
- // maxContentBoost, and hdrCapacityMax, which are required. Return false if
- // we encounter a present field that couldn't be parsed, since this
- // indicates it is invalid (eg. string where there should be a float).
- bool present = false;
- if (!handler.getVersion(&metadata->version, &present) || !present) {
- return false;
- }
- if (!handler.getMaxContentBoost(&metadata->maxContentBoost, &present) || !present) {
- return false;
- }
- if (!handler.getHdrCapacityMax(&metadata->hdrCapacityMax, &present) || !present) {
- return false;
- }
- if (!handler.getMinContentBoost(&metadata->minContentBoost, &present)) {
- if (present) return false;
- metadata->minContentBoost = 1.0f;
- }
- if (!handler.getGamma(&metadata->gamma, &present)) {
- if (present) return false;
- metadata->gamma = 1.0f;
- }
- if (!handler.getOffsetSdr(&metadata->offsetSdr, &present)) {
- if (present) return false;
- metadata->offsetSdr = 1.0f / 64.0f;
- }
- if (!handler.getOffsetHdr(&metadata->offsetHdr, &present)) {
- if (present) return false;
- metadata->offsetHdr = 1.0f / 64.0f;
- }
- if (!handler.getHdrCapacityMin(&metadata->hdrCapacityMin, &present)) {
- if (present) return false;
- metadata->hdrCapacityMin = 1.0f;
- }
-
- bool base_rendition_is_hdr;
- if (!handler.getBaseRenditionIsHdr(&base_rendition_is_hdr, &present)) {
- if (present) return false;
- base_rendition_is_hdr = false;
- }
- if (base_rendition_is_hdr) {
- ALOGE("Base rendition of HDR is not supported!");
- return false;
- }
-
- return true;
-}
-
-string generateXmpForPrimaryImage(int secondary_image_length, ultrahdr_metadata_struct& metadata) {
- const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
- const vector<string> kLiItem({string("rdf:li"), kConItem});
-
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kContainerPrefix, kContainerUri);
- writer.WriteXmlns(kItemPrefix, kItemUri);
- writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
- writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
-
- writer.StartWritingElements(kConDirSeq);
-
- size_t item_depth = writer.StartWritingElement("rdf:li");
- writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
- writer.StartWritingElement(kConItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticPrimary);
- writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
- writer.FinishWritingElementsToDepth(item_depth);
-
- writer.StartWritingElement("rdf:li");
- writer.WriteAttributeNameAndValue("rdf:parseType", "Resource");
- writer.StartWritingElement(kConItem);
- writer.WriteAttributeNameAndValue(kItemSemantic, kSemanticGainMap);
- writer.WriteAttributeNameAndValue(kItemMime, kMimeImageJpeg);
- writer.WriteAttributeNameAndValue(kItemLength, secondary_image_length);
-
- writer.FinishWriting();
-
- return ss.str();
-}
-
-string generateXmpForSecondaryImage(ultrahdr_metadata_struct& metadata) {
- const vector<string> kConDirSeq({kConDirectory, string("rdf:Seq")});
-
- std::stringstream ss;
- photos_editing_formats::image_io::XmlWriter writer(ss);
- writer.StartWritingElement("x:xmpmeta");
- writer.WriteXmlns("x", "adobe:ns:meta/");
- writer.WriteAttributeNameAndValue("x:xmptk", "Adobe XMP Core 5.1.2");
- writer.StartWritingElement("rdf:RDF");
- writer.WriteXmlns("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
- writer.StartWritingElement("rdf:Description");
- writer.WriteXmlns(kGainMapPrefix, kGainMapUri);
- writer.WriteAttributeNameAndValue(kMapVersion, metadata.version);
- writer.WriteAttributeNameAndValue(kMapGainMapMin, log2(metadata.minContentBoost));
- writer.WriteAttributeNameAndValue(kMapGainMapMax, log2(metadata.maxContentBoost));
- writer.WriteAttributeNameAndValue(kMapGamma, metadata.gamma);
- writer.WriteAttributeNameAndValue(kMapOffsetSdr, metadata.offsetSdr);
- writer.WriteAttributeNameAndValue(kMapOffsetHdr, metadata.offsetHdr);
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMin, log2(metadata.hdrCapacityMin));
- writer.WriteAttributeNameAndValue(kMapHDRCapacityMax, log2(metadata.hdrCapacityMax));
- writer.WriteAttributeNameAndValue(kMapBaseRenditionIsHDR, "False");
- writer.FinishWriting();
-
- return ss.str();
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/multipictureformat.cpp b/libs/ultrahdr/multipictureformat.cpp
deleted file mode 100644
index f1679ef..0000000
--- a/libs/ultrahdr/multipictureformat.cpp
+++ /dev/null
@@ -1,94 +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.
- */
-#include <ultrahdr/multipictureformat.h>
-#include <ultrahdr/jpegrutils.h>
-
-namespace android::ultrahdr {
-size_t calculateMpfSize() {
- return sizeof(kMpfSig) + // Signature
- kMpEndianSize + // Endianness
- sizeof(uint32_t) + // Index IFD Offset
- sizeof(uint16_t) + // Tag count
- kTagSerializedCount * kTagSize + // 3 tags at 12 bytes each
- sizeof(uint32_t) + // Attribute IFD offset
- kNumPictures * kMPEntrySize; // MP Entries for each image
-}
-
-sp<DataStruct> generateMpf(int primary_image_size, int primary_image_offset,
- int secondary_image_size, int secondary_image_offset) {
- size_t mpf_size = calculateMpfSize();
- sp<DataStruct> dataStruct = sp<DataStruct>::make(mpf_size);
-
- dataStruct->write(static_cast<const void*>(kMpfSig), sizeof(kMpfSig));
-#if USE_BIG_ENDIAN
- dataStruct->write(static_cast<const void*>(kMpBigEndian), kMpEndianSize);
-#else
- dataStruct->write(static_cast<const void*>(kMpLittleEndian), kMpEndianSize);
-#endif
-
- // Set the Index IFD offset be the position after the endianness value and this offset.
- constexpr uint32_t indexIfdOffset =
- static_cast<uint16_t>(kMpEndianSize + sizeof(kMpfSig));
- dataStruct->write32(Endian_SwapBE32(indexIfdOffset));
-
- // We will write 3 tags (version, number of images, MP entries).
- dataStruct->write16(Endian_SwapBE16(kTagSerializedCount));
-
- // Write the version tag.
- dataStruct->write16(Endian_SwapBE16(kVersionTag));
- dataStruct->write16(Endian_SwapBE16(kVersionType));
- dataStruct->write32(Endian_SwapBE32(kVersionCount));
- dataStruct->write(kVersionExpected, kVersionSize);
-
- // Write the number of images.
- dataStruct->write16(Endian_SwapBE16(kNumberOfImagesTag));
- dataStruct->write16(Endian_SwapBE16(kNumberOfImagesType));
- dataStruct->write32(Endian_SwapBE32(kNumberOfImagesCount));
- dataStruct->write32(Endian_SwapBE32(kNumPictures));
-
- // Write the MP entries.
- dataStruct->write16(Endian_SwapBE16(kMPEntryTag));
- dataStruct->write16(Endian_SwapBE16(kMPEntryType));
- dataStruct->write32(Endian_SwapBE32(kMPEntrySize * kNumPictures));
- const uint32_t mpEntryOffset =
- static_cast<uint32_t>(dataStruct->getBytesWritten() - // The bytes written so far
- sizeof(kMpfSig) + // Excluding the MPF signature
- sizeof(uint32_t) + // The 4 bytes for this offset
- sizeof(uint32_t)); // The 4 bytes for the attribute IFD offset.
- dataStruct->write32(Endian_SwapBE32(mpEntryOffset));
-
- // Write the attribute IFD offset (zero because we don't write it).
- dataStruct->write32(0);
-
- // Write the MP entries for primary image
- dataStruct->write32(
- Endian_SwapBE32(kMPEntryAttributeFormatJpeg | kMPEntryAttributeTypePrimary));
- dataStruct->write32(Endian_SwapBE32(primary_image_size));
- dataStruct->write32(Endian_SwapBE32(primary_image_offset));
- dataStruct->write16(0);
- dataStruct->write16(0);
-
- // Write the MP entries for secondary image
- dataStruct->write32(Endian_SwapBE32(kMPEntryAttributeFormatJpeg));
- dataStruct->write32(Endian_SwapBE32(secondary_image_size));
- dataStruct->write32(Endian_SwapBE32(secondary_image_offset));
- dataStruct->write16(0);
- dataStruct->write16(0);
-
- return dataStruct;
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/AndroidTest.xml b/libs/ultrahdr/tests/AndroidTest.xml
deleted file mode 100644
index 1754a5c..0000000
--- a/libs/ultrahdr/tests/AndroidTest.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<configuration description="Unit test configuration for ultrahdr_unit_test">
- <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
- <option name="cleanup" value="true" />
- <option name="push-file" key="ultrahdr_unit_test" value="/data/local/tmp/ultrahdr_unit_test" />
- <option name="push" value="data/*->/data/local/tmp/" />
- </target_preparer>
- <test class="com.android.tradefed.testtype.GTest" >
- <option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="ultrahdr_unit_test" />
- </test>
-</configuration>
diff --git a/libs/ultrahdr/tests/data/jpeg_image.jpg b/libs/ultrahdr/tests/data/jpeg_image.jpg
deleted file mode 100644
index e285742..0000000
--- a/libs/ultrahdr/tests/data/jpeg_image.jpg
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-318x240.yu12 b/libs/ultrahdr/tests/data/minnie-318x240.yu12
deleted file mode 100644
index 7b2fc71..0000000
--- a/libs/ultrahdr/tests/data/minnie-318x240.yu12
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-320x240-y.jpg b/libs/ultrahdr/tests/data/minnie-320x240-y.jpg
deleted file mode 100644
index 20b5a2c..0000000
--- a/libs/ultrahdr/tests/data/minnie-320x240-y.jpg
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg b/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
deleted file mode 100644
index c7f4538..0000000
--- a/libs/ultrahdr/tests/data/minnie-320x240-yuv-icc.jpg
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-320x240-yuv.jpg b/libs/ultrahdr/tests/data/minnie-320x240-yuv.jpg
deleted file mode 100644
index 41300f4..0000000
--- a/libs/ultrahdr/tests/data/minnie-320x240-yuv.jpg
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-320x240.y b/libs/ultrahdr/tests/data/minnie-320x240.y
deleted file mode 100644
index f9d8371..0000000
--- a/libs/ultrahdr/tests/data/minnie-320x240.y
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/minnie-320x240.yu12 b/libs/ultrahdr/tests/data/minnie-320x240.yu12
deleted file mode 100644
index 0d66f53..0000000
--- a/libs/ultrahdr/tests/data/minnie-320x240.yu12
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/raw_p010_image.p010 b/libs/ultrahdr/tests/data/raw_p010_image.p010
deleted file mode 100644
index 01673bf..0000000
--- a/libs/ultrahdr/tests/data/raw_p010_image.p010
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/data/raw_yuv420_image.yuv420 b/libs/ultrahdr/tests/data/raw_yuv420_image.yuv420
deleted file mode 100644
index c043da6..0000000
--- a/libs/ultrahdr/tests/data/raw_yuv420_image.yuv420
+++ /dev/null
Binary files differ
diff --git a/libs/ultrahdr/tests/gainmapmath_test.cpp b/libs/ultrahdr/tests/gainmapmath_test.cpp
deleted file mode 100644
index 7c2d076..0000000
--- a/libs/ultrahdr/tests/gainmapmath_test.cpp
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <cmath>
-#include <gtest/gtest.h>
-#include <gmock/gmock.h>
-#include <ultrahdr/gainmapmath.h>
-
-namespace android::ultrahdr {
-
-class GainMapMathTest : public testing::Test {
-public:
- GainMapMathTest();
- ~GainMapMathTest();
-
- float ComparisonEpsilon() { return 1e-4f; }
- float LuminanceEpsilon() { return 1e-2f; }
- float YuvConversionEpsilon() { return 1.0f / (255.0f * 2.0f); }
-
- Color Yuv420(uint8_t y, uint8_t u, uint8_t v) {
- return {{{ static_cast<float>(y) / 255.0f,
- (static_cast<float>(u) - 128.0f) / 255.0f,
- (static_cast<float>(v) - 128.0f) / 255.0f }}};
- }
-
- Color P010(uint16_t y, uint16_t u, uint16_t v) {
- return {{{ (static_cast<float>(y) - 64.0f) / 876.0f,
- (static_cast<float>(u) - 64.0f) / 896.0f - 0.5f,
- (static_cast<float>(v) - 64.0f) / 896.0f - 0.5f }}};
- }
-
- float Map(uint8_t e) {
- return static_cast<float>(e) / 255.0f;
- }
-
- Color ColorMin(Color e1, Color e2) {
- return {{{ fmin(e1.r, e2.r), fmin(e1.g, e2.g), fmin(e1.b, e2.b) }}};
- }
-
- Color ColorMax(Color e1, Color e2) {
- return {{{ fmax(e1.r, e2.r), fmax(e1.g, e2.g), fmax(e1.b, e2.b) }}};
- }
-
- Color RgbBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
- Color RgbWhite() { return {{{ 1.0f, 1.0f, 1.0f }}}; }
-
- Color RgbRed() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
- Color RgbGreen() { return {{{ 0.0f, 1.0f, 0.0f }}}; }
- Color RgbBlue() { return {{{ 0.0f, 0.0f, 1.0f }}}; }
-
- Color YuvBlack() { return {{{ 0.0f, 0.0f, 0.0f }}}; }
- Color YuvWhite() { return {{{ 1.0f, 0.0f, 0.0f }}}; }
-
- Color SrgbYuvRed() { return {{{ 0.2126f, -0.11457f, 0.5f }}}; }
- Color SrgbYuvGreen() { return {{{ 0.7152f, -0.38543f, -0.45415f }}}; }
- Color SrgbYuvBlue() { return {{{ 0.0722f, 0.5f, -0.04585f }}}; }
-
- Color P3YuvRed() { return {{{ 0.299f, -0.16874f, 0.5f }}}; }
- Color P3YuvGreen() { return {{{ 0.587f, -0.33126f, -0.41869f }}}; }
- Color P3YuvBlue() { return {{{ 0.114f, 0.5f, -0.08131f }}}; }
-
- Color Bt2100YuvRed() { return {{{ 0.2627f, -0.13963f, 0.5f }}}; }
- Color Bt2100YuvGreen() { return {{{ 0.6780f, -0.36037f, -0.45979f }}}; }
- Color Bt2100YuvBlue() { return {{{ 0.0593f, 0.5f, -0.04021f }}}; }
-
- float SrgbYuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
- Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
- Color rgb = srgbInvOetf(rgb_gamma);
- float luminance_scaled = luminanceFn(rgb);
- return luminance_scaled * kSdrWhiteNits;
- }
-
- float P3YuvToLuminance(Color yuv_gamma, ColorCalculationFn luminanceFn) {
- Color rgb_gamma = p3YuvToRgb(yuv_gamma);
- Color rgb = srgbInvOetf(rgb_gamma);
- float luminance_scaled = luminanceFn(rgb);
- return luminance_scaled * kSdrWhiteNits;
- }
-
- float Bt2100YuvToLuminance(Color yuv_gamma, ColorTransformFn hdrInvOetf,
- ColorTransformFn gamutConversionFn, ColorCalculationFn luminanceFn,
- float scale_factor) {
- Color rgb_gamma = bt2100YuvToRgb(yuv_gamma);
- Color rgb = hdrInvOetf(rgb_gamma);
- rgb = gamutConversionFn(rgb);
- float luminance_scaled = luminanceFn(rgb);
- return luminance_scaled * scale_factor;
- }
-
- Color Recover(Color yuv_gamma, float gain, ultrahdr_metadata_ptr metadata) {
- Color rgb_gamma = srgbYuvToRgb(yuv_gamma);
- Color rgb = srgbInvOetf(rgb_gamma);
- return applyGain(rgb, gain, metadata);
- }
-
- jpegr_uncompressed_struct Yuv420Image() {
- static uint8_t pixels[] = {
- // Y
- 0x00, 0x10, 0x20, 0x30,
- 0x01, 0x11, 0x21, 0x31,
- 0x02, 0x12, 0x22, 0x32,
- 0x03, 0x13, 0x23, 0x33,
- // U
- 0xA0, 0xA1,
- 0xA2, 0xA3,
- // V
- 0xB0, 0xB1,
- 0xB2, 0xB3,
- };
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 2 };
- }
-
- Color (*Yuv420Colors())[4] {
- static Color colors[4][4] = {
- {
- Yuv420(0x00, 0xA0, 0xB0), Yuv420(0x10, 0xA0, 0xB0),
- Yuv420(0x20, 0xA1, 0xB1), Yuv420(0x30, 0xA1, 0xB1),
- }, {
- Yuv420(0x01, 0xA0, 0xB0), Yuv420(0x11, 0xA0, 0xB0),
- Yuv420(0x21, 0xA1, 0xB1), Yuv420(0x31, 0xA1, 0xB1),
- }, {
- Yuv420(0x02, 0xA2, 0xB2), Yuv420(0x12, 0xA2, 0xB2),
- Yuv420(0x22, 0xA3, 0xB3), Yuv420(0x32, 0xA3, 0xB3),
- }, {
- Yuv420(0x03, 0xA2, 0xB2), Yuv420(0x13, 0xA2, 0xB2),
- Yuv420(0x23, 0xA3, 0xB3), Yuv420(0x33, 0xA3, 0xB3),
- },
- };
- return colors;
- }
-
- jpegr_uncompressed_struct P010Image() {
- static uint16_t pixels[] = {
- // Y
- 0x00 << 6, 0x10 << 6, 0x20 << 6, 0x30 << 6,
- 0x01 << 6, 0x11 << 6, 0x21 << 6, 0x31 << 6,
- 0x02 << 6, 0x12 << 6, 0x22 << 6, 0x32 << 6,
- 0x03 << 6, 0x13 << 6, 0x23 << 6, 0x33 << 6,
- // UV
- 0xA0 << 6, 0xB0 << 6, 0xA1 << 6, 0xB1 << 6,
- 0xA2 << 6, 0xB2 << 6, 0xA3 << 6, 0xB3 << 6,
- };
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_BT709, pixels + 16, 4, 4 };
- }
-
- Color (*P010Colors())[4] {
- static Color colors[4][4] = {
- {
- P010(0x00, 0xA0, 0xB0), P010(0x10, 0xA0, 0xB0),
- P010(0x20, 0xA1, 0xB1), P010(0x30, 0xA1, 0xB1),
- }, {
- P010(0x01, 0xA0, 0xB0), P010(0x11, 0xA0, 0xB0),
- P010(0x21, 0xA1, 0xB1), P010(0x31, 0xA1, 0xB1),
- }, {
- P010(0x02, 0xA2, 0xB2), P010(0x12, 0xA2, 0xB2),
- P010(0x22, 0xA3, 0xB3), P010(0x32, 0xA3, 0xB3),
- }, {
- P010(0x03, 0xA2, 0xB2), P010(0x13, 0xA2, 0xB2),
- P010(0x23, 0xA3, 0xB3), P010(0x33, 0xA3, 0xB3),
- },
- };
- return colors;
- }
-
- jpegr_uncompressed_struct MapImage() {
- static uint8_t pixels[] = {
- 0x00, 0x10, 0x20, 0x30,
- 0x01, 0x11, 0x21, 0x31,
- 0x02, 0x12, 0x22, 0x32,
- 0x03, 0x13, 0x23, 0x33,
- };
- return { pixels, 4, 4, ULTRAHDR_COLORGAMUT_UNSPECIFIED };
- }
-
- float (*MapValues())[4] {
- static float values[4][4] = {
- {
- Map(0x00), Map(0x10), Map(0x20), Map(0x30),
- }, {
- Map(0x01), Map(0x11), Map(0x21), Map(0x31),
- }, {
- Map(0x02), Map(0x12), Map(0x22), Map(0x32),
- }, {
- Map(0x03), Map(0x13), Map(0x23), Map(0x33),
- },
- };
- return values;
- }
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-};
-
-GainMapMathTest::GainMapMathTest() {}
-GainMapMathTest::~GainMapMathTest() {}
-
-void GainMapMathTest::SetUp() {}
-void GainMapMathTest::TearDown() {}
-
-#define EXPECT_RGB_EQ(e1, e2) \
- EXPECT_FLOAT_EQ((e1).r, (e2).r); \
- EXPECT_FLOAT_EQ((e1).g, (e2).g); \
- EXPECT_FLOAT_EQ((e1).b, (e2).b)
-
-#define EXPECT_RGB_NEAR(e1, e2) \
- EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon()); \
- EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon()); \
- EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon())
-
-#define EXPECT_RGB_CLOSE(e1, e2) \
- EXPECT_NEAR((e1).r, (e2).r, ComparisonEpsilon() * 10.0f); \
- EXPECT_NEAR((e1).g, (e2).g, ComparisonEpsilon() * 10.0f); \
- EXPECT_NEAR((e1).b, (e2).b, ComparisonEpsilon() * 10.0f)
-
-#define EXPECT_YUV_EQ(e1, e2) \
- EXPECT_FLOAT_EQ((e1).y, (e2).y); \
- EXPECT_FLOAT_EQ((e1).u, (e2).u); \
- EXPECT_FLOAT_EQ((e1).v, (e2).v)
-
-#define EXPECT_YUV_NEAR(e1, e2) \
- EXPECT_NEAR((e1).y, (e2).y, ComparisonEpsilon()); \
- EXPECT_NEAR((e1).u, (e2).u, ComparisonEpsilon()); \
- EXPECT_NEAR((e1).v, (e2).v, ComparisonEpsilon())
-
-#define EXPECT_YUV_BETWEEN(e, min, max) \
- EXPECT_THAT((e).y, testing::AllOf(testing::Ge((min).y), testing::Le((max).y))); \
- EXPECT_THAT((e).u, testing::AllOf(testing::Ge((min).u), testing::Le((max).u))); \
- EXPECT_THAT((e).v, testing::AllOf(testing::Ge((min).v), testing::Le((max).v)))
-
-// TODO: a bunch of these tests can be parameterized.
-
-TEST_F(GainMapMathTest, ColorConstruct) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- EXPECT_FLOAT_EQ(e1.r, 0.1f);
- EXPECT_FLOAT_EQ(e1.g, 0.2f);
- EXPECT_FLOAT_EQ(e1.b, 0.3f);
-
- EXPECT_FLOAT_EQ(e1.y, 0.1f);
- EXPECT_FLOAT_EQ(e1.u, 0.2f);
- EXPECT_FLOAT_EQ(e1.v, 0.3f);
-}
-
-TEST_F(GainMapMathTest, ColorAddColor) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 + e1;
- EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
-
- e2 += e1;
- EXPECT_FLOAT_EQ(e2.r, e1.r * 3.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g * 3.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b * 3.0f);
-}
-
-TEST_F(GainMapMathTest, ColorAddFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 + 0.1f;
- EXPECT_FLOAT_EQ(e2.r, e1.r + 0.1f);
- EXPECT_FLOAT_EQ(e2.g, e1.g + 0.1f);
- EXPECT_FLOAT_EQ(e2.b, e1.b + 0.1f);
-
- e2 += 0.1f;
- EXPECT_FLOAT_EQ(e2.r, e1.r + 0.2f);
- EXPECT_FLOAT_EQ(e2.g, e1.g + 0.2f);
- EXPECT_FLOAT_EQ(e2.b, e1.b + 0.2f);
-}
-
-TEST_F(GainMapMathTest, ColorSubtractColor) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 - e1;
- EXPECT_FLOAT_EQ(e2.r, 0.0f);
- EXPECT_FLOAT_EQ(e2.g, 0.0f);
- EXPECT_FLOAT_EQ(e2.b, 0.0f);
-
- e2 -= e1;
- EXPECT_FLOAT_EQ(e2.r, -e1.r);
- EXPECT_FLOAT_EQ(e2.g, -e1.g);
- EXPECT_FLOAT_EQ(e2.b, -e1.b);
-}
-
-TEST_F(GainMapMathTest, ColorSubtractFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 - 0.1f;
- EXPECT_FLOAT_EQ(e2.r, e1.r - 0.1f);
- EXPECT_FLOAT_EQ(e2.g, e1.g - 0.1f);
- EXPECT_FLOAT_EQ(e2.b, e1.b - 0.1f);
-
- e2 -= 0.1f;
- EXPECT_FLOAT_EQ(e2.r, e1.r - 0.2f);
- EXPECT_FLOAT_EQ(e2.g, e1.g - 0.2f);
- EXPECT_FLOAT_EQ(e2.b, e1.b - 0.2f);
-}
-
-TEST_F(GainMapMathTest, ColorMultiplyFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 * 2.0f;
- EXPECT_FLOAT_EQ(e2.r, e1.r * 2.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g * 2.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b * 2.0f);
-
- e2 *= 2.0f;
- EXPECT_FLOAT_EQ(e2.r, e1.r * 4.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g * 4.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b * 4.0f);
-}
-
-TEST_F(GainMapMathTest, ColorDivideFloat) {
- Color e1 = {{{ 0.1f, 0.2f, 0.3f }}};
-
- Color e2 = e1 / 2.0f;
- EXPECT_FLOAT_EQ(e2.r, e1.r / 2.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g / 2.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b / 2.0f);
-
- e2 /= 2.0f;
- EXPECT_FLOAT_EQ(e2.r, e1.r / 4.0f);
- EXPECT_FLOAT_EQ(e2.g, e1.g / 4.0f);
- EXPECT_FLOAT_EQ(e2.b, e1.b / 4.0f);
-}
-
-TEST_F(GainMapMathTest, SrgbLuminance) {
- EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
- EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
- EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
- EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
- EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
-}
-
-TEST_F(GainMapMathTest, SrgbYuvToRgb) {
- Color rgb_black = srgbYuvToRgb(YuvBlack());
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = srgbYuvToRgb(YuvWhite());
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = srgbYuvToRgb(SrgbYuvRed());
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = srgbYuvToRgb(SrgbYuvGreen());
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = srgbYuvToRgb(SrgbYuvBlue());
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-
-TEST_F(GainMapMathTest, SrgbRgbToYuv) {
- Color yuv_black = srgbRgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv_black, YuvBlack());
-
- Color yuv_white = srgbRgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv_white, YuvWhite());
-
- Color yuv_r = srgbRgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv_r, SrgbYuvRed());
-
- Color yuv_g = srgbRgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv_g, SrgbYuvGreen());
-
- Color yuv_b = srgbRgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv_b, SrgbYuvBlue());
-}
-
-TEST_F(GainMapMathTest, SrgbRgbYuvRoundtrip) {
- Color rgb_black = srgbYuvToRgb(srgbRgbToYuv(RgbBlack()));
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = srgbYuvToRgb(srgbRgbToYuv(RgbWhite()));
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = srgbYuvToRgb(srgbRgbToYuv(RgbRed()));
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = srgbYuvToRgb(srgbRgbToYuv(RgbGreen()));
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = srgbYuvToRgb(srgbRgbToYuv(RgbBlue()));
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-
-TEST_F(GainMapMathTest, SrgbTransferFunction) {
- EXPECT_FLOAT_EQ(srgbInvOetf(0.0f), 0.0f);
- EXPECT_NEAR(srgbInvOetf(0.02f), 0.00154f, ComparisonEpsilon());
- EXPECT_NEAR(srgbInvOetf(0.04045f), 0.00313f, ComparisonEpsilon());
- EXPECT_NEAR(srgbInvOetf(0.5f), 0.21404f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(srgbInvOetf(1.0f), 1.0f);
-}
-
-TEST_F(GainMapMathTest, P3Luminance) {
- EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
- EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
- EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
- EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
- EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
-}
-
-TEST_F(GainMapMathTest, P3YuvToRgb) {
- Color rgb_black = p3YuvToRgb(YuvBlack());
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = p3YuvToRgb(YuvWhite());
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = p3YuvToRgb(P3YuvRed());
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = p3YuvToRgb(P3YuvGreen());
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = p3YuvToRgb(P3YuvBlue());
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-
-TEST_F(GainMapMathTest, P3RgbToYuv) {
- Color yuv_black = p3RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv_black, YuvBlack());
-
- Color yuv_white = p3RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv_white, YuvWhite());
-
- Color yuv_r = p3RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv_r, P3YuvRed());
-
- Color yuv_g = p3RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv_g, P3YuvGreen());
-
- Color yuv_b = p3RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv_b, P3YuvBlue());
-}
-
-TEST_F(GainMapMathTest, P3RgbYuvRoundtrip) {
- Color rgb_black = p3YuvToRgb(p3RgbToYuv(RgbBlack()));
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = p3YuvToRgb(p3RgbToYuv(RgbWhite()));
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = p3YuvToRgb(p3RgbToYuv(RgbRed()));
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = p3YuvToRgb(p3RgbToYuv(RgbGreen()));
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = p3YuvToRgb(p3RgbToYuv(RgbBlue()));
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-TEST_F(GainMapMathTest, Bt2100Luminance) {
- EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
- EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
- EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
- EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
- EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
-}
-
-TEST_F(GainMapMathTest, Bt2100YuvToRgb) {
- Color rgb_black = bt2100YuvToRgb(YuvBlack());
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = bt2100YuvToRgb(YuvWhite());
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = bt2100YuvToRgb(Bt2100YuvRed());
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = bt2100YuvToRgb(Bt2100YuvGreen());
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = bt2100YuvToRgb(Bt2100YuvBlue());
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-
-TEST_F(GainMapMathTest, Bt2100RgbToYuv) {
- Color yuv_black = bt2100RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv_black, YuvBlack());
-
- Color yuv_white = bt2100RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv_white, YuvWhite());
-
- Color yuv_r = bt2100RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv_r, Bt2100YuvRed());
-
- Color yuv_g = bt2100RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv_g, Bt2100YuvGreen());
-
- Color yuv_b = bt2100RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv_b, Bt2100YuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt2100RgbYuvRoundtrip) {
- Color rgb_black = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlack()));
- EXPECT_RGB_NEAR(rgb_black, RgbBlack());
-
- Color rgb_white = bt2100YuvToRgb(bt2100RgbToYuv(RgbWhite()));
- EXPECT_RGB_NEAR(rgb_white, RgbWhite());
-
- Color rgb_r = bt2100YuvToRgb(bt2100RgbToYuv(RgbRed()));
- EXPECT_RGB_NEAR(rgb_r, RgbRed());
-
- Color rgb_g = bt2100YuvToRgb(bt2100RgbToYuv(RgbGreen()));
- EXPECT_RGB_NEAR(rgb_g, RgbGreen());
-
- Color rgb_b = bt2100YuvToRgb(bt2100RgbToYuv(RgbBlue()));
- EXPECT_RGB_NEAR(rgb_b, RgbBlue());
-}
-
-TEST_F(GainMapMathTest, Bt709ToBt601YuvConversion) {
- Color yuv_black = srgbRgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv709To601(yuv_black), YuvBlack());
-
- Color yuv_white = srgbRgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv709To601(yuv_white), YuvWhite());
-
- Color yuv_r = srgbRgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv709To601(yuv_r), P3YuvRed());
-
- Color yuv_g = srgbRgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv709To601(yuv_g), P3YuvGreen());
-
- Color yuv_b = srgbRgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv709To601(yuv_b), P3YuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt709ToBt2100YuvConversion) {
- Color yuv_black = srgbRgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv709To2100(yuv_black), YuvBlack());
-
- Color yuv_white = srgbRgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv709To2100(yuv_white), YuvWhite());
-
- Color yuv_r = srgbRgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv709To2100(yuv_r), Bt2100YuvRed());
-
- Color yuv_g = srgbRgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv709To2100(yuv_g), Bt2100YuvGreen());
-
- Color yuv_b = srgbRgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv709To2100(yuv_b), Bt2100YuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt601ToBt709YuvConversion) {
- Color yuv_black = p3RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv601To709(yuv_black), YuvBlack());
-
- Color yuv_white = p3RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv601To709(yuv_white), YuvWhite());
-
- Color yuv_r = p3RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv601To709(yuv_r), SrgbYuvRed());
-
- Color yuv_g = p3RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv601To709(yuv_g), SrgbYuvGreen());
-
- Color yuv_b = p3RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv601To709(yuv_b), SrgbYuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt601ToBt2100YuvConversion) {
- Color yuv_black = p3RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv601To2100(yuv_black), YuvBlack());
-
- Color yuv_white = p3RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv601To2100(yuv_white), YuvWhite());
-
- Color yuv_r = p3RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv601To2100(yuv_r), Bt2100YuvRed());
-
- Color yuv_g = p3RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv601To2100(yuv_g), Bt2100YuvGreen());
-
- Color yuv_b = p3RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv601To2100(yuv_b), Bt2100YuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt2100ToBt709YuvConversion) {
- Color yuv_black = bt2100RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv2100To709(yuv_black), YuvBlack());
-
- Color yuv_white = bt2100RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv2100To709(yuv_white), YuvWhite());
-
- Color yuv_r = bt2100RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv2100To709(yuv_r), SrgbYuvRed());
-
- Color yuv_g = bt2100RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv2100To709(yuv_g), SrgbYuvGreen());
-
- Color yuv_b = bt2100RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv2100To709(yuv_b), SrgbYuvBlue());
-}
-
-TEST_F(GainMapMathTest, Bt2100ToBt601YuvConversion) {
- Color yuv_black = bt2100RgbToYuv(RgbBlack());
- EXPECT_YUV_NEAR(yuv2100To601(yuv_black), YuvBlack());
-
- Color yuv_white = bt2100RgbToYuv(RgbWhite());
- EXPECT_YUV_NEAR(yuv2100To601(yuv_white), YuvWhite());
-
- Color yuv_r = bt2100RgbToYuv(RgbRed());
- EXPECT_YUV_NEAR(yuv2100To601(yuv_r), P3YuvRed());
-
- Color yuv_g = bt2100RgbToYuv(RgbGreen());
- EXPECT_YUV_NEAR(yuv2100To601(yuv_g), P3YuvGreen());
-
- Color yuv_b = bt2100RgbToYuv(RgbBlue());
- EXPECT_YUV_NEAR(yuv2100To601(yuv_b), P3YuvBlue());
-}
-
-TEST_F(GainMapMathTest, TransformYuv420) {
- ColorTransformFn transforms[] = { yuv709To601, yuv709To2100, yuv601To709, yuv601To2100,
- yuv2100To709, yuv2100To601 };
- for (const ColorTransformFn& transform : transforms) {
- jpegr_uncompressed_struct input = Yuv420Image();
-
- size_t out_buf_size = input.width * input.height * 3 / 2;
- std::unique_ptr<uint8_t[]> out_buf = std::make_unique<uint8_t[]>(out_buf_size);
- memcpy(out_buf.get(), input.data, out_buf_size);
- jpegr_uncompressed_struct output = Yuv420Image();
- output.data = out_buf.get();
- output.chroma_data = out_buf.get() + input.width * input.height;
- output.luma_stride = input.width;
- output.chroma_stride = input.width / 2;
-
- transformYuv420(&output, 1, 1, transform);
-
- for (size_t y = 0; y < 4; ++y) {
- for (size_t x = 0; x < 4; ++x) {
- // Skip the last chroma sample, which we modified above
- if (x >= 2 && y >= 2) {
- continue;
- }
-
- // All other pixels should remain unchanged
- EXPECT_YUV_EQ(getYuv420Pixel(&input, x, y), getYuv420Pixel(&output, x, y));
- }
- }
-
- // modified pixels should be updated as intended by the transformYuv420 algorithm
- Color in1 = getYuv420Pixel(&input, 2, 2);
- Color in2 = getYuv420Pixel(&input, 3, 2);
- Color in3 = getYuv420Pixel(&input, 2, 3);
- Color in4 = getYuv420Pixel(&input, 3, 3);
- Color out1 = getYuv420Pixel(&output, 2, 2);
- Color out2 = getYuv420Pixel(&output, 3, 2);
- Color out3 = getYuv420Pixel(&output, 2, 3);
- Color out4 = getYuv420Pixel(&output, 3, 3);
-
- EXPECT_NEAR(transform(in1).y, out1.y, YuvConversionEpsilon());
- EXPECT_NEAR(transform(in2).y, out2.y, YuvConversionEpsilon());
- EXPECT_NEAR(transform(in3).y, out3.y, YuvConversionEpsilon());
- EXPECT_NEAR(transform(in4).y, out4.y, YuvConversionEpsilon());
-
- Color expect_uv = (transform(in1) + transform(in2) + transform(in3) + transform(in4)) / 4.0f;
-
- EXPECT_NEAR(expect_uv.u, out1.u, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.u, out2.u, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.u, out3.u, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.u, out4.u, YuvConversionEpsilon());
-
- EXPECT_NEAR(expect_uv.v, out1.v, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.v, out2.v, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.v, out3.v, YuvConversionEpsilon());
- EXPECT_NEAR(expect_uv.v, out4.v, YuvConversionEpsilon());
- }
-}
-
-TEST_F(GainMapMathTest, HlgOetf) {
- EXPECT_FLOAT_EQ(hlgOetf(0.0f), 0.0f);
- EXPECT_NEAR(hlgOetf(0.04167f), 0.35357f, ComparisonEpsilon());
- EXPECT_NEAR(hlgOetf(0.08333f), 0.5f, ComparisonEpsilon());
- EXPECT_NEAR(hlgOetf(0.5f), 0.87164f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(hlgOetf(1.0f), 1.0f);
-
- Color e = {{{ 0.04167f, 0.08333f, 0.5f }}};
- Color e_gamma = {{{ 0.35357f, 0.5f, 0.87164f }}};
- EXPECT_RGB_NEAR(hlgOetf(e), e_gamma);
-}
-
-TEST_F(GainMapMathTest, HlgInvOetf) {
- EXPECT_FLOAT_EQ(hlgInvOetf(0.0f), 0.0f);
- EXPECT_NEAR(hlgInvOetf(0.25f), 0.02083f, ComparisonEpsilon());
- EXPECT_NEAR(hlgInvOetf(0.5f), 0.08333f, ComparisonEpsilon());
- EXPECT_NEAR(hlgInvOetf(0.75f), 0.26496f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(hlgInvOetf(1.0f), 1.0f);
-
- Color e_gamma = {{{ 0.25f, 0.5f, 0.75f }}};
- Color e = {{{ 0.02083f, 0.08333f, 0.26496f }}};
- EXPECT_RGB_NEAR(hlgInvOetf(e_gamma), e);
-}
-
-TEST_F(GainMapMathTest, HlgTransferFunctionRoundtrip) {
- EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(0.0f)), 0.0f);
- EXPECT_NEAR(hlgInvOetf(hlgOetf(0.04167f)), 0.04167f, ComparisonEpsilon());
- EXPECT_NEAR(hlgInvOetf(hlgOetf(0.08333f)), 0.08333f, ComparisonEpsilon());
- EXPECT_NEAR(hlgInvOetf(hlgOetf(0.5f)), 0.5f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(hlgInvOetf(hlgOetf(1.0f)), 1.0f);
-}
-
-TEST_F(GainMapMathTest, PqOetf) {
- EXPECT_FLOAT_EQ(pqOetf(0.0f), 0.0f);
- EXPECT_NEAR(pqOetf(0.01f), 0.50808f, ComparisonEpsilon());
- EXPECT_NEAR(pqOetf(0.5f), 0.92655f, ComparisonEpsilon());
- EXPECT_NEAR(pqOetf(0.99f), 0.99895f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(pqOetf(1.0f), 1.0f);
-
- Color e = {{{ 0.01f, 0.5f, 0.99f }}};
- Color e_gamma = {{{ 0.50808f, 0.92655f, 0.99895f }}};
- EXPECT_RGB_NEAR(pqOetf(e), e_gamma);
-}
-
-TEST_F(GainMapMathTest, PqInvOetf) {
- EXPECT_FLOAT_EQ(pqInvOetf(0.0f), 0.0f);
- EXPECT_NEAR(pqInvOetf(0.01f), 2.31017e-7f, ComparisonEpsilon());
- EXPECT_NEAR(pqInvOetf(0.5f), 0.00922f, ComparisonEpsilon());
- EXPECT_NEAR(pqInvOetf(0.99f), 0.90903f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(pqInvOetf(1.0f), 1.0f);
-
- Color e_gamma = {{{ 0.01f, 0.5f, 0.99f }}};
- Color e = {{{ 2.31017e-7f, 0.00922f, 0.90903f }}};
- EXPECT_RGB_NEAR(pqInvOetf(e_gamma), e);
-}
-
-TEST_F(GainMapMathTest, PqInvOetfLUT) {
- for (int idx = 0; idx < kPqInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(pqInvOetf(value), pqInvOetfLUT(value));
- }
-}
-
-TEST_F(GainMapMathTest, HlgInvOetfLUT) {
- for (int idx = 0; idx < kHlgInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(hlgInvOetf(value), hlgInvOetfLUT(value));
- }
-}
-
-TEST_F(GainMapMathTest, pqOetfLUT) {
- for (int idx = 0; idx < kPqOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kPqOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(pqOetf(value), pqOetfLUT(value));
- }
-}
-
-TEST_F(GainMapMathTest, hlgOetfLUT) {
- for (int idx = 0; idx < kHlgOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kHlgOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(hlgOetf(value), hlgOetfLUT(value));
- }
-}
-
-TEST_F(GainMapMathTest, srgbInvOetfLUT) {
- for (int idx = 0; idx < kSrgbInvOETFNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kSrgbInvOETFNumEntries - 1);
- EXPECT_FLOAT_EQ(srgbInvOetf(value), srgbInvOetfLUT(value));
- }
-}
-
-TEST_F(GainMapMathTest, applyGainLUT) {
- for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f / static_cast<float>(boost) };
- GainLUT gainLUT(&metadata);
- GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
- for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
- applyGainLUT(RgbBlack(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
- applyGainLUT(RgbWhite(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
- applyGainLUT(RgbRed(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
- applyGainLUT(RgbGreen(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
- applyGainLUT(RgbBlue(), value, gainLUT));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
- applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
- applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
- applyGainLUT(RgbRed(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
- applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
- applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
- }
- }
-
- for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f };
- GainLUT gainLUT(&metadata);
- GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
- for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
- applyGainLUT(RgbBlack(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
- applyGainLUT(RgbWhite(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
- applyGainLUT(RgbRed(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
- applyGainLUT(RgbGreen(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
- applyGainLUT(RgbBlue(), value, gainLUT));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
- applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
- applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
- applyGainLUT(RgbRed(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
- applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
- applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
- }
- }
-
- for (int boost = 1; boost <= 10; boost++) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = static_cast<float>(boost),
- .minContentBoost = 1.0f / pow(static_cast<float>(boost),
- 1.0f / 3.0f) };
- GainLUT gainLUT(&metadata);
- GainLUT gainLUTWithBoost(&metadata, metadata.maxContentBoost);
- for (int idx = 0; idx < kGainFactorNumEntries; idx++) {
- float value = static_cast<float>(idx) / static_cast<float>(kGainFactorNumEntries - 1);
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), value, &metadata),
- applyGainLUT(RgbBlack(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), value, &metadata),
- applyGainLUT(RgbWhite(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbRed(), value, &metadata),
- applyGainLUT(RgbRed(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbGreen(), value, &metadata),
- applyGainLUT(RgbGreen(), value, gainLUT));
- EXPECT_RGB_NEAR(applyGain(RgbBlue(), value, &metadata),
- applyGainLUT(RgbBlue(), value, gainLUT));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlack(), value, gainLUT),
- applyGainLUT(RgbBlack(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbWhite(), value, gainLUT),
- applyGainLUT(RgbWhite(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbRed(), value, gainLUT),
- applyGainLUT(RgbRed(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbGreen(), value, gainLUT),
- applyGainLUT(RgbGreen(), value, gainLUTWithBoost));
- EXPECT_RGB_EQ(applyGainLUT(RgbBlue(), value, gainLUT),
- applyGainLUT(RgbBlue(), value, gainLUTWithBoost));
- }
- }
-}
-
-TEST_F(GainMapMathTest, PqTransferFunctionRoundtrip) {
- EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(0.0f)), 0.0f);
- EXPECT_NEAR(pqInvOetf(pqOetf(0.01f)), 0.01f, ComparisonEpsilon());
- EXPECT_NEAR(pqInvOetf(pqOetf(0.5f)), 0.5f, ComparisonEpsilon());
- EXPECT_NEAR(pqInvOetf(pqOetf(0.99f)), 0.99f, ComparisonEpsilon());
- EXPECT_FLOAT_EQ(pqInvOetf(pqOetf(1.0f)), 1.0f);
-}
-
-TEST_F(GainMapMathTest, ColorConversionLookup) {
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT709),
- identityConversion);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3),
- p3ToBt709);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_BT2100),
- bt2100ToBt709);
-
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT709),
- bt709ToP3);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_P3),
- identityConversion);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100),
- bt2100ToP3);
-
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT709),
- bt709ToBt2100);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_P3),
- p3ToBt2100);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_BT2100, ULTRAHDR_COLORGAMUT_BT2100),
- identityConversion);
-
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_UNSPECIFIED),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT709),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_P3),
- nullptr);
- EXPECT_EQ(getHdrConversionFn(ULTRAHDR_COLORGAMUT_UNSPECIFIED, ULTRAHDR_COLORGAMUT_BT2100),
- nullptr);
-}
-
-TEST_F(GainMapMathTest, EncodeGain) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
-
- EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 127);
- EXPECT_EQ(encodeGain(0.0f, 1.0f, &metadata), 127);
- EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(0.5f, 0.0f, &metadata), 0);
-
- EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 127);
- EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(1.0f, 5.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(4.0f, 1.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(4.0f, 0.5f, &metadata), 0);
- EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 191);
- EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 63);
-
- metadata.maxContentBoost = 2.0f;
- metadata.minContentBoost = 1.0f / 2.0f;
-
- EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(2.0f, 1.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(1.0f, 1.41421f, &metadata), 191);
- EXPECT_EQ(encodeGain(1.41421f, 1.0f, &metadata), 63);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 1.0f / 8.0f;
-
- EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(8.0f, 1.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(1.0f, 2.82843f, &metadata), 191);
- EXPECT_EQ(encodeGain(2.82843f, 1.0f, &metadata), 63);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 1.0f;
-
- EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
-
- EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 0);
- EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 170);
- EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 85);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 0.5f;
-
- EXPECT_EQ(encodeGain(0.0f, 0.0f, &metadata), 63);
- EXPECT_EQ(encodeGain(1.0f, 0.0f, &metadata), 0);
-
- EXPECT_EQ(encodeGain(1.0f, 1.0f, &metadata), 63);
- EXPECT_EQ(encodeGain(1.0f, 8.0f, &metadata), 255);
- EXPECT_EQ(encodeGain(1.0f, 4.0f, &metadata), 191);
- EXPECT_EQ(encodeGain(1.0f, 2.0f, &metadata), 127);
- EXPECT_EQ(encodeGain(1.0f, 0.7071f, &metadata), 31);
- EXPECT_EQ(encodeGain(1.0f, 0.5f, &metadata), 0);
-}
-
-TEST_F(GainMapMathTest, ApplyGain) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 4.0f,
- .minContentBoost = 1.0f / 4.0f };
- float displayBoost = metadata.maxContentBoost;
-
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.0f, &metadata), RgbBlack());
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), 0.5f, &metadata), RgbBlack());
- EXPECT_RGB_NEAR(applyGain(RgbBlack(), 1.0f, &metadata), RgbBlack());
-
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 4.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 4.0f);
-
- metadata.maxContentBoost = 2.0f;
- metadata.minContentBoost = 1.0f / 2.0f;
-
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 1.41421f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 1.41421f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 2.0f);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 1.0f / 8.0f;
-
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 8.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite() / 2.82843f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite());
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 2.82843f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 1.0f;
-
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite());
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f / 3.0f, &metadata), RgbWhite() * 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 2.0f / 3.0f, &metadata), RgbWhite() * 4.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 0.5f;
-
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.0f, &metadata), RgbWhite() / 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.25f, &metadata), RgbWhite());
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.5f, &metadata), RgbWhite() * 2.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 0.75f, &metadata), RgbWhite() * 4.0f);
- EXPECT_RGB_NEAR(applyGain(RgbWhite(), 1.0f, &metadata), RgbWhite() * 8.0f);
-
- Color e = {{{ 0.0f, 0.5f, 1.0f }}};
- metadata.maxContentBoost = 4.0f;
- metadata.minContentBoost = 1.0f / 4.0f;
-
- EXPECT_RGB_NEAR(applyGain(e, 0.0f, &metadata), e / 4.0f);
- EXPECT_RGB_NEAR(applyGain(e, 0.25f, &metadata), e / 2.0f);
- EXPECT_RGB_NEAR(applyGain(e, 0.5f, &metadata), e);
- EXPECT_RGB_NEAR(applyGain(e, 0.75f, &metadata), e * 2.0f);
- EXPECT_RGB_NEAR(applyGain(e, 1.0f, &metadata), e * 4.0f);
-
- EXPECT_RGB_EQ(applyGain(RgbBlack(), 1.0f, &metadata),
- applyGain(RgbBlack(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(RgbWhite(), 1.0f, &metadata),
- applyGain(RgbWhite(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(RgbRed(), 1.0f, &metadata),
- applyGain(RgbRed(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(RgbGreen(), 1.0f, &metadata),
- applyGain(RgbGreen(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(RgbBlue(), 1.0f, &metadata),
- applyGain(RgbBlue(), 1.0f, &metadata, displayBoost));
- EXPECT_RGB_EQ(applyGain(e, 1.0f, &metadata),
- applyGain(e, 1.0f, &metadata, displayBoost));
-}
-
-TEST_F(GainMapMathTest, GetYuv420Pixel) {
- jpegr_uncompressed_struct image = Yuv420Image();
- Color (*colors)[4] = Yuv420Colors();
-
- for (size_t y = 0; y < 4; ++y) {
- for (size_t x = 0; x < 4; ++x) {
- EXPECT_YUV_NEAR(getYuv420Pixel(&image, x, y), colors[y][x]);
- }
- }
-}
-
-TEST_F(GainMapMathTest, GetP010Pixel) {
- jpegr_uncompressed_struct image = P010Image();
- Color (*colors)[4] = P010Colors();
-
- for (size_t y = 0; y < 4; ++y) {
- for (size_t x = 0; x < 4; ++x) {
- EXPECT_YUV_NEAR(getP010Pixel(&image, x, y), colors[y][x]);
- }
- }
-}
-
-TEST_F(GainMapMathTest, SampleYuv420) {
- jpegr_uncompressed_struct image = Yuv420Image();
- Color (*colors)[4] = Yuv420Colors();
-
- static const size_t kMapScaleFactor = 2;
- for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
- for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
- Color min = {{{ 1.0f, 1.0f, 1.0f }}};
- Color max = {{{ -1.0f, -1.0f, -1.0f }}};
-
- for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
- for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
- Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
- min = ColorMin(min, e);
- max = ColorMax(max, e);
- }
- }
-
- // Instead of reimplementing the sampling algorithm, confirm that the
- // sample output is within the range of the min and max of the nearest
- // points.
- EXPECT_YUV_BETWEEN(sampleYuv420(&image, kMapScaleFactor, x, y), min, max);
- }
- }
-}
-
-TEST_F(GainMapMathTest, SampleP010) {
- jpegr_uncompressed_struct image = P010Image();
- Color (*colors)[4] = P010Colors();
-
- static const size_t kMapScaleFactor = 2;
- for (size_t y = 0; y < 4 / kMapScaleFactor; ++y) {
- for (size_t x = 0; x < 4 / kMapScaleFactor; ++x) {
- Color min = {{{ 1.0f, 1.0f, 1.0f }}};
- Color max = {{{ -1.0f, -1.0f, -1.0f }}};
-
- for (size_t dy = 0; dy < kMapScaleFactor; ++dy) {
- for (size_t dx = 0; dx < kMapScaleFactor; ++dx) {
- Color e = colors[y * kMapScaleFactor + dy][x * kMapScaleFactor + dx];
- min = ColorMin(min, e);
- max = ColorMax(max, e);
- }
- }
-
- // Instead of reimplementing the sampling algorithm, confirm that the
- // sample output is within the range of the min and max of the nearest
- // points.
- EXPECT_YUV_BETWEEN(sampleP010(&image, kMapScaleFactor, x, y), min, max);
- }
- }
-}
-
-TEST_F(GainMapMathTest, SampleMap) {
- jpegr_uncompressed_struct image = MapImage();
- float (*values)[4] = MapValues();
-
- static const size_t kMapScaleFactor = 2;
- ShepardsIDW idwTable(kMapScaleFactor);
- for (size_t y = 0; y < 4 * kMapScaleFactor; ++y) {
- for (size_t x = 0; x < 4 * kMapScaleFactor; ++x) {
- size_t x_base = x / kMapScaleFactor;
- size_t y_base = y / kMapScaleFactor;
-
- float min = 1.0f;
- float max = -1.0f;
-
- min = fmin(min, values[y_base][x_base]);
- max = fmax(max, values[y_base][x_base]);
- if (y_base + 1 < 4) {
- min = fmin(min, values[y_base + 1][x_base]);
- max = fmax(max, values[y_base + 1][x_base]);
- }
- if (x_base + 1 < 4) {
- min = fmin(min, values[y_base][x_base + 1]);
- max = fmax(max, values[y_base][x_base + 1]);
- }
- if (y_base + 1 < 4 && x_base + 1 < 4) {
- min = fmin(min, values[y_base + 1][x_base + 1]);
- max = fmax(max, values[y_base + 1][x_base + 1]);
- }
-
- // Instead of reimplementing the sampling algorithm, confirm that the
- // sample output is within the range of the min and max of the nearest
- // points.
- EXPECT_THAT(sampleMap(&image, kMapScaleFactor, x, y),
- testing::AllOf(testing::Ge(min), testing::Le(max)));
- EXPECT_EQ(sampleMap(&image, kMapScaleFactor, x, y, idwTable),
- sampleMap(&image, kMapScaleFactor, x, y));
- }
- }
-}
-
-TEST_F(GainMapMathTest, ColorToRgba1010102) {
- EXPECT_EQ(colorToRgba1010102(RgbBlack()), 0x3 << 30);
- EXPECT_EQ(colorToRgba1010102(RgbWhite()), 0xFFFFFFFF);
- EXPECT_EQ(colorToRgba1010102(RgbRed()), 0x3 << 30 | 0x3ff);
- EXPECT_EQ(colorToRgba1010102(RgbGreen()), 0x3 << 30 | 0x3ff << 10);
- EXPECT_EQ(colorToRgba1010102(RgbBlue()), 0x3 << 30 | 0x3ff << 20);
-
- Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
- EXPECT_EQ(colorToRgba1010102(e_gamma),
- 0x3 << 30
- | static_cast<uint32_t>(0.1f * static_cast<float>(0x3ff))
- | static_cast<uint32_t>(0.2f * static_cast<float>(0x3ff)) << 10
- | static_cast<uint32_t>(0.3f * static_cast<float>(0x3ff)) << 20);
-}
-
-TEST_F(GainMapMathTest, ColorToRgbaF16) {
- EXPECT_EQ(colorToRgbaF16(RgbBlack()), ((uint64_t) 0x3C00) << 48);
- EXPECT_EQ(colorToRgbaF16(RgbWhite()), 0x3C003C003C003C00);
- EXPECT_EQ(colorToRgbaF16(RgbRed()), (((uint64_t) 0x3C00) << 48) | ((uint64_t) 0x3C00));
- EXPECT_EQ(colorToRgbaF16(RgbGreen()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 16));
- EXPECT_EQ(colorToRgbaF16(RgbBlue()), (((uint64_t) 0x3C00) << 48) | (((uint64_t) 0x3C00) << 32));
-
- Color e_gamma = {{{ 0.1f, 0.2f, 0.3f }}};
- EXPECT_EQ(colorToRgbaF16(e_gamma), 0x3C0034CD32662E66);
-}
-
-TEST_F(GainMapMathTest, Float32ToFloat16) {
- EXPECT_EQ(floatToHalf(0.1f), 0x2E66);
- EXPECT_EQ(floatToHalf(0.0f), 0x0);
- EXPECT_EQ(floatToHalf(1.0f), 0x3C00);
- EXPECT_EQ(floatToHalf(-1.0f), 0xBC00);
- EXPECT_EQ(floatToHalf(0x1.fffffep127f), 0x7FFF); // float max
- EXPECT_EQ(floatToHalf(-0x1.fffffep127f), 0xFFFF); // float min
- EXPECT_EQ(floatToHalf(0x1.0p-126f), 0x0); // float zero
-}
-
-TEST_F(GainMapMathTest, GenerateMapLuminanceSrgb) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), srgbLuminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), srgbLuminance),
- kSdrWhiteNits);
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), srgbLuminance),
- srgbLuminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), srgbLuminance),
- srgbLuminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), srgbLuminance),
- srgbLuminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
-}
-
-TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbP3) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), p3Luminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), p3Luminance),
- kSdrWhiteNits);
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), p3Luminance),
- p3Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), p3Luminance),
- p3Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), p3Luminance),
- p3Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
-}
-
-TEST_F(GainMapMathTest, GenerateMapLuminanceSrgbBt2100) {
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvBlack(), bt2100Luminance),
- 0.0f);
- EXPECT_FLOAT_EQ(SrgbYuvToLuminance(YuvWhite(), bt2100Luminance),
- kSdrWhiteNits);
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvRed(), bt2100Luminance),
- bt2100Luminance(RgbRed()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvGreen(), bt2100Luminance),
- bt2100Luminance(RgbGreen()) * kSdrWhiteNits, LuminanceEpsilon());
- EXPECT_NEAR(SrgbYuvToLuminance(SrgbYuvBlue(), bt2100Luminance),
- bt2100Luminance(RgbBlue()) * kSdrWhiteNits, LuminanceEpsilon());
-}
-
-TEST_F(GainMapMathTest, GenerateMapLuminanceHlg) {
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
- 0.0f);
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
- kHlgMaxNits);
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
- bt2100Luminance(RgbRed()) * kHlgMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
- bt2100Luminance(RgbGreen()) * kHlgMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), hlgInvOetf, identityConversion,
- bt2100Luminance, kHlgMaxNits),
- bt2100Luminance(RgbBlue()) * kHlgMaxNits, LuminanceEpsilon());
-}
-
-TEST_F(GainMapMathTest, GenerateMapLuminancePq) {
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvBlack(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- 0.0f);
- EXPECT_FLOAT_EQ(Bt2100YuvToLuminance(YuvWhite(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- kPqMaxNits);
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvRed(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- bt2100Luminance(RgbRed()) * kPqMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvGreen(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- bt2100Luminance(RgbGreen()) * kPqMaxNits, LuminanceEpsilon());
- EXPECT_NEAR(Bt2100YuvToLuminance(Bt2100YuvBlue(), pqInvOetf, identityConversion,
- bt2100Luminance, kPqMaxNits),
- bt2100Luminance(RgbBlue()) * kPqMaxNits, LuminanceEpsilon());
-}
-
-TEST_F(GainMapMathTest, ApplyMap) {
- ultrahdr_metadata_struct metadata = { .maxContentBoost = 8.0f,
- .minContentBoost = 1.0f / 8.0f };
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvBlack(), 1.0f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 1.0f, &metadata),
- RgbRed() * 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 1.0f, &metadata),
- RgbGreen() * 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 1.0f, &metadata),
- RgbBlue() * 8.0f);
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75f, &metadata),
- RgbWhite() * sqrt(8.0f));
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.75f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.75f, &metadata),
- RgbRed() * sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.75f, &metadata),
- RgbGreen() * sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.75f, &metadata),
- RgbBlue() * sqrt(8.0f));
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
- RgbWhite());
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.5f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.5f, &metadata),
- RgbRed());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.5f, &metadata),
- RgbGreen());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.5f, &metadata),
- RgbBlue());
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
- RgbWhite() / sqrt(8.0f));
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.25f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.25f, &metadata),
- RgbRed() / sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.25f, &metadata),
- RgbGreen() / sqrt(8.0f));
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.25f, &metadata),
- RgbBlue() / sqrt(8.0f));
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite() / 8.0f);
- EXPECT_RGB_EQ(Recover(YuvBlack(), 0.0f, &metadata),
- RgbBlack());
- EXPECT_RGB_CLOSE(Recover(SrgbYuvRed(), 0.0f, &metadata),
- RgbRed() / 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvGreen(), 0.0f, &metadata),
- RgbGreen() / 8.0f);
- EXPECT_RGB_CLOSE(Recover(SrgbYuvBlue(), 0.0f, &metadata),
- RgbBlue() / 8.0f);
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 1.0f;
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 2.0f / 3.0f, &metadata),
- RgbWhite() * 4.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f / 3.0f, &metadata),
- RgbWhite() * 2.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite());
-
- metadata.maxContentBoost = 8.0f;
- metadata.minContentBoost = 0.5f;;
-
- EXPECT_RGB_EQ(Recover(YuvWhite(), 1.0f, &metadata),
- RgbWhite() * 8.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.75, &metadata),
- RgbWhite() * 4.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.5f, &metadata),
- RgbWhite() * 2.0f);
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.25f, &metadata),
- RgbWhite());
- EXPECT_RGB_EQ(Recover(YuvWhite(), 0.0f, &metadata),
- RgbWhite() / 2.0f);
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/icchelper_test.cpp b/libs/ultrahdr/tests/icchelper_test.cpp
deleted file mode 100644
index ff61c08..0000000
--- a/libs/ultrahdr/tests/icchelper_test.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <ultrahdr/icc.h>
-#include <ultrahdr/ultrahdr.h>
-#include <utils/Log.h>
-
-namespace android::ultrahdr {
-
-class IccHelperTest : public testing::Test {
-public:
- IccHelperTest();
- ~IccHelperTest();
-protected:
- virtual void SetUp();
- virtual void TearDown();
-};
-
-IccHelperTest::IccHelperTest() {}
-
-IccHelperTest::~IccHelperTest() {}
-
-void IccHelperTest::SetUp() {}
-
-void IccHelperTest::TearDown() {}
-
-TEST_F(IccHelperTest, iccWriteThenRead) {
- sp<DataStruct> iccBt709 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- ULTRAHDR_COLORGAMUT_BT709);
- ASSERT_NE(iccBt709->getLength(), 0);
- ASSERT_NE(iccBt709->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccBt709->getData(), iccBt709->getLength()),
- ULTRAHDR_COLORGAMUT_BT709);
-
- sp<DataStruct> iccP3 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_P3);
- ASSERT_NE(iccP3->getLength(), 0);
- ASSERT_NE(iccP3->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccP3->getData(), iccP3->getLength()),
- ULTRAHDR_COLORGAMUT_P3);
-
- sp<DataStruct> iccBt2100 = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB,
- ULTRAHDR_COLORGAMUT_BT2100);
- ASSERT_NE(iccBt2100->getLength(), 0);
- ASSERT_NE(iccBt2100->getData(), nullptr);
- EXPECT_EQ(IccHelper::readIccColorGamut(iccBt2100->getData(), iccBt2100->getLength()),
- ULTRAHDR_COLORGAMUT_BT2100);
-}
-
-TEST_F(IccHelperTest, iccEndianness) {
- sp<DataStruct> icc = IccHelper::writeIccProfile(ULTRAHDR_TF_SRGB, ULTRAHDR_COLORGAMUT_BT709);
- size_t profile_size = icc->getLength() - kICCIdentifierSize;
-
- uint8_t* icc_bytes = reinterpret_cast<uint8_t*>(icc->getData()) + kICCIdentifierSize;
- uint32_t encoded_size = static_cast<uint32_t>(icc_bytes[0]) << 24 |
- static_cast<uint32_t>(icc_bytes[1]) << 16 |
- static_cast<uint32_t>(icc_bytes[2]) << 8 |
- static_cast<uint32_t>(icc_bytes[3]);
-
- EXPECT_EQ(static_cast<size_t>(encoded_size), profile_size);
-}
-
-} // namespace android::ultrahdr
-
diff --git a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp b/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
deleted file mode 100644
index af0d59e..0000000
--- a/libs/ultrahdr/tests/jpegdecoderhelper_test.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gtest/gtest.h>
-#include <ultrahdr/icc.h>
-#include <ultrahdr/jpegdecoderhelper.h>
-#include <utils/Log.h>
-
-#include <fcntl.h>
-
-namespace android::ultrahdr {
-
-// No ICC or EXIF
-#define YUV_IMAGE "/data/local/tmp/minnie-320x240-yuv.jpg"
-#define YUV_IMAGE_SIZE 20193
-// Has ICC and EXIF
-#define YUV_ICC_IMAGE "/data/local/tmp/minnie-320x240-yuv-icc.jpg"
-#define YUV_ICC_IMAGE_SIZE 34266
-// No ICC or EXIF
-#define GREY_IMAGE "/data/local/tmp/minnie-320x240-y.jpg"
-#define GREY_IMAGE_SIZE 20193
-
-#define IMAGE_WIDTH 320
-#define IMAGE_HEIGHT 240
-
-class JpegDecoderHelperTest : public testing::Test {
-public:
- struct Image {
- std::unique_ptr<uint8_t[]> buffer;
- size_t size;
- };
- JpegDecoderHelperTest();
- ~JpegDecoderHelperTest();
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-
- Image mYuvImage, mYuvIccImage, mGreyImage;
-};
-
-JpegDecoderHelperTest::JpegDecoderHelperTest() {}
-
-JpegDecoderHelperTest::~JpegDecoderHelperTest() {}
-
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
-static bool loadFile(const char filename[], JpegDecoderHelperTest::Image* result) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- result->buffer.reset(new uint8_t[length]);
- if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
- return true;
-}
-
-void JpegDecoderHelperTest::SetUp() {
- if (!loadFile(YUV_IMAGE, &mYuvImage)) {
- FAIL() << "Load file " << YUV_IMAGE << " failed";
- }
- mYuvImage.size = YUV_IMAGE_SIZE;
- if (!loadFile(YUV_ICC_IMAGE, &mYuvIccImage)) {
- FAIL() << "Load file " << YUV_ICC_IMAGE << " failed";
- }
- mYuvIccImage.size = YUV_ICC_IMAGE_SIZE;
- if (!loadFile(GREY_IMAGE, &mGreyImage)) {
- FAIL() << "Load file " << GREY_IMAGE << " failed";
- }
- mGreyImage.size = GREY_IMAGE_SIZE;
-}
-
-void JpegDecoderHelperTest::TearDown() {}
-
-TEST_F(JpegDecoderHelperTest, decodeYuvImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mYuvImage.buffer.get(), mYuvImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
- EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
- ULTRAHDR_COLORGAMUT_UNSPECIFIED);
-}
-
-TEST_F(JpegDecoderHelperTest, decodeYuvIccImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mYuvIccImage.buffer.get(), mYuvIccImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
- EXPECT_EQ(IccHelper::readIccColorGamut(decoder.getICCPtr(), decoder.getICCSize()),
- ULTRAHDR_COLORGAMUT_BT709);
-}
-
-TEST_F(JpegDecoderHelperTest, decodeGreyImage) {
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.decompressImage(mGreyImage.buffer.get(), mGreyImage.size));
- ASSERT_GT(decoder.getDecompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-TEST_F(JpegDecoderHelperTest, getCompressedImageParameters) {
- size_t width = 0, height = 0;
- std::vector<uint8_t> icc, exif;
-
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvImage.buffer.get(), mYuvImage.size, &width,
- &height, &icc, &exif));
-
- EXPECT_EQ(width, IMAGE_WIDTH);
- EXPECT_EQ(height, IMAGE_HEIGHT);
- EXPECT_EQ(icc.size(), 0);
- EXPECT_EQ(exif.size(), 0);
-}
-
-TEST_F(JpegDecoderHelperTest, getCompressedImageParametersIcc) {
- size_t width = 0, height = 0;
- std::vector<uint8_t> icc, exif;
-
- JpegDecoderHelper decoder;
- EXPECT_TRUE(decoder.getCompressedImageParameters(mYuvIccImage.buffer.get(), mYuvIccImage.size,
- &width, &height, &icc, &exif));
-
- EXPECT_EQ(width, IMAGE_WIDTH);
- EXPECT_EQ(height, IMAGE_HEIGHT);
- EXPECT_GT(icc.size(), 0);
- EXPECT_GT(exif.size(), 0);
-
- EXPECT_EQ(IccHelper::readIccColorGamut(icc.data(), icc.size()), ULTRAHDR_COLORGAMUT_BT709);
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp b/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
deleted file mode 100644
index af54eb2..0000000
--- a/libs/ultrahdr/tests/jpegencoderhelper_test.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <ultrahdr/jpegencoderhelper.h>
-#include <gtest/gtest.h>
-#include <utils/Log.h>
-
-#include <fcntl.h>
-
-namespace android::ultrahdr {
-
-#define ALIGNED_IMAGE "/data/local/tmp/minnie-320x240.yu12"
-#define ALIGNED_IMAGE_WIDTH 320
-#define ALIGNED_IMAGE_HEIGHT 240
-#define SINGLE_CHANNEL_IMAGE "/data/local/tmp/minnie-320x240.y"
-#define SINGLE_CHANNEL_IMAGE_WIDTH ALIGNED_IMAGE_WIDTH
-#define SINGLE_CHANNEL_IMAGE_HEIGHT ALIGNED_IMAGE_HEIGHT
-#define UNALIGNED_IMAGE "/data/local/tmp/minnie-318x240.yu12"
-#define UNALIGNED_IMAGE_WIDTH 318
-#define UNALIGNED_IMAGE_HEIGHT 240
-#define JPEG_QUALITY 90
-
-class JpegEncoderHelperTest : public testing::Test {
-public:
- struct Image {
- std::unique_ptr<uint8_t[]> buffer;
- size_t width;
- size_t height;
- };
- JpegEncoderHelperTest();
- ~JpegEncoderHelperTest();
-
-protected:
- virtual void SetUp();
- virtual void TearDown();
-
- Image mAlignedImage, mUnalignedImage, mSingleChannelImage;
-};
-
-JpegEncoderHelperTest::JpegEncoderHelperTest() {}
-
-JpegEncoderHelperTest::~JpegEncoderHelperTest() {}
-
-static size_t getFileSize(int fd) {
- struct stat st;
- if (fstat(fd, &st) < 0) {
- ALOGW("%s : fstat failed", __func__);
- return 0;
- }
- return st.st_size; // bytes
-}
-
-static bool loadFile(const char filename[], JpegEncoderHelperTest::Image* result) {
- int fd = open(filename, O_CLOEXEC);
- if (fd < 0) {
- return false;
- }
- int length = getFileSize(fd);
- if (length == 0) {
- close(fd);
- return false;
- }
- result->buffer.reset(new uint8_t[length]);
- if (read(fd, result->buffer.get(), length) != static_cast<ssize_t>(length)) {
- close(fd);
- return false;
- }
- close(fd);
- return true;
-}
-
-void JpegEncoderHelperTest::SetUp() {
- if (!loadFile(ALIGNED_IMAGE, &mAlignedImage)) {
- FAIL() << "Load file " << ALIGNED_IMAGE << " failed";
- }
- mAlignedImage.width = ALIGNED_IMAGE_WIDTH;
- mAlignedImage.height = ALIGNED_IMAGE_HEIGHT;
- if (!loadFile(UNALIGNED_IMAGE, &mUnalignedImage)) {
- FAIL() << "Load file " << UNALIGNED_IMAGE << " failed";
- }
- mUnalignedImage.width = UNALIGNED_IMAGE_WIDTH;
- mUnalignedImage.height = UNALIGNED_IMAGE_HEIGHT;
- if (!loadFile(SINGLE_CHANNEL_IMAGE, &mSingleChannelImage)) {
- FAIL() << "Load file " << SINGLE_CHANNEL_IMAGE << " failed";
- }
- mSingleChannelImage.width = SINGLE_CHANNEL_IMAGE_WIDTH;
- mSingleChannelImage.height = SINGLE_CHANNEL_IMAGE_HEIGHT;
-}
-
-void JpegEncoderHelperTest::TearDown() {}
-
-TEST_F(JpegEncoderHelperTest, encodeAlignedImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mAlignedImage.buffer.get(),
- mAlignedImage.buffer.get() +
- mAlignedImage.width * mAlignedImage.height,
- mAlignedImage.width, mAlignedImage.height,
- mAlignedImage.width, mAlignedImage.width / 2, JPEG_QUALITY,
- NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-TEST_F(JpegEncoderHelperTest, encodeUnalignedImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mUnalignedImage.buffer.get(),
- mUnalignedImage.buffer.get() +
- mUnalignedImage.width * mUnalignedImage.height,
- mUnalignedImage.width, mUnalignedImage.height,
- mUnalignedImage.width, mUnalignedImage.width / 2,
- JPEG_QUALITY, NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-TEST_F(JpegEncoderHelperTest, encodeSingleChannelImage) {
- JpegEncoderHelper encoder;
- EXPECT_TRUE(encoder.compressImage(mSingleChannelImage.buffer.get(), nullptr,
- mSingleChannelImage.width, mSingleChannelImage.height,
- mSingleChannelImage.width, 0, JPEG_QUALITY, NULL, 0));
- ASSERT_GT(encoder.getCompressedImageSize(), static_cast<uint32_t>(0));
-}
-
-} // namespace android::ultrahdr
diff --git a/libs/ultrahdr/tests/jpegr_test.cpp b/libs/ultrahdr/tests/jpegr_test.cpp
deleted file mode 100644
index 5fa758e..0000000
--- a/libs/ultrahdr/tests/jpegr_test.cpp
+++ /dev/null
@@ -1,2035 +0,0 @@
-/*
- * Copyright 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <sys/time.h>
-#include <fstream>
-#include <iostream>
-
-#include <ultrahdr/gainmapmath.h>
-#include <ultrahdr/jpegr.h>
-#include <ultrahdr/jpegrutils.h>
-
-#include <gtest/gtest.h>
-#include <utils/Log.h>
-
-//#define DUMP_OUTPUT
-
-namespace android::ultrahdr {
-
-// resources used by unit tests
-const char* kYCbCrP010FileName = "/data/local/tmp/raw_p010_image.p010";
-const char* kYCbCr420FileName = "/data/local/tmp/raw_yuv420_image.yuv420";
-const char* kSdrJpgFileName = "/data/local/tmp/jpeg_image.jpg";
-const int kImageWidth = 1280;
-const int kImageHeight = 720;
-const int kQuality = 90;
-
-// Wrapper to describe the input type
-typedef enum {
- YCbCr_p010 = 0,
- YCbCr_420 = 1,
-} UhdrInputFormat;
-
-/**
- * Wrapper class for raw resource
- * Sample usage:
- * UhdrUnCompressedStructWrapper rawImg(width, height, YCbCr_p010);
- * rawImg.setImageColorGamut(colorGamut));
- * rawImg.setImageStride(strideLuma, strideChroma); // optional
- * rawImg.setChromaMode(false); // optional
- * rawImg.allocateMemory();
- * rawImg.loadRawResource(kYCbCrP010FileName);
- */
-class UhdrUnCompressedStructWrapper {
-public:
- UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height, UhdrInputFormat format);
- ~UhdrUnCompressedStructWrapper() = default;
-
- bool setChromaMode(bool isChromaContiguous);
- bool setImageStride(int lumaStride, int chromaStride);
- bool setImageColorGamut(ultrahdr_color_gamut colorGamut);
- bool allocateMemory();
- bool loadRawResource(const char* fileName);
- jr_uncompressed_ptr getImageHandle();
-
-private:
- std::unique_ptr<uint8_t[]> mLumaData;
- std::unique_ptr<uint8_t[]> mChromaData;
- jpegr_uncompressed_struct mImg;
- UhdrInputFormat mFormat;
- bool mIsChromaContiguous;
-};
-
-/**
- * Wrapper class for compressed resource
- * Sample usage:
- * UhdrCompressedStructWrapper jpgImg(width, height);
- * rawImg.allocateMemory();
- */
-class UhdrCompressedStructWrapper {
-public:
- UhdrCompressedStructWrapper(uint32_t width, uint32_t height);
- ~UhdrCompressedStructWrapper() = default;
-
- bool allocateMemory();
- jr_compressed_ptr getImageHandle();
-
-private:
- std::unique_ptr<uint8_t[]> mData;
- jpegr_compressed_struct mImg{};
- uint32_t mWidth;
- uint32_t mHeight;
-};
-
-UhdrUnCompressedStructWrapper::UhdrUnCompressedStructWrapper(uint32_t width, uint32_t height,
- UhdrInputFormat format) {
- mImg.data = nullptr;
- mImg.width = width;
- mImg.height = height;
- mImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- mImg.chroma_data = nullptr;
- mImg.luma_stride = 0;
- mImg.chroma_stride = 0;
- mFormat = format;
- mIsChromaContiguous = true;
-}
-
-bool UhdrUnCompressedStructWrapper::setChromaMode(bool isChromaContiguous) {
- if (mLumaData.get() != nullptr) {
- std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
- return false;
- }
- mIsChromaContiguous = isChromaContiguous;
- return true;
-}
-
-bool UhdrUnCompressedStructWrapper::setImageStride(int lumaStride, int chromaStride) {
- if (mLumaData.get() != nullptr) {
- std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
- return false;
- }
- if (lumaStride != 0) {
- if (lumaStride < mImg.width) {
- std::cerr << "Bad luma stride received" << std::endl;
- return false;
- }
- mImg.luma_stride = lumaStride;
- }
- if (chromaStride != 0) {
- if (mFormat == YCbCr_p010 && chromaStride < mImg.width) {
- std::cerr << "Bad chroma stride received for format YCbCrP010" << std::endl;
- return false;
- }
- if (mFormat == YCbCr_420 && chromaStride < (mImg.width >> 1)) {
- std::cerr << "Bad chroma stride received for format YCbCr420" << std::endl;
- return false;
- }
- mImg.chroma_stride = chromaStride;
- }
- return true;
-}
-
-bool UhdrUnCompressedStructWrapper::setImageColorGamut(ultrahdr_color_gamut colorGamut) {
- if (mLumaData.get() != nullptr) {
- std::cerr << "Object has sailed, no further modifications are allowed" << std::endl;
- return false;
- }
- mImg.colorGamut = colorGamut;
- return true;
-}
-
-bool UhdrUnCompressedStructWrapper::allocateMemory() {
- if (mImg.width == 0 || (mImg.width % 2 != 0) || mImg.height == 0 || (mImg.height % 2 != 0) ||
- (mFormat != YCbCr_p010 && mFormat != YCbCr_420)) {
- std::cerr << "Object in bad state, mem alloc failed" << std::endl;
- return false;
- }
- int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
- int lumaSize = lumaStride * mImg.height * (mFormat == YCbCr_p010 ? 2 : 1);
- int chromaSize = (mImg.height >> 1) * (mFormat == YCbCr_p010 ? 2 : 1);
- if (mIsChromaContiguous) {
- chromaSize *= lumaStride;
- } else {
- if (mImg.chroma_stride == 0) {
- std::cerr << "Object in bad state, mem alloc failed" << std::endl;
- return false;
- }
- if (mFormat == YCbCr_p010) {
- chromaSize *= mImg.chroma_stride;
- } else {
- chromaSize *= (mImg.chroma_stride * 2);
- }
- }
- if (mIsChromaContiguous) {
- mLumaData = std::make_unique<uint8_t[]>(lumaSize + chromaSize);
- mImg.data = mLumaData.get();
- mImg.chroma_data = nullptr;
- } else {
- mLumaData = std::make_unique<uint8_t[]>(lumaSize);
- mImg.data = mLumaData.get();
- mChromaData = std::make_unique<uint8_t[]>(chromaSize);
- mImg.chroma_data = mChromaData.get();
- }
- return true;
-}
-
-bool UhdrUnCompressedStructWrapper::loadRawResource(const char* fileName) {
- if (!mImg.data) {
- std::cerr << "memory is not allocated, read not possible" << std::endl;
- return false;
- }
- std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
- if (ifd.good()) {
- int bpp = mFormat == YCbCr_p010 ? 2 : 1;
- int size = ifd.tellg();
- int length = mImg.width * mImg.height * bpp * 3 / 2; // 2x2 subsampling
- if (size < length) {
- std::cerr << "requested to read " << length << " bytes from file : " << fileName
- << ", file contains only " << length << " bytes" << std::endl;
- return false;
- }
- ifd.seekg(0, std::ios::beg);
- int lumaStride = mImg.luma_stride == 0 ? mImg.width : mImg.luma_stride;
- char* mem = static_cast<char*>(mImg.data);
- for (int i = 0; i < mImg.height; i++) {
- ifd.read(mem, mImg.width * bpp);
- mem += lumaStride * bpp;
- }
- if (!mIsChromaContiguous) {
- mem = static_cast<char*>(mImg.chroma_data);
- }
- int chromaStride;
- if (mIsChromaContiguous) {
- chromaStride = mFormat == YCbCr_p010 ? lumaStride : lumaStride / 2;
- } else {
- if (mFormat == YCbCr_p010) {
- chromaStride = mImg.chroma_stride == 0 ? lumaStride : mImg.chroma_stride;
- } else {
- chromaStride = mImg.chroma_stride == 0 ? (lumaStride / 2) : mImg.chroma_stride;
- }
- }
- if (mFormat == YCbCr_p010) {
- for (int i = 0; i < mImg.height / 2; i++) {
- ifd.read(mem, mImg.width * 2);
- mem += chromaStride * 2;
- }
- } else {
- for (int i = 0; i < mImg.height / 2; i++) {
- ifd.read(mem, (mImg.width / 2));
- mem += chromaStride;
- }
- for (int i = 0; i < mImg.height / 2; i++) {
- ifd.read(mem, (mImg.width / 2));
- mem += chromaStride;
- }
- }
- return true;
- }
- std::cerr << "unable to open file : " << fileName << std::endl;
- return false;
-}
-
-jr_uncompressed_ptr UhdrUnCompressedStructWrapper::getImageHandle() {
- return &mImg;
-}
-
-UhdrCompressedStructWrapper::UhdrCompressedStructWrapper(uint32_t width, uint32_t height) {
- mWidth = width;
- mHeight = height;
-}
-
-bool UhdrCompressedStructWrapper::allocateMemory() {
- if (mWidth == 0 || (mWidth % 2 != 0) || mHeight == 0 || (mHeight % 2 != 0)) {
- std::cerr << "Object in bad state, mem alloc failed" << std::endl;
- return false;
- }
- int maxLength = std::max(8 * 1024 /* min size 8kb */, (int)(mWidth * mHeight * 3 * 2));
- mData = std::make_unique<uint8_t[]>(maxLength);
- mImg.data = mData.get();
- mImg.length = 0;
- mImg.maxLength = maxLength;
- return true;
-}
-
-jr_compressed_ptr UhdrCompressedStructWrapper::getImageHandle() {
- return &mImg;
-}
-
-static bool writeFile(const char* filename, void*& result, int length) {
- std::ofstream ofd(filename, std::ios::binary);
- if (ofd.is_open()) {
- ofd.write(static_cast<char*>(result), length);
- return true;
- }
- std::cerr << "unable to write to file : " << filename << std::endl;
- return false;
-}
-
-static bool readFile(const char* fileName, void*& result, int maxLength, int& length) {
- std::ifstream ifd(fileName, std::ios::binary | std::ios::ate);
- if (ifd.good()) {
- length = ifd.tellg();
- if (length > maxLength) {
- std::cerr << "not enough space to read file" << std::endl;
- return false;
- }
- ifd.seekg(0, std::ios::beg);
- ifd.read(static_cast<char*>(result), length);
- return true;
- }
- std::cerr << "unable to read file : " << fileName << std::endl;
- return false;
-}
-
-void decodeJpegRImg(jr_compressed_ptr img, [[maybe_unused]] const char* outFileName) {
- std::vector<uint8_t> iccData(0);
- std::vector<uint8_t> exifData(0);
- jpegr_info_struct info{0, 0, &iccData, &exifData};
- JpegR jpegHdr;
- ASSERT_EQ(OK, jpegHdr.getJPEGRInfo(img, &info));
- ASSERT_EQ(kImageWidth, info.width);
- ASSERT_EQ(kImageHeight, info.height);
- size_t outSize = info.width * info.height * 8;
- std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
- jpegr_uncompressed_struct destImage{};
- destImage.data = data.get();
- ASSERT_EQ(OK, jpegHdr.decodeJPEGR(img, &destImage));
- ASSERT_EQ(kImageWidth, destImage.width);
- ASSERT_EQ(kImageHeight, destImage.height);
-#ifdef DUMP_OUTPUT
- if (!writeFile(outFileName, destImage.data, outSize)) {
- std::cerr << "unable to write output file" << std::endl;
- }
-#endif
-}
-
-// ============================================================================
-// Unit Tests
-// ============================================================================
-
-// Test Encode API-0 invalid arguments
-TEST(JpegRTest, EncodeAPI0WithInvalidArgs) {
- JpegR uHdrLib;
-
- UhdrCompressedStructWrapper jpgImg(16, 16);
- ASSERT_TRUE(jpgImg.allocateMemory());
-
- // test quality factor and transfer function
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), -1, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), 101, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- }
-
- // test dest
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
- nullptr),
- OK)
- << "fail, API allows nullptr dest";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr dest";
- }
-
- // test p010 input
- {
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
-
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
- }
-
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED));
- ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg2.setImageColorGamut(
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1)));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
- }
-
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
-
- rawImgP010->width = kWidth - 1;
- rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = 0;
- rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth + 64;
- rawImgP010->chroma_data = rawImgP010->data;
- rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride";
- }
-}
-
-/* Test Encode API-1 invalid arguments */
-TEST(JpegRTest, EncodeAPI1WithInvalidArgs) {
- JpegR uHdrLib;
-
- UhdrCompressedStructWrapper jpgImg(16, 16);
- ASSERT_TRUE(jpgImg.allocateMemory());
-
- // test quality factor and transfer function
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), -1, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), 101, nullptr),
- OK)
- << "fail, API allows bad jpeg quality factor";
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad hdr transfer function";
- }
-
- // test dest
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr, kQuality,
- nullptr),
- OK)
- << "fail, API allows nullptr dest";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr dest";
- }
-
- // test p010 input
- {
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
-
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr p010 image";
- }
-
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
- UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- auto rawImg420 = rawImg2.getImageHandle();
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth - 1;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = 0;
- rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth + 64;
- rawImgP010->chroma_data = rawImgP010->data;
- rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride";
- }
-
- // test 420 input
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr 420 image";
-
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows nullptr 420 image";
- }
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
- UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- auto rawImg420 = rawImg2.getImageHandle();
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad 420 color gamut";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad 420 color gamut";
-
- rawImg420->width = kWidth - 1;
- rawImg420->height = kHeight;
- rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height for 420";
-
- rawImg420->width = 0;
- rawImg420->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image width for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad image height for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad luma stride for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->luma_stride = 0;
- rawImg420->chroma_data = rawImgP010->data;
- rawImg420->chroma_stride = kWidth / 2 - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK)
- << "fail, API allows bad chroma stride for 420";
- }
-}
-
-/* Test Encode API-2 invalid arguments */
-TEST(JpegRTest, EncodeAPI2WithInvalidArgs) {
- JpegR uHdrLib;
-
- UhdrCompressedStructWrapper jpgImg(16, 16);
- ASSERT_TRUE(jpgImg.allocateMemory());
-
- // test quality factor and transfer function
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- }
-
- // test dest
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
- OK)
- << "fail, API allows nullptr dest";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
- }
-
- // test compressed image
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(), nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
- }
-
- // test p010 input
- {
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, rawImg2.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
-
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
- }
-
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
- UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- auto rawImg420 = rawImg2.getImageHandle();
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth - 1;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = 0;
- rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth + 64;
- rawImgP010->chroma_data = rawImgP010->data;
- rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride";
- }
-
- // test 420 input
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr 420 image";
-
- UhdrUnCompressedStructWrapper rawImg2(16, 16, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), rawImg2.getImageHandle(),
- jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr 420 image";
- }
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
- UhdrUnCompressedStructWrapper rawImg2(kWidth, kHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg2.allocateMemory());
- auto rawImg420 = rawImg2.getImageHandle();
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad 420 color gamut";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad 420 color gamut";
-
- rawImg420->width = kWidth - 1;
- rawImg420->height = kHeight;
- rawImg420->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height for 420";
-
- rawImg420->width = 0;
- rawImg420->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride for 420";
-
- rawImg420->width = kWidth;
- rawImg420->height = kHeight;
- rawImg420->luma_stride = 0;
- rawImg420->chroma_data = rawImgP010->data;
- rawImg420->chroma_stride = kWidth / 2 - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, rawImg420, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride for 420";
- }
-}
-
-/* Test Encode API-3 invalid arguments */
-TEST(JpegRTest, EncodeAPI3WithInvalidArgs) {
- JpegR uHdrLib;
-
- UhdrCompressedStructWrapper jpgImg(16, 16);
- ASSERT_TRUE(jpgImg.allocateMemory());
-
- // test quality factor and transfer function
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_UNSPECIFIED,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(
- ultrahdr_transfer_function::ULTRAHDR_TF_MAX + 1),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- static_cast<ultrahdr_transfer_function>(-10),
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad hdr transfer function";
- }
-
- // test dest
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG, nullptr),
- OK)
- << "fail, API allows nullptr dest";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
- }
-
- // test compressed image
- {
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
-
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), nullptr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr for compressed image";
- }
-
- // test p010 input
- {
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
-
- UhdrUnCompressedStructWrapper rawImg(16, 16, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImg.getImageHandle(), jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr p010 image";
- }
-
- {
- const int kWidth = 32, kHeight = 32;
- UhdrUnCompressedStructWrapper rawImg(kWidth, kHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImg.allocateMemory());
- auto rawImgP010 = rawImg.getImageHandle();
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_UNSPECIFIED;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut =
- static_cast<ultrahdr_color_gamut>(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_MAX + 1);
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad p010 color gamut";
-
- rawImgP010->width = kWidth - 1;
- rawImgP010->height = kHeight;
- rawImgP010->colorGamut = ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight - 1;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = 0;
- rawImgP010->height = kHeight;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image width";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = 0;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad image height";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad luma stride";
-
- rawImgP010->width = kWidth;
- rawImgP010->height = kHeight;
- rawImgP010->luma_stride = kWidth + 64;
- rawImgP010->chroma_data = rawImgP010->data;
- rawImgP010->chroma_stride = kWidth - 2;
- ASSERT_NE(uHdrLib.encodeJPEGR(rawImgP010, jpgImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad chroma stride";
- }
-}
-
-/* Test Encode API-4 invalid arguments */
-TEST(JpegRTest, EncodeAPI4WithInvalidArgs) {
- UhdrCompressedStructWrapper jpgImg(16, 16);
- ASSERT_TRUE(jpgImg.allocateMemory());
- UhdrCompressedStructWrapper jpgImg2(16, 16);
- JpegR uHdrLib;
-
- // test dest
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr, nullptr),
- OK)
- << "fail, API allows nullptr dest";
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), nullptr,
- jpgImg2.getImageHandle()),
- OK)
- << "fail, API allows nullptr dest";
-
- // test primary image
- ASSERT_NE(uHdrLib.encodeJPEGR(nullptr, jpgImg.getImageHandle(), nullptr, jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr primary image";
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg2.getImageHandle(), jpgImg.getImageHandle(), nullptr,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr primary image";
-
- // test gain map
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), nullptr, nullptr, jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr gain map image";
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg2.getImageHandle(), nullptr,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows nullptr gain map image";
-
- // test metadata
- ultrahdr_metadata_struct good_metadata;
- good_metadata.version = "1.0";
- good_metadata.minContentBoost = 1.0f;
- good_metadata.maxContentBoost = 2.0f;
- good_metadata.gamma = 1.0f;
- good_metadata.offsetSdr = 0.0f;
- good_metadata.offsetHdr = 0.0f;
- good_metadata.hdrCapacityMin = 1.0f;
- good_metadata.hdrCapacityMax = 2.0f;
-
- ultrahdr_metadata_struct metadata = good_metadata;
- metadata.version = "1.1";
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata version";
-
- metadata = good_metadata;
- metadata.minContentBoost = 3.0f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata content boost";
-
- metadata = good_metadata;
- metadata.gamma = -0.1f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata gamma";
-
- metadata = good_metadata;
- metadata.offsetSdr = -0.1f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata offset sdr";
-
- metadata = good_metadata;
- metadata.offsetHdr = -0.1f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata offset hdr";
-
- metadata = good_metadata;
- metadata.hdrCapacityMax = 0.5f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata hdr capacity max";
-
- metadata = good_metadata;
- metadata.hdrCapacityMin = 0.5f;
- ASSERT_NE(uHdrLib.encodeJPEGR(jpgImg.getImageHandle(), jpgImg.getImageHandle(), &metadata,
- jpgImg.getImageHandle()),
- OK)
- << "fail, API allows bad metadata hdr capacity min";
-}
-
-/* Test Decode API invalid arguments */
-TEST(JpegRTest, DecodeAPIWithInvalidArgs) {
- JpegR uHdrLib;
-
- UhdrCompressedStructWrapper jpgImg(16, 16);
- jpegr_uncompressed_struct destImage{};
- size_t outSize = 16 * 16 * 8;
- std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(outSize);
- destImage.data = data.get();
-
- // test jpegr image
- ASSERT_NE(uHdrLib.decodeJPEGR(nullptr, &destImage), OK)
- << "fail, API allows nullptr for jpegr img";
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
- << "fail, API allows nullptr for jpegr img";
- ASSERT_TRUE(jpgImg.allocateMemory());
-
- // test dest image
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), nullptr), OK)
- << "fail, API allows nullptr for dest";
- destImage.data = nullptr;
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage), OK)
- << "fail, API allows nullptr for dest";
- destImage.data = data.get();
-
- // test max display boost
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, 0.5), OK)
- << "fail, API allows invalid max display boost";
-
- // test output format
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
- static_cast<ultrahdr_output_format>(-1)),
- OK)
- << "fail, API allows invalid output format";
- ASSERT_NE(uHdrLib.decodeJPEGR(jpgImg.getImageHandle(), &destImage, FLT_MAX, nullptr,
- static_cast<ultrahdr_output_format>(ULTRAHDR_OUTPUT_MAX + 1)),
- OK)
- << "fail, API allows invalid output format";
-}
-
-TEST(JpegRTest, writeXmpThenRead) {
- ultrahdr_metadata_struct metadata_expected;
- metadata_expected.version = "1.0";
- metadata_expected.maxContentBoost = 1.25f;
- metadata_expected.minContentBoost = 0.75f;
- metadata_expected.gamma = 1.0f;
- metadata_expected.offsetSdr = 0.0f;
- metadata_expected.offsetHdr = 0.0f;
- metadata_expected.hdrCapacityMin = 1.0f;
- metadata_expected.hdrCapacityMax = metadata_expected.maxContentBoost;
- const std::string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
- const int nameSpaceLength = nameSpace.size() + 1; // need to count the null terminator
-
- std::string xmp = generateXmpForSecondaryImage(metadata_expected);
-
- std::vector<uint8_t> xmpData;
- xmpData.reserve(nameSpaceLength + xmp.size());
- xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(nameSpace.c_str()),
- reinterpret_cast<const uint8_t*>(nameSpace.c_str()) + nameSpaceLength);
- xmpData.insert(xmpData.end(), reinterpret_cast<const uint8_t*>(xmp.c_str()),
- reinterpret_cast<const uint8_t*>(xmp.c_str()) + xmp.size());
-
- ultrahdr_metadata_struct metadata_read;
- EXPECT_TRUE(getMetadataFromXMP(xmpData.data(), xmpData.size(), &metadata_read));
- EXPECT_FLOAT_EQ(metadata_expected.maxContentBoost, metadata_read.maxContentBoost);
- EXPECT_FLOAT_EQ(metadata_expected.minContentBoost, metadata_read.minContentBoost);
- EXPECT_FLOAT_EQ(metadata_expected.gamma, metadata_read.gamma);
- EXPECT_FLOAT_EQ(metadata_expected.offsetSdr, metadata_read.offsetSdr);
- EXPECT_FLOAT_EQ(metadata_expected.offsetHdr, metadata_read.offsetHdr);
- EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMin, metadata_read.hdrCapacityMin);
- EXPECT_FLOAT_EQ(metadata_expected.hdrCapacityMax, metadata_read.hdrCapacityMax);
-}
-
-class JpegRAPIEncodeAndDecodeTest
- : public ::testing::TestWithParam<std::tuple<ultrahdr_color_gamut, ultrahdr_color_gamut>> {
-public:
- JpegRAPIEncodeAndDecodeTest()
- : mP010ColorGamut(std::get<0>(GetParam())), mYuv420ColorGamut(std::get<1>(GetParam())){};
-
- const ultrahdr_color_gamut mP010ColorGamut;
- const ultrahdr_color_gamut mYuv420ColorGamut;
-};
-
-/* Test Encode API-0 and Decode */
-TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI0AndDecodeTest) {
- // reference encode
- UhdrUnCompressedStructWrapper rawImg(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg.allocateMemory());
- ASSERT_TRUE(rawImg.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg.allocateMemory());
- JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK);
- // encode with luma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, 0));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2.setImageStride(kImageWidth + 18, kImageWidth + 28));
- ASSERT_TRUE(rawImg2.setChromaMode(false));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2.setImageStride(0, kImageWidth + 34));
- ASSERT_TRUE(rawImg2.setChromaMode(false));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set but no chroma ptr
- {
- UhdrUnCompressedStructWrapper rawImg2(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2.setImageStride(kImageWidth, kImageWidth + 38));
- ASSERT_TRUE(rawImg2.allocateMemory());
- ASSERT_TRUE(rawImg2.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
-
- auto jpg1 = jpgImg.getImageHandle();
-#ifdef DUMP_OUTPUT
- if (!writeFile("encode_api0_output.jpeg", jpg1->data, jpg1->length)) {
- std::cerr << "unable to write output file" << std::endl;
- }
-#endif
-
- ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api0_output.rgb"));
-}
-
-/* Test Encode API-1 and Decode */
-TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI1AndDecodeTest) {
- UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImgP010.allocateMemory());
- ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
- UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg420.allocateMemory());
- ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg.allocateMemory());
- JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle(), kQuality, nullptr),
- OK);
- // encode with luma stride set p010
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set p010
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set p010
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set but no chroma ptr p010
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 64, kImageWidth + 256));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma stride set 420
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 14, 0));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set 420
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 46, kImageWidth / 2 + 34));
- ASSERT_TRUE(rawImg2420.setChromaMode(false));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set 420
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth / 2 + 38));
- ASSERT_TRUE(rawImg2420.setChromaMode(false));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set but no chroma ptr 420
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 26, kImageWidth / 2 + 44));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(),
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle(), kQuality, nullptr),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
-
- auto jpg1 = jpgImg.getImageHandle();
-
-#ifdef DUMP_OUTPUT
- if (!writeFile("encode_api1_output.jpeg", jpg1->data, jpg1->length)) {
- std::cerr << "unable to write output file" << std::endl;
- }
-#endif
-
- ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api1_output.rgb"));
-}
-
-/* Test Encode API-2 and Decode */
-TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI2AndDecodeTest) {
- UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImgP010.allocateMemory());
- ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
- UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg420.allocateMemory());
- ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg.allocateMemory());
- UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgSdr.allocateMemory());
- auto sdr = jpgSdr.getImageHandle();
- ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
- JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK);
- // encode with luma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), rawImg420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, 0));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(kImageWidth + 128, kImageWidth + 256));
- ASSERT_TRUE(rawImg2420.setChromaMode(false));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg2420.setImageColorGamut(mYuv420ColorGamut));
- ASSERT_TRUE(rawImg2420.setImageStride(0, kImageWidth + 64));
- ASSERT_TRUE(rawImg2420.setChromaMode(false));
- ASSERT_TRUE(rawImg2420.allocateMemory());
- ASSERT_TRUE(rawImg2420.loadRawResource(kYCbCr420FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), rawImg2420.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
-
- auto jpg1 = jpgImg.getImageHandle();
-
-#ifdef DUMP_OUTPUT
- if (!writeFile("encode_api2_output.jpeg", jpg1->data, jpg1->length)) {
- std::cerr << "unable to write output file" << std::endl;
- }
-#endif
-
- ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api2_output.rgb"));
-}
-
-/* Test Encode API-3 and Decode */
-TEST_P(JpegRAPIEncodeAndDecodeTest, EncodeAPI3AndDecodeTest) {
- UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImgP010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImgP010.allocateMemory());
- ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg.allocateMemory());
- UhdrCompressedStructWrapper jpgSdr(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgSdr.allocateMemory());
- auto sdr = jpgSdr.getImageHandle();
- ASSERT_TRUE(readFile(kSdrJpgFileName, sdr->data, sdr->maxLength, sdr->length));
- JpegR uHdrLib;
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImgP010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg.getImageHandle()),
- OK);
- // encode with luma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, 0));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 128, kImageWidth + 256));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with chroma stride set
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(0, kImageWidth + 64));
- ASSERT_TRUE(rawImg2P010.setChromaMode(false));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
- // encode with luma and chroma stride set and no chroma ptr
- {
- UhdrUnCompressedStructWrapper rawImg2P010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImg2P010.setImageColorGamut(mP010ColorGamut));
- ASSERT_TRUE(rawImg2P010.setImageStride(kImageWidth + 32, kImageWidth + 256));
- ASSERT_TRUE(rawImg2P010.allocateMemory());
- ASSERT_TRUE(rawImg2P010.loadRawResource(kYCbCrP010FileName));
- UhdrCompressedStructWrapper jpgImg2(kImageWidth, kImageHeight);
- ASSERT_TRUE(jpgImg2.allocateMemory());
- ASSERT_EQ(uHdrLib.encodeJPEGR(rawImg2P010.getImageHandle(), sdr,
- ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- jpgImg2.getImageHandle()),
- OK);
- auto jpg1 = jpgImg.getImageHandle();
- auto jpg2 = jpgImg2.getImageHandle();
- ASSERT_EQ(jpg1->length, jpg2->length);
- ASSERT_EQ(0, memcmp(jpg1->data, jpg2->data, jpg1->length));
- }
-
- auto jpg1 = jpgImg.getImageHandle();
-
-#ifdef DUMP_OUTPUT
- if (!writeFile("encode_api3_output.jpeg", jpg1->data, jpg1->length)) {
- std::cerr << "unable to write output file" << std::endl;
- }
-#endif
-
- ASSERT_NO_FATAL_FAILURE(decodeJpegRImg(jpg1, "decode_api3_output.rgb"));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- JpegRAPIParameterizedTests, JpegRAPIEncodeAndDecodeTest,
- ::testing::Combine(::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
- ULTRAHDR_COLORGAMUT_BT2100),
- ::testing::Values(ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3,
- ULTRAHDR_COLORGAMUT_BT2100)));
-
-// ============================================================================
-// Profiling
-// ============================================================================
-
-class Profiler {
-public:
- void timerStart() { gettimeofday(&mStartingTime, nullptr); }
-
- void timerStop() { gettimeofday(&mEndingTime, nullptr); }
-
- int64_t elapsedTime() {
- struct timeval elapsedMicroseconds;
- elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
- elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
- return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
- }
-
-private:
- struct timeval mStartingTime;
- struct timeval mEndingTime;
-};
-
-class JpegRBenchmark : public JpegR {
-public:
- void BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr map);
- void BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata, jr_uncompressed_ptr dest);
-
-private:
- const int kProfileCount = 10;
-};
-
-void JpegRBenchmark::BenchmarkGenerateGainMap(jr_uncompressed_ptr yuv420Image,
- jr_uncompressed_ptr p010Image,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr map) {
- ASSERT_EQ(yuv420Image->width, p010Image->width);
- ASSERT_EQ(yuv420Image->height, p010Image->height);
- Profiler profileGenerateMap;
- profileGenerateMap.timerStart();
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK,
- generateGainMap(yuv420Image, p010Image, ultrahdr_transfer_function::ULTRAHDR_TF_HLG,
- metadata, map));
- if (i != kProfileCount - 1) delete[] static_cast<uint8_t*>(map->data);
- }
- profileGenerateMap.timerStop();
- ALOGE("Generate Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
- profileGenerateMap.elapsedTime() / (kProfileCount * 1000.f));
-}
-
-void JpegRBenchmark::BenchmarkApplyGainMap(jr_uncompressed_ptr yuv420Image, jr_uncompressed_ptr map,
- ultrahdr_metadata_ptr metadata,
- jr_uncompressed_ptr dest) {
- Profiler profileRecMap;
- profileRecMap.timerStart();
- for (auto i = 0; i < kProfileCount; i++) {
- ASSERT_EQ(OK,
- applyGainMap(yuv420Image, map, metadata, ULTRAHDR_OUTPUT_HDR_HLG,
- metadata->maxContentBoost /* displayBoost */, dest));
- }
- profileRecMap.timerStop();
- ALOGE("Apply Gain Map:- Res = %i x %i, time = %f ms", yuv420Image->width, yuv420Image->height,
- profileRecMap.elapsedTime() / (kProfileCount * 1000.f));
-}
-
-TEST(JpegRTest, ProfileGainMapFuncs) {
- UhdrUnCompressedStructWrapper rawImgP010(kImageWidth, kImageHeight, YCbCr_p010);
- ASSERT_TRUE(rawImgP010.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT2100));
- ASSERT_TRUE(rawImgP010.allocateMemory());
- ASSERT_TRUE(rawImgP010.loadRawResource(kYCbCrP010FileName));
- UhdrUnCompressedStructWrapper rawImg420(kImageWidth, kImageHeight, YCbCr_420);
- ASSERT_TRUE(rawImg420.setImageColorGamut(ultrahdr_color_gamut::ULTRAHDR_COLORGAMUT_BT709));
- ASSERT_TRUE(rawImg420.allocateMemory());
- ASSERT_TRUE(rawImg420.loadRawResource(kYCbCr420FileName));
- ultrahdr_metadata_struct metadata = {.version = "1.0"};
- jpegr_uncompressed_struct map = {.data = NULL,
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
- {
- auto rawImg = rawImgP010.getImageHandle();
- if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width;
- if (!rawImg->chroma_data) {
- uint16_t* data = reinterpret_cast<uint16_t*>(rawImg->data);
- rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height;
- rawImg->chroma_stride = rawImg->luma_stride;
- }
- }
- {
- auto rawImg = rawImg420.getImageHandle();
- if (rawImg->luma_stride == 0) rawImg->luma_stride = rawImg->width;
- if (!rawImg->chroma_data) {
- uint8_t* data = reinterpret_cast<uint8_t*>(rawImg->data);
- rawImg->chroma_data = data + rawImg->luma_stride * rawImg->height;
- rawImg->chroma_stride = rawImg->luma_stride / 2;
- }
- }
-
- JpegRBenchmark benchmark;
- ASSERT_NO_FATAL_FAILURE(benchmark.BenchmarkGenerateGainMap(rawImg420.getImageHandle(),
- rawImgP010.getImageHandle(), &metadata,
- &map));
-
- const int dstSize = kImageWidth * kImageWidth * 4;
- auto bufferDst = std::make_unique<uint8_t[]>(dstSize);
- jpegr_uncompressed_struct dest = {.data = bufferDst.get(),
- .width = 0,
- .height = 0,
- .colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED};
-
- ASSERT_NO_FATAL_FAILURE(
- benchmark.BenchmarkApplyGainMap(rawImg420.getImageHandle(), &map, &metadata, &dest));
-}
-
-} // namespace android::ultrahdr
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index af0bcff..ec7b190 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -184,6 +184,14 @@
}
}
+ // Return true if app requests to use ANGLE, but ANGLE is not loaded.
+ // Difference with the case above is on devices that don't have an ANGLE apk installed,
+ // ANGLE namespace is not set. In that case if ANGLE in system partition is not loaded,
+ // we should unload the system driver first, and then load ANGLE from system partition.
+ if (!cnx->angleLoaded && android::GraphicsEnv::getInstance().shouldUseAngle()) {
+ return true;
+ }
+
// Return true if native GLES drivers should be used and ANGLE is already loaded.
if (android::GraphicsEnv::getInstance().shouldUseNativeDriver() && cnx->angleLoaded) {
return true;
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 79f22c1..fadb1fd 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -72,6 +72,9 @@
};
GpuService::~GpuService() {
+ mGpuMem->stop();
+ mGpuWork->stop();
+
mGpuWorkAsyncInitThread->join();
mGpuMemAsyncInitThread->join();
}
@@ -97,6 +100,12 @@
mGpuStats->insertTargetStatsArray(appPackageName, driverVersionCode, stats, values, valueCount);
}
+void GpuService::addVulkanEngineName(const std::string& appPackageName,
+ const uint64_t driverVersionCode,
+ const char* engineName) {
+ mGpuStats->addVulkanEngineName(appPackageName, driverVersionCode, engineName);
+}
+
void GpuService::toggleAngleAsSystemDriver(bool enabled) {
IPCThreadState* ipc = IPCThreadState::self();
const int pid = ipc->getCallingPid();
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 141fe02..d0783df 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -61,6 +61,7 @@
return;
}
// Retry until GPU driver loaded or timeout.
+ if (mStop.load()) return;
sleep(1);
}
diff --git a/services/gpuservice/gpumem/include/gpumem/GpuMem.h b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
index 9aa74d6..16b201f 100644
--- a/services/gpuservice/gpumem/include/gpumem/GpuMem.h
+++ b/services/gpuservice/gpumem/include/gpumem/GpuMem.h
@@ -34,6 +34,7 @@
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
bool isInitialized() { return mInitialized.load(); }
+ void stop() { mStop.store(true); }
// Traverse the gpu memory total map to feed the callback function.
void traverseGpuMemTotals(const std::function<void(int64_t ts, uint32_t gpuId, uint32_t pid,
@@ -48,6 +49,10 @@
// indicate whether ebpf has been initialized
std::atomic<bool> mInitialized = false;
+
+ // whether initialization should be stopped
+ std::atomic<bool> mStop = false;
+
// bpf map for GPU memory total data
android::bpf::BpfMapRO<uint64_t, uint64_t> mGpuMemTotalMap;
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 11b636d..6d758bc 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -181,6 +181,33 @@
return insertTargetStatsArray(appPackageName, driverVersionCode, stats, &value, 1);
}
+void GpuStats::addVulkanEngineName(const std::string& appPackageName,
+ const uint64_t driverVersionCode,
+ const char* engineNameCStr) {
+ ATRACE_CALL();
+
+ const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
+ const size_t engineNameLen = std::min(strlen(engineNameCStr),
+ GpuStatsAppInfo::MAX_VULKAN_ENGINE_NAME_LENGTH);
+ const std::string engineName{engineNameCStr, engineNameLen};
+
+ std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
+
+ const auto foundApp = mAppStats.find(appStatsKey);
+ if (foundApp == mAppStats.end()) {
+ return;
+ }
+
+ // Storing in std::set<> is not efficient for serialization tasks. Use
+ // vector instead and filter out dups
+ std::vector<std::string>& engineNames = foundApp->second.vulkanEngineNames;
+ if (engineNames.size() < GpuStatsAppInfo::MAX_VULKAN_ENGINE_NAMES
+ && std::find(engineNames.cbegin(), engineNames.cend(), engineName) == engineNames.cend()) {
+ engineNames.push_back(engineName);
+ }
+}
+
void GpuStats::insertTargetStatsArray(const std::string& appPackageName,
const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
const uint64_t* values, const uint32_t valueCount) {
@@ -389,6 +416,11 @@
std::string angleDriverBytes = int64VectorToProtoByteString(
ele.second.angleDriverLoadingTime);
+ std::vector<const char*> engineNames;
+ for (const std::string &engineName : ele.second.vulkanEngineNames) {
+ engineNames.push_back(engineName.c_str());
+ }
+
android::util::addAStatsEvent(
data,
android::util::GPU_STATS_APP_INFO,
@@ -410,7 +442,8 @@
ele.second.vulkanApiVersion,
ele.second.vulkanDeviceFeaturesEnabled,
ele.second.vulkanInstanceExtensions,
- ele.second.vulkanDeviceExtensions);
+ ele.second.vulkanDeviceExtensions,
+ engineNames);
}
}
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 22c64db..961f011 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -44,6 +44,9 @@
void insertTargetStatsArray(const std::string& appPackageName,
const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
const uint64_t* values, const uint32_t valueCount);
+ // Add the engine name passed in VkApplicationInfo during CreateInstance
+ void addVulkanEngineName(const std::string& appPackageName,
+ const uint64_t driverVersionCode, const char* engineName);
// dumpsys interface
void dump(const Vector<String16>& args, std::string* result);
diff --git a/services/gpuservice/gpuwork/GpuWork.cpp b/services/gpuservice/gpuwork/GpuWork.cpp
index fd70323..1a744ab 100644
--- a/services/gpuservice/gpuwork/GpuWork.cpp
+++ b/services/gpuservice/gpuwork/GpuWork.cpp
@@ -243,6 +243,7 @@
return false;
}
// Retry until GPU driver loaded or timeout.
+ if (mStop.load()) return false;
sleep(1);
errno = 0;
}
diff --git a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
index cece999..e70da54 100644
--- a/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
+++ b/services/gpuservice/gpuwork/include/gpuwork/GpuWork.h
@@ -40,6 +40,7 @@
~GpuWork();
void initialize();
+ void stop() { mStop.store(true); }
// Dumps the GPU work information.
void dump(const Vector<String16>& args, std::string* result);
@@ -47,7 +48,7 @@
private:
// Attaches tracepoint |tracepoint_group|/|tracepoint_name| to BPF program at path
// |program_path|. The tracepoint is also enabled.
- static bool attachTracepoint(const char* program_path, const char* tracepoint_group,
+ bool attachTracepoint(const char* program_path, const char* tracepoint_group,
const char* tracepoint_name);
// Native atom puller callback registered in statsd.
@@ -80,6 +81,9 @@
// Indicates whether our eBPF components have been initialized.
std::atomic<bool> mInitialized = false;
+ // Indicates whether eBPF initialization should be stopped.
+ std::atomic<bool> mStop = false;
+
// A thread that periodically checks whether |mGpuWorkMap| is nearly full
// and, if so, clears it.
std::thread mMapClearerThread;
diff --git a/services/gpuservice/include/gpuservice/GpuService.h b/services/gpuservice/include/gpuservice/GpuService.h
index 54f8f66..3072885 100644
--- a/services/gpuservice/include/gpuservice/GpuService.h
+++ b/services/gpuservice/include/gpuservice/GpuService.h
@@ -64,6 +64,8 @@
void setUpdatableDriverPath(const std::string& driverPath) override;
std::string getUpdatableDriverPath() override;
void toggleAngleAsSystemDriver(bool enabled) override;
+ void addVulkanEngineName(const std::string& appPackageName, const uint64_t driverVersionCode,
+ const char *engineName) override;
/*
* IBinder interface
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 4ce533f..b367457 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -46,6 +46,8 @@
#define UPDATED_DRIVER_VER_CODE 1
#define UPDATED_DRIVER_BUILD_TIME 234
#define VULKAN_VERSION 345
+#define VULKAN_ENGINE_NAME_1 "testVulkanEngine1"
+#define VULKAN_ENGINE_NAME_2 "testVulkanEngine2"
#define APP_PKG_NAME_1 "testapp1"
#define APP_PKG_NAME_2 "testapp2"
#define DRIVER_LOADING_TIME_1 678
@@ -243,6 +245,8 @@
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
VULKAN_DEVICE_EXTENSION_1);
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_1);
EXPECT_TRUE(inputCommand(InputCommand::DUMP_APP).empty());
}
@@ -282,6 +286,8 @@
mGpuStats->insertTargetStats(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
VULKAN_DEVICE_EXTENSION_2);
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_1);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
@@ -302,6 +308,64 @@
expectedResult.str("");
expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
<< " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ expectedResult.str("");
+ expectedResult << "vulkanEngineNames: " << VULKAN_ENGINE_NAME_1 << ",";
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+}
+
+// Verify the vulkanEngineNames list behaves like a set and dedupes additions
+TEST_F(GpuStatsTest, vulkanEngineNamesBehavesLikeSet) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+ for (int i = 0; i < 4; i++) {
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_1);
+ }
+
+ std::stringstream wrongResult, expectedResult;
+ wrongResult << "vulkanEngineNames: " << VULKAN_ENGINE_NAME_1 << ", " <<
+ VULKAN_ENGINE_NAME_1;
+ expectedResult << "vulkanEngineNames: " << VULKAN_ENGINE_NAME_1;
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), Not(HasSubstr(wrongResult.str())));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+}
+
+TEST_F(GpuStatsTest, vulkanEngineNamesCheckEmptyEngineNameAlone) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ "");
+
+ std::stringstream expectedResult;
+ expectedResult << "vulkanEngineNames: ,";
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
+}
+
+TEST_F(GpuStatsTest, vulkanEngineNamesCheckEmptyEngineNameWithOthers) {
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
+ VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_1);
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ "");
+ mGpuStats->addVulkanEngineName(APP_PKG_NAME_1, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_2);
+
+ std::stringstream expectedResult;
+ expectedResult << "vulkanEngineNames: " << VULKAN_ENGINE_NAME_1 << ", "
+ << ", " << VULKAN_ENGINE_NAME_2;
+
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
@@ -350,6 +414,10 @@
mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION,
VULKAN_DEVICE_EXTENSION_2);
+ mGpuStats->addVulkanEngineName(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_1);
+ mGpuStats->addVulkanEngineName(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ VULKAN_ENGINE_NAME_2);
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
@@ -371,6 +439,9 @@
expectedResult.str("");
expectedResult << "vulkanDeviceExtensions: 0x" << std::hex << VULKAN_DEVICE_EXTENSION_1
<< " 0x" << std::hex << VULKAN_DEVICE_EXTENSION_2;
+ expectedResult.str("");
+ expectedResult << "vulkanEngineNames: " << VULKAN_ENGINE_NAME_1 << ", "
+ << VULKAN_ENGINE_NAME_2 << ",";
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(expectedResult.str()));
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index d244b1a..70801dc 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -56,6 +56,16 @@
host: {
sanitize: {
address: true,
+ diag: {
+ cfi: true,
+ integer_overflow: true,
+ memtag_heap: true,
+ undefined: true,
+ misc_undefined: [
+ "bounds",
+ "all",
+ ],
+ },
},
include_dirs: [
"bionic/libc/kernel/android/uapi/",
@@ -107,6 +117,7 @@
"libutils",
"libstatspull",
"libstatssocket",
+ "packagemanager_aidl-cpp",
"server_configurable_flags",
],
static_libs: [
@@ -270,5 +281,6 @@
"FrameworksServicesTests",
"CtsSecurityTestCases",
"CtsSecurityBulletinHostTestCases",
+ "monkey_test",
],
}
diff --git a/services/inputflinger/InputCommonConverter.cpp b/services/inputflinger/InputCommonConverter.cpp
index 6ccd9e7..e376734 100644
--- a/services/inputflinger/InputCommonConverter.cpp
+++ b/services/inputflinger/InputCommonConverter.cpp
@@ -20,6 +20,9 @@
namespace android {
+const static ui::Transform kIdentityTransform;
+const static std::array<uint8_t, 32> kInvalidHmac{};
+
static common::Source getSource(uint32_t source) {
static_assert(static_cast<common::Source>(AINPUT_SOURCE_UNKNOWN) == common::Source::UNKNOWN,
"SOURCE_UNKNOWN mismatch");
@@ -311,7 +314,7 @@
common::MotionEvent event;
event.deviceId = args.deviceId;
event.source = getSource(args.source);
- event.displayId = args.displayId;
+ event.displayId = args.displayId.val();
event.downTime = args.downTime;
event.eventTime = args.eventTime;
event.deviceTimestamp = 0;
@@ -337,4 +340,31 @@
return event;
}
+MotionEvent toMotionEvent(const NotifyMotionArgs& args, const ui::Transform* transform,
+ const ui::Transform* rawTransform, const std::array<uint8_t, 32>* hmac) {
+ if (transform == nullptr) transform = &kIdentityTransform;
+ if (rawTransform == nullptr) rawTransform = &kIdentityTransform;
+ if (hmac == nullptr) hmac = &kInvalidHmac;
+
+ MotionEvent event;
+ event.initialize(args.id, args.deviceId, args.source, args.displayId, *hmac, args.action,
+ args.actionButton, args.flags, args.edgeFlags, args.metaState,
+ args.buttonState, args.classification, *transform, args.xPrecision,
+ args.yPrecision, args.xCursorPosition, args.yCursorPosition, *rawTransform,
+ args.downTime, args.eventTime, args.getPointerCount(),
+ args.pointerProperties.data(), args.pointerCoords.data());
+ return event;
+}
+
+KeyEvent toKeyEvent(const NotifyKeyArgs& args, int32_t repeatCount,
+ const std::array<uint8_t, 32>* hmac) {
+ if (hmac == nullptr) hmac = &kInvalidHmac;
+
+ KeyEvent event;
+ event.initialize(args.id, args.deviceId, args.source, args.displayId, *hmac, args.action,
+ args.flags, args.keyCode, args.scanCode, args.metaState, repeatCount,
+ args.downTime, args.eventTime);
+ return event;
+}
+
} // namespace android
diff --git a/services/inputflinger/InputCommonConverter.h b/services/inputflinger/InputCommonConverter.h
index 4d3b768..0d4cbb0 100644
--- a/services/inputflinger/InputCommonConverter.h
+++ b/services/inputflinger/InputCommonConverter.h
@@ -16,16 +16,25 @@
#pragma once
+#include "InputListener.h"
+
#include <aidl/android/hardware/input/common/Axis.h>
#include <aidl/android/hardware/input/common/MotionEvent.h>
-#include "InputListener.h"
+#include <input/Input.h>
namespace android {
-/**
- * Convert from framework's NotifyMotionArgs to hidl's common::MotionEvent
- */
+/** Convert from framework's NotifyMotionArgs to hidl's common::MotionEvent. */
::aidl::android::hardware::input::common::MotionEvent notifyMotionArgsToHalMotionEvent(
const NotifyMotionArgs& args);
+/** Convert from NotifyMotionArgs to MotionEvent. */
+MotionEvent toMotionEvent(const NotifyMotionArgs&, const ui::Transform* transform = nullptr,
+ const ui::Transform* rawTransform = nullptr,
+ const std::array<uint8_t, 32>* hmac = nullptr);
+
+/** Convert from NotifyKeyArgs to KeyEvent. */
+KeyEvent toKeyEvent(const NotifyKeyArgs&, int32_t repeatCount = 0,
+ const std::array<uint8_t, 32>* hmac = nullptr);
+
} // namespace android
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 1ada5e5..8e73ce5 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -32,7 +32,7 @@
event.eventTime = args.eventTime;
event.deviceId = args.deviceId;
event.source = static_cast<Source>(args.source);
- event.displayId = args.displayId;
+ event.displayId = args.displayId.val();
event.policyFlags = args.policyFlags;
event.action = static_cast<KeyEventAction>(args.action);
event.flags = args.flags;
diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp
index 6c31442..5fbdc84 100644
--- a/services/inputflinger/InputFilterCallbacks.cpp
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -19,9 +19,10 @@
#include "InputFilterCallbacks.h"
#include <aidl/com/android/server/inputflinger/BnInputThread.h>
#include <android/binder_auto_utils.h>
+#include <utils/Looper.h>
#include <utils/StrongPointer.h>
-#include <utils/Thread.h>
#include <functional>
+#include "InputThread.h"
namespace android {
@@ -29,45 +30,46 @@
NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) {
return NotifyKeyArgs(event.id, event.eventTime, event.readTime, event.deviceId,
- static_cast<uint32_t>(event.source), event.displayId, event.policyFlags,
- static_cast<int32_t>(event.action), event.flags, event.keyCode,
- event.scanCode, event.metaState, event.downTime);
+ static_cast<uint32_t>(event.source), ui::LogicalDisplayId{event.displayId},
+ event.policyFlags, static_cast<int32_t>(event.action), event.flags,
+ event.keyCode, event.scanCode, event.metaState, event.downTime);
}
namespace {
using namespace aidl::com::android::server::inputflinger;
-class InputFilterThreadImpl : public Thread {
-public:
- explicit InputFilterThreadImpl(std::function<void()> loop)
- : Thread(/*canCallJava=*/true), mThreadLoop(loop) {}
-
- ~InputFilterThreadImpl() {}
-
-private:
- std::function<void()> mThreadLoop;
-
- bool threadLoop() override {
- mThreadLoop();
- return true;
- }
-};
-
class InputFilterThread : public BnInputThread {
public:
InputFilterThread(std::shared_ptr<IInputThreadCallback> callback) : mCallback(callback) {
- mThread = sp<InputFilterThreadImpl>::make([this]() { loopOnce(); });
- mThread->run("InputFilterThread", ANDROID_PRIORITY_URGENT_DISPLAY);
+ mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
+ mThread = std::make_unique<InputThread>(
+ "InputFilter", [this]() { loopOnce(); }, [this]() { mLooper->wake(); });
}
ndk::ScopedAStatus finish() override {
- mThread->requestExit();
+ if (mThread && mThread->isCallingThread()) {
+ ALOGE("InputFilterThread cannot be stopped on itself!");
+ return ndk::ScopedAStatus::fromStatus(INVALID_OPERATION);
+ }
+ mThread.reset();
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ndk::ScopedAStatus sleepUntil(nsecs_t when) override {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mLooper->pollOnce(toMillisecondTimeoutDelay(now, when));
+ return ndk::ScopedAStatus::ok();
+ }
+
+ ndk::ScopedAStatus wake() override {
+ mLooper->wake();
return ndk::ScopedAStatus::ok();
}
private:
- sp<Thread> mThread;
+ sp<Looper> mLooper;
+ std::unique_ptr<InputThread> mThread;
std::shared_ptr<IInputThreadCallback> mCallback;
void loopOnce() { LOG_ALWAYS_FATAL_IF(!mCallback->loopOnce().isOk()); }
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index ae066c0..41e5247 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -41,7 +41,6 @@
const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
-const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
int32_t exceptionCodeFromStatusT(status_t status) {
@@ -152,12 +151,10 @@
mTracingStages.emplace_back(
std::make_unique<TracedInputListener>("InputProcessor", *mProcessor));
- if (ENABLE_POINTER_CHOREOGRAPHER) {
- mChoreographer =
- std::make_unique<PointerChoreographer>(*mTracingStages.back(), choreographerPolicy);
- mTracingStages.emplace_back(
- std::make_unique<TracedInputListener>("PointerChoreographer", *mChoreographer));
- }
+ mChoreographer =
+ std::make_unique<PointerChoreographer>(*mTracingStages.back(), choreographerPolicy);
+ mTracingStages.emplace_back(
+ std::make_unique<TracedInputListener>("PointerChoreographer", *mChoreographer));
mBlocker = std::make_unique<UnwantedInteractionBlocker>(*mTracingStages.back());
mTracingStages.emplace_back(
@@ -245,10 +242,8 @@
dump += '\n';
mBlocker->dump(dump);
dump += '\n';
- if (ENABLE_POINTER_CHOREOGRAPHER) {
- mChoreographer->dump(dump);
- dump += '\n';
- }
+ mChoreographer->dump(dump);
+ dump += '\n';
mProcessor->dump(dump);
dump += '\n';
if (ENABLE_INPUT_DEVICE_USAGE_METRICS) {
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 4ec5b89..29b487f 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -68,7 +68,7 @@
if (currentViewport.type == type) {
if (!result ||
(type == ViewportType::INTERNAL &&
- currentViewport.displayId == ADISPLAY_ID_DEFAULT)) {
+ currentViewport.displayId == ui::ADISPLAY_ID_DEFAULT)) {
result = std::make_optional(currentViewport);
}
count++;
@@ -93,7 +93,7 @@
}
std::optional<DisplayViewport> InputReaderConfiguration::getDisplayViewportById(
- int32_t displayId) const {
+ ui::LogicalDisplayId displayId) const {
for (const DisplayViewport& currentViewport : mDisplays) {
if (currentViewport.displayId == displayId) {
return std::make_optional(currentViewport);
diff --git a/services/inputflinger/NotifyArgs.cpp b/services/inputflinger/NotifyArgs.cpp
index de836e9..19a4f26 100644
--- a/services/inputflinger/NotifyArgs.cpp
+++ b/services/inputflinger/NotifyArgs.cpp
@@ -43,7 +43,7 @@
// --- NotifyKeyArgs ---
NotifyKeyArgs::NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
- uint32_t source, int32_t displayId, uint32_t policyFlags,
+ uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
int32_t metaState, nsecs_t downTime)
: id(id),
@@ -64,7 +64,7 @@
NotifyMotionArgs::NotifyMotionArgs(
int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId, uint32_t source,
- int32_t displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
+ ui::LogicalDisplayId displayId, uint32_t policyFlags, int32_t action, int32_t actionButton,
int32_t flags, int32_t metaState, int32_t buttonState, MotionClassification classification,
int32_t edgeFlags, uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp
index 1aa1077..d6c6f93 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -17,7 +17,12 @@
#define LOG_TAG "PointerChoreographer"
#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#if defined(__ANDROID__)
+#include <gui/SurfaceComposerClient.h>
+#endif
#include <input/PrintTools.h>
+#include <unordered_set>
#include "PointerChoreographer.h"
@@ -25,6 +30,10 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+static const bool HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS =
+ input_flags::hide_pointer_indicators_for_secure_windows();
+
namespace {
bool isFromMouse(const NotifyMotionArgs& args) {
@@ -58,8 +67,9 @@
!isFromSource(sources, AINPUT_SOURCE_STYLUS));
}
-inline void notifyPointerDisplayChange(std::optional<std::tuple<int32_t, FloatPoint>> change,
- PointerChoreographerPolicyInterface& policy) {
+inline void notifyPointerDisplayChange(
+ std::optional<std::tuple<ui::LogicalDisplayId, FloatPoint>> change,
+ PointerChoreographerPolicyInterface& policy) {
if (!change) {
return;
}
@@ -91,11 +101,19 @@
}),
mNextListener(listener),
mPolicy(policy),
- mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
- mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
+ mDefaultMouseDisplayId(ui::ADISPLAY_ID_DEFAULT),
+ mNotifiedPointerDisplayId(ui::ADISPLAY_ID_NONE),
mShowTouchesEnabled(false),
mStylusPointerIconEnabled(false) {}
+PointerChoreographer::~PointerChoreographer() {
+ std::scoped_lock _l(mLock);
+ if (mWindowInfoListener == nullptr) {
+ return;
+ }
+ mWindowInfoListener->onPointerChoreographerDestroyed();
+}
+
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
PointerDisplayChange pointerDisplayChange;
@@ -147,6 +165,7 @@
<< args.dump();
}
+ mMouseDevices.emplace(args.deviceId);
auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
NotifyMotionArgs newArgs(args);
newArgs.displayId = displayId;
@@ -178,6 +197,7 @@
}
NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
+ mMouseDevices.emplace(args.deviceId);
auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
NotifyMotionArgs newArgs(args);
@@ -216,7 +236,7 @@
}
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
- if (args.displayId == ADISPLAY_ID_NONE) {
+ if (args.displayId == ui::ADISPLAY_ID_NONE) {
return;
}
@@ -229,6 +249,7 @@
auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
getMouseControllerConstructor(
args.displayId));
+ // TODO (b/325252005): Add handing for drawing tablets mouse pointer controller
PointerControllerInterface& pc = *it->second;
@@ -252,7 +273,7 @@
* For touch events, we do not need to populate the cursor position.
*/
void PointerChoreographer::processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) {
- if (args.displayId == ADISPLAY_ID_NONE) {
+ if (!args.displayId.isValid()) {
return;
}
@@ -266,7 +287,11 @@
}
// Get the touch pointer controller for the device, or create one if it doesn't exist.
- auto [it, _] = mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+ auto [it, controllerAdded] =
+ mTouchPointersByDevice.try_emplace(args.deviceId, mTouchControllerConstructor);
+ if (controllerAdded) {
+ onControllerAddedOrRemoved();
+ }
PointerControllerInterface& pc = *it->second;
@@ -291,7 +316,7 @@
}
void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
- if (args.displayId == ADISPLAY_ID_NONE) {
+ if (!args.displayId.isValid()) {
return;
}
@@ -304,6 +329,7 @@
auto [it, _] =
mStylusPointersByDevice.try_emplace(args.deviceId,
getStylusControllerConstructor(args.displayId));
+ // TODO (b/325252005): Add handing for stylus pointer controller
PointerControllerInterface& pc = *it->second;
@@ -343,6 +369,31 @@
mTouchPointersByDevice.erase(args.deviceId);
mStylusPointersByDevice.erase(args.deviceId);
mDrawingTabletPointersByDevice.erase(args.deviceId);
+ onControllerAddedOrRemoved();
+}
+
+void PointerChoreographer::onControllerAddedOrRemoved() {
+ if (!HIDE_TOUCH_INDICATORS_FOR_SECURE_WINDOWS) {
+ return;
+ }
+ bool requireListener = !mTouchPointersByDevice.empty();
+ // TODO (b/325252005): Update for other types of pointer controllers
+
+ if (requireListener && mWindowInfoListener == nullptr) {
+ mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
+ auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
+ std::vector<android::gui::DisplayInfo>{});
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener,
+ &initialInfo);
+#endif
+ onWindowInfosChangedLocked(initialInfo.first);
+ } else if (!requireListener && mWindowInfoListener != nullptr) {
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(mWindowInfoListener);
+#endif
+ mWindowInfoListener = nullptr;
+ }
}
void PointerChoreographer::notifyPointerCaptureChanged(
@@ -356,6 +407,12 @@
mNextListener.notify(args);
}
+void PointerChoreographer::onWindowInfosChanged(
+ const std::vector<android::gui::WindowInfo>& windowInfos) {
+ std::scoped_lock _l(mLock);
+ onWindowInfosChangedLocked(windowInfos);
+}
+
void PointerChoreographer::dump(std::string& dump) {
std::scoped_lock _l(mLock);
@@ -367,7 +424,7 @@
dump += INDENT "MousePointerControllers:\n";
for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) {
std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT);
- dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump;
+ dump += INDENT + displayId.toString() + " : " + pointerControllerDump;
}
dump += INDENT "TouchPointerControllers:\n";
for (const auto& [deviceId, touchPointerController] : mTouchPointersByDevice) {
@@ -387,7 +444,8 @@
dump += "\n";
}
-const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const {
+const DisplayViewport* PointerChoreographer::findViewportByIdLocked(
+ ui::LogicalDisplayId displayId) const {
for (auto& viewport : mViewports) {
if (viewport.displayId == displayId) {
return &viewport;
@@ -396,17 +454,21 @@
return nullptr;
}
-int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const {
- return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId;
+ui::LogicalDisplayId PointerChoreographer::getTargetMouseDisplayLocked(
+ ui::LogicalDisplayId associatedDisplayId) const {
+ return associatedDisplayId.isValid() ? associatedDisplayId : mDefaultMouseDisplayId;
}
-std::pair<int32_t, PointerControllerInterface&> PointerChoreographer::ensureMouseControllerLocked(
- int32_t associatedDisplayId) {
- const int32_t displayId = getTargetMouseDisplayLocked(associatedDisplayId);
+std::pair<ui::LogicalDisplayId, PointerControllerInterface&>
+PointerChoreographer::ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) {
+ const ui::LogicalDisplayId displayId = getTargetMouseDisplayLocked(associatedDisplayId);
auto it = mMousePointersByDisplay.find(displayId);
- LOG_ALWAYS_FATAL_IF(it == mMousePointersByDisplay.end(),
- "There is no mouse controller created for display %d", displayId);
+ if (it == mMousePointersByDisplay.end()) {
+ it = mMousePointersByDisplay.emplace(displayId, getMouseControllerConstructor(displayId))
+ .first;
+ // TODO (b/325252005): Add handing for mouse pointer controller
+ }
return {displayId, *it->second};
}
@@ -417,12 +479,12 @@
return it != mInputDeviceInfos.end() ? &(*it) : nullptr;
}
-bool PointerChoreographer::canUnfadeOnDisplay(int32_t displayId) {
+bool PointerChoreographer::canUnfadeOnDisplay(ui::LogicalDisplayId displayId) {
return mDisplaysWithPointersHidden.find(displayId) == mDisplaysWithPointersHidden.end();
}
PointerChoreographer::PointerDisplayChange PointerChoreographer::updatePointerControllersLocked() {
- std::set<int32_t /*displayId*/> mouseDisplaysToKeep;
+ std::set<ui::LogicalDisplayId /*displayId*/> mouseDisplaysToKeep;
std::set<DeviceId> touchDevicesToKeep;
std::set<DeviceId> stylusDevicesToKeep;
std::set<DeviceId> drawingTabletDevicesToKeep;
@@ -430,30 +492,40 @@
// Mark the displayIds or deviceIds of PointerControllers currently needed, and create
// new PointerControllers if necessary.
for (const auto& info : mInputDeviceInfos) {
+ if (!info.isEnabled()) {
+ // If device is disabled, we should not keep it, and should not show pointer for
+ // disabled mouse device.
+ continue;
+ }
const uint32_t sources = info.getSources();
- if (isMouseOrTouchpad(sources)) {
- const int32_t displayId = getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
+ const bool isKnownMouse = mMouseDevices.count(info.getId()) != 0;
+
+ if (isMouseOrTouchpad(sources) || isKnownMouse) {
+ const ui::LogicalDisplayId displayId =
+ getTargetMouseDisplayLocked(info.getAssociatedDisplayId());
mouseDisplaysToKeep.insert(displayId);
// For mice, show the cursor immediately when the device is first connected or
// when it moves to a new display.
auto [mousePointerIt, isNewMousePointer] =
mMousePointersByDisplay.try_emplace(displayId,
getMouseControllerConstructor(displayId));
- auto [_, isNewMouseDevice] = mMouseDevices.emplace(info.getId());
- if ((isNewMouseDevice || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
+ // TODO (b/325252005): Add handing for mouse pointer controller
+
+ mMouseDevices.emplace(info.getId());
+ if ((!isKnownMouse || isNewMousePointer) && canUnfadeOnDisplay(displayId)) {
mousePointerIt->second->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
}
if (isFromSource(sources, AINPUT_SOURCE_TOUCHSCREEN) && mShowTouchesEnabled &&
- info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ info.getAssociatedDisplayId().isValid()) {
touchDevicesToKeep.insert(info.getId());
}
if (isFromSource(sources, AINPUT_SOURCE_STYLUS) && mStylusPointerIconEnabled &&
- info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ info.getAssociatedDisplayId().isValid()) {
stylusDevicesToKeep.insert(info.getId());
}
if (isFromSource(sources, AINPUT_SOURCE_STYLUS | AINPUT_SOURCE_MOUSE) &&
- info.getAssociatedDisplayId() != ADISPLAY_ID_NONE) {
+ info.getAssociatedDisplayId().isValid()) {
drawingTabletDevicesToKeep.insert(info.getId());
}
}
@@ -477,13 +549,15 @@
mInputDeviceInfos.end();
});
+ onControllerAddedOrRemoved();
+
// Check if we need to notify the policy if there's a change on the pointer display ID.
return calculatePointerDisplayChangeToNotify();
}
PointerChoreographer::PointerDisplayChange
PointerChoreographer::calculatePointerDisplayChangeToNotify() {
- int32_t displayIdToNotify = ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId displayIdToNotify = ui::ADISPLAY_ID_NONE;
FloatPoint cursorPosition = {0, 0};
if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
it != mMousePointersByDisplay.end()) {
@@ -501,7 +575,7 @@
return {{displayIdToNotify, cursorPosition}};
}
-void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
+void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
@@ -520,7 +594,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
for (const auto& viewport : viewports) {
- const int32_t displayId = viewport.displayId;
+ const ui::LogicalDisplayId displayId = viewport.displayId;
if (const auto it = mMousePointersByDisplay.find(displayId);
it != mMousePointersByDisplay.end()) {
it->second->setDisplayViewport(viewport);
@@ -546,18 +620,18 @@
}
std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice(
- int32_t associatedDisplayId) {
+ ui::LogicalDisplayId associatedDisplayId) {
std::scoped_lock _l(mLock);
- const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
+ const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId);
if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) {
return *viewport;
}
return std::nullopt;
}
-FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) {
+FloatPoint PointerChoreographer::getMouseCursorPosition(ui::LogicalDisplayId displayId) {
std::scoped_lock _l(mLock);
- const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
+ const ui::LogicalDisplayId resolvedDisplayId = getTargetMouseDisplayLocked(displayId);
if (auto it = mMousePointersByDisplay.find(resolvedDisplayId);
it != mMousePointersByDisplay.end()) {
return it->second->getPosition();
@@ -596,8 +670,8 @@
}
bool PointerChoreographer::setPointerIcon(
- std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon, int32_t displayId,
- DeviceId deviceId) {
+ std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
+ ui::LogicalDisplayId displayId, DeviceId deviceId) {
std::scoped_lock _l(mLock);
if (deviceId < 0) {
LOG(WARNING) << "Invalid device id " << deviceId << ". Cannot set pointer icon.";
@@ -641,7 +715,32 @@
return false;
}
-void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
+void PointerChoreographer::onWindowInfosChangedLocked(
+ const std::vector<android::gui::WindowInfo>& windowInfos) {
+ // Mark all spot controllers secure on displays containing secure windows and
+ // remove secure flag from others if required
+ std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
+ std::unordered_set<ui::LogicalDisplayId> allDisplayIds;
+ for (const auto& windowInfo : windowInfos) {
+ allDisplayIds.insert(windowInfo.displayId);
+ if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
+ privacySensitiveDisplays.insert(windowInfo.displayId);
+ }
+ }
+
+ for (auto& it : mTouchPointersByDevice) {
+ auto& pc = it.second;
+ for (ui::LogicalDisplayId displayId : allDisplayIds) {
+ pc->setSkipScreenshot(displayId,
+ privacySensitiveDisplays.find(displayId) !=
+ privacySensitiveDisplays.end());
+ }
+ }
+ // TODO (b/325252005): update skip screenshot flag for other types of pointer controllers
+}
+
+void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
std::scoped_lock lock(mLock);
if (visible) {
mDisplaysWithPointersHidden.erase(displayId);
@@ -664,7 +763,7 @@
}
PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor(
- int32_t displayId) {
+ ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
[this, displayId]() REQUIRES(mLock) {
auto pc = mPolicy.createPointerController(
@@ -678,7 +777,7 @@
}
PointerChoreographer::ControllerConstructor PointerChoreographer::getStylusControllerConstructor(
- int32_t displayId) {
+ ui::LogicalDisplayId displayId) {
std::function<std::shared_ptr<PointerControllerInterface>()> ctor =
[this, displayId]() REQUIRES(mLock) {
auto pc = mPolicy.createPointerController(
@@ -691,4 +790,18 @@
return ConstructorDelegate(std::move(ctor));
}
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
+ const gui::WindowInfosUpdate& windowInfosUpdate) {
+ std::scoped_lock _l(mListenerLock);
+ if (mPointerChoreographer != nullptr) {
+ mPointerChoreographer->onWindowInfosChanged(windowInfosUpdate.windowInfos);
+ }
+}
+
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::
+ onPointerChoreographerDestroyed() {
+ std::scoped_lock _l(mListenerLock);
+ mPointerChoreographer = nullptr;
+}
+
} // namespace android
diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h
index a3c210e..fda5f52 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -21,6 +21,7 @@
#include "PointerChoreographerPolicyInterface.h"
#include <android-base/thread_annotations.h>
+#include <gui/WindowInfosListener.h>
#include <type_traits>
namespace android {
@@ -53,11 +54,11 @@
* Set the display that pointers, like the mouse cursor and drawing tablets,
* should be drawn on.
*/
- virtual void setDefaultMouseDisplayId(int32_t displayId) = 0;
+ virtual void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) = 0;
virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0;
virtual std::optional<DisplayViewport> getViewportForPointerDevice(
- int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
- virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0;
+ ui::LogicalDisplayId associatedDisplayId = ui::ADISPLAY_ID_NONE) = 0;
+ virtual FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId) = 0;
virtual void setShowTouchesEnabled(bool enabled) = 0;
virtual void setStylusPointerIconEnabled(bool enabled) = 0;
/**
@@ -66,12 +67,12 @@
* Returns true if the icon was changed successfully, false otherwise.
*/
virtual bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
- int32_t displayId, DeviceId deviceId) = 0;
+ ui::LogicalDisplayId displayId, DeviceId deviceId) = 0;
/**
* Set whether pointer icons for mice, touchpads, and styluses should be visible on the
* given display.
*/
- virtual void setPointerIconVisibility(int32_t displayId, bool visible) = 0;
+ virtual void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) = 0;
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
@@ -83,18 +84,18 @@
public:
explicit PointerChoreographer(InputListenerInterface& listener,
PointerChoreographerPolicyInterface&);
- ~PointerChoreographer() override = default;
+ ~PointerChoreographer() override;
- void setDefaultMouseDisplayId(int32_t displayId) override;
+ void setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) override;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override;
std::optional<DisplayViewport> getViewportForPointerDevice(
- int32_t associatedDisplayId) override;
- FloatPoint getMouseCursorPosition(int32_t displayId) override;
+ ui::LogicalDisplayId associatedDisplayId) override;
+ FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId) override;
void setShowTouchesEnabled(bool enabled) override;
void setStylusPointerIconEnabled(bool enabled) override;
bool setPointerIcon(std::variant<std::unique_ptr<SpriteIcon>, PointerIconStyle> icon,
- int32_t displayId, DeviceId deviceId) override;
- void setPointerIconVisibility(int32_t displayId, bool visible) override;
+ ui::LogicalDisplayId displayId, DeviceId deviceId) override;
+ void setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -106,19 +107,24 @@
void notifyDeviceReset(const NotifyDeviceResetArgs& args) override;
void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override;
+ // Public because it's also used by tests to simulate the WindowInfosListener callback
+ void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos);
+
void dump(std::string& dump) override;
private:
- using PointerDisplayChange =
- std::optional<std::tuple<int32_t /*displayId*/, FloatPoint /*cursorPosition*/>>;
+ using PointerDisplayChange = std::optional<
+ std::tuple<ui::LogicalDisplayId /*displayId*/, FloatPoint /*cursorPosition*/>>;
[[nodiscard]] PointerDisplayChange updatePointerControllersLocked() REQUIRES(mLock);
[[nodiscard]] PointerDisplayChange calculatePointerDisplayChangeToNotify() REQUIRES(mLock);
- const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock);
- int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock);
- std::pair<int32_t /*displayId*/, PointerControllerInterface&> ensureMouseControllerLocked(
- int32_t associatedDisplayId) REQUIRES(mLock);
+ const DisplayViewport* findViewportByIdLocked(ui::LogicalDisplayId displayId) const
+ REQUIRES(mLock);
+ ui::LogicalDisplayId getTargetMouseDisplayLocked(ui::LogicalDisplayId associatedDisplayId) const
+ REQUIRES(mLock);
+ std::pair<ui::LogicalDisplayId /*displayId*/, PointerControllerInterface&>
+ ensureMouseControllerLocked(ui::LogicalDisplayId associatedDisplayId) REQUIRES(mLock);
InputDeviceInfo* findInputDeviceLocked(DeviceId deviceId) REQUIRES(mLock);
- bool canUnfadeOnDisplay(int32_t displayId) REQUIRES(mLock);
+ bool canUnfadeOnDisplay(ui::LogicalDisplayId displayId) REQUIRES(mLock);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
@@ -127,20 +133,38 @@
void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processDeviceReset(const NotifyDeviceResetArgs& args);
+ void onControllerAddedOrRemoved() REQUIRES(mLock);
+ void onWindowInfosChangedLocked(const std::vector<android::gui::WindowInfo>& windowInfos)
+ REQUIRES(mLock);
+
+ class PointerChoreographerDisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
+ : mPointerChoreographer(pc){};
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
+ void onPointerChoreographerDestroyed();
+
+ private:
+ std::mutex mListenerLock;
+ PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+ };
+ sp<PointerChoreographerDisplayInfoListener> mWindowInfoListener GUARDED_BY(mLock);
using ControllerConstructor =
ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>;
ControllerConstructor mTouchControllerConstructor GUARDED_BY(mLock);
- ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock);
- ControllerConstructor getStylusControllerConstructor(int32_t displayId) REQUIRES(mLock);
+ ControllerConstructor getMouseControllerConstructor(ui::LogicalDisplayId displayId)
+ REQUIRES(mLock);
+ ControllerConstructor getStylusControllerConstructor(ui::LogicalDisplayId displayId)
+ REQUIRES(mLock);
std::mutex mLock;
InputListenerInterface& mNextListener;
PointerChoreographerPolicyInterface& mPolicy;
- std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay
- GUARDED_BY(mLock);
+ std::map<ui::LogicalDisplayId, std::shared_ptr<PointerControllerInterface>>
+ mMousePointersByDisplay GUARDED_BY(mLock);
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mTouchPointersByDevice
GUARDED_BY(mLock);
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mStylusPointersByDevice
@@ -148,14 +172,14 @@
std::map<DeviceId, std::shared_ptr<PointerControllerInterface>> mDrawingTabletPointersByDevice
GUARDED_BY(mLock);
- int32_t mDefaultMouseDisplayId GUARDED_BY(mLock);
- int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock);
+ ui::LogicalDisplayId mDefaultMouseDisplayId GUARDED_BY(mLock);
+ ui::LogicalDisplayId mNotifiedPointerDisplayId GUARDED_BY(mLock);
std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock);
std::set<DeviceId> mMouseDevices GUARDED_BY(mLock);
std::vector<DisplayViewport> mViewports GUARDED_BY(mLock);
bool mShowTouchesEnabled GUARDED_BY(mLock);
bool mStylusPointerIconEnabled GUARDED_BY(mLock);
- std::set<int32_t /*displayId*/> mDisplaysWithPointersHidden;
+ std::set<ui::LogicalDisplayId /*displayId*/> mDisplaysWithPointersHidden;
};
} // namespace android
diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING
index 293ad66..a4dd909 100644
--- a/services/inputflinger/TEST_MAPPING
+++ b/services/inputflinger/TEST_MAPPING
@@ -1,10 +1,10 @@
{
"presubmit": [
{
- "name": "CtsWindowManagerDeviceWindow",
+ "name": "CtsWindowManagerDeviceInput",
"options": [
{
- "include-filter": "android.server.wm.window.WindowInputTests"
+ "include-filter": "android.server.wm.input.WindowInputTests"
}
]
},
@@ -146,6 +146,9 @@
"include-filter": "android.security.cts.Poc19_03#testPocBug_115739809"
}
]
+ },
+ {
+ "name": "monkey_test"
}
],
"postsubmit": [
@@ -284,6 +287,9 @@
},
{
"name": "CtsInputHostTestCases"
+ },
+ {
+ "name": "monkey_test"
}
],
"staged-platinum-postsubmit": [
diff --git a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
index 2f6b8fc..cc0592e 100644
--- a/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
+++ b/services/inputflinger/aidl/com/android/server/inputflinger/IInputThread.aidl
@@ -21,6 +21,13 @@
* infrastructure.
*
* <p>
+ * Earlier, we used rust thread park()/unpark() to put the thread to sleep and wake up from sleep.
+ * But that caused some breakages after migrating the rust system crates to 2021 edition. Since,
+ * the threads are created in C++, it was more reliable to rely on C++ side of the implementation
+ * to implement the sleep and wake functions.
+ * </p>
+ *
+ * <p>
* NOTE: Tried using rust provided threading infrastructure but that uses std::thread which doesn't
* have JNI support and can't call into Java policy that we use currently. libutils provided
* Thread.h also recommends against using std::thread and using the provided infrastructure that
@@ -33,6 +40,16 @@
/** Finish input thread (if not running, this call does nothing) */
void finish();
+ /** Wakes up the thread (if sleeping) */
+ void wake();
+
+ /**
+ * Puts the thread to sleep until a future time provided.
+ *
+ * NOTE: The thread can be awaken before the provided time using {@link wake()} function.
+ */
+ void sleepUntil(long whenNanos);
+
/** Callbacks from C++ to call into inputflinger rust components */
interface IInputThreadCallback {
/**
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
index 2d12574..4385072 100644
--- a/services/inputflinger/benchmarks/Android.bp
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -11,6 +11,7 @@
cc_benchmark {
name: "inputflinger_benchmarks",
srcs: [
+ ":inputdispatcher_common_test_sources",
"InputDispatcher_benchmarks.cpp",
],
defaults: [
@@ -31,6 +32,8 @@
],
static_libs: [
"libattestation",
+ "libgmock",
+ "libgtest",
"libinputdispatcher",
],
}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
index 5ae3715..66e4a29 100644
--- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -18,11 +18,10 @@
#include <android/os/IInputConstants.h>
#include <binder/Binder.h>
-#include <gui/constants.h>
#include "../dispatcher/InputDispatcher.h"
#include "../tests/FakeApplicationHandle.h"
#include "../tests/FakeInputDispatcherPolicy.h"
-#include "../tests/FakeWindowHandle.h"
+#include "../tests/FakeWindows.h"
using android::base::Result;
using android::gui::WindowInfo;
@@ -38,7 +37,7 @@
constexpr DeviceId DEVICE_ID = 1;
// An arbitrary display id
-constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
@@ -63,7 +62,7 @@
ui::Transform identityTransform;
MotionEvent event;
event.initialize(IInputConstants::INVALID_INPUT_EVENT_ID, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ ui::ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0,
/* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
identityTransform, /* xPrecision */ 0,
@@ -89,7 +88,7 @@
const nsecs_t currentTime = now();
// Define a valid motion event.
NotifyMotionArgs args(IInputConstants::INVALID_INPUT_EVENT_ID, currentTime, currentTime,
- DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ui::ADISPLAY_ID_DEFAULT,
POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
/* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
@@ -104,16 +103,16 @@
static void benchmarkNotifyMotion(benchmark::State& state) {
// Create dispatcher
FakeInputDispatcherPolicy fakePolicy;
- InputDispatcher dispatcher(fakePolicy);
- dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- dispatcher.start();
+ auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
- dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ dispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs = generateMotionArgs();
@@ -122,60 +121,60 @@
motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
motionArgs.downTime = now();
motionArgs.eventTime = motionArgs.downTime;
- dispatcher.notifyMotion(motionArgs);
+ dispatcher->notifyMotion(motionArgs);
// Send ACTION_UP
motionArgs.action = AMOTION_EVENT_ACTION_UP;
motionArgs.eventTime = now();
- dispatcher.notifyMotion(motionArgs);
+ dispatcher->notifyMotion(motionArgs);
- window->consumeMotion();
- window->consumeMotion();
+ window->consumeMotionEvent();
+ window->consumeMotionEvent();
}
- dispatcher.stop();
+ dispatcher->stop();
}
static void benchmarkInjectMotion(benchmark::State& state) {
// Create dispatcher
FakeInputDispatcherPolicy fakePolicy;
- InputDispatcher dispatcher(fakePolicy);
- dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- dispatcher.start();
+ auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
// Create a window that will receive motion events
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);
- dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ dispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
for (auto _ : state) {
MotionEvent event = generateMotionEvent();
// Send ACTION_DOWN
- dispatcher.injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
- INJECT_EVENT_TIMEOUT,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ dispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
+ INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
// Send ACTION_UP
event.setAction(AMOTION_EVENT_ACTION_UP);
- dispatcher.injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
- INJECT_EVENT_TIMEOUT,
- POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+ dispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
+ INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
- window->consumeMotion();
- window->consumeMotion();
+ window->consumeMotionEvent();
+ window->consumeMotionEvent();
}
- dispatcher.stop();
+ dispatcher->stop();
}
static void benchmarkOnWindowInfosChanged(benchmark::State& state) {
// Create dispatcher
FakeInputDispatcherPolicy fakePolicy;
- InputDispatcher dispatcher(fakePolicy);
- dispatcher.setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
- dispatcher.start();
+ auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
// Create a window
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -188,12 +187,12 @@
std::vector<gui::DisplayInfo> displayInfos{info};
for (auto _ : state) {
- dispatcher.onWindowInfosChanged(
+ dispatcher->onWindowInfosChanged(
{windowInfos, displayInfos, /*vsyncId=*/0, /*timestamp=*/0});
- dispatcher.onWindowInfosChanged(
+ dispatcher->onWindowInfosChanged(
{/*windowInfos=*/{}, /*displayInfos=*/{}, /*vsyncId=*/{}, /*timestamp=*/0});
}
- dispatcher.stop();
+ dispatcher->stop();
}
} // namespace
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 6d71acc..29aa3c3 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -59,6 +59,8 @@
srcs: [":libinputdispatcher_sources"],
shared_libs: [
"libbase",
+ "libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libinput",
@@ -69,6 +71,7 @@
"libutils",
"libstatspull",
"libstatssocket",
+ "packagemanager_aidl-cpp",
"server_configurable_flags",
],
static_libs: [
diff --git a/services/inputflinger/dispatcher/CancelationOptions.h b/services/inputflinger/dispatcher/CancelationOptions.h
index 9c73f03..4a0889f 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -48,7 +48,7 @@
std::optional<int32_t> deviceId = std::nullopt;
// The specific display id of events to cancel, or nullopt to cancel events on any display.
- std::optional<int32_t> displayId = std::nullopt;
+ std::optional<ui::LogicalDisplayId> displayId = std::nullopt;
// The specific pointers to cancel, or nullopt to cancel all pointer events
std::optional<std::bitset<MAX_POINTER_ID + 1>> pointerIds = std::nullopt;
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 0246d60..ad9cec1 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -23,6 +23,7 @@
#include <android-base/stringprintf.h>
#include <cutils/atomic.h>
+#include <ftl/enum.h>
#include <inttypes.h>
using android::base::StringPrintf;
@@ -131,9 +132,9 @@
// --- KeyEntry ---
KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
- int32_t metaState, int32_t repeatCount, nsecs_t downTime)
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode,
+ int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime)
: EventEntry(id, Type::KEY, eventTime, policyFlags),
deviceId(deviceId),
source(source),
@@ -155,13 +156,14 @@
if (!IS_DEBUGGABLE_BUILD) {
return "KeyEvent";
}
- return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
- ", action=%s, "
+ return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%s, "
+ "action=%s, "
"flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
- KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode),
- keyCode, scanCode, metaState, repeatCount, policyFlags);
+ deviceId, eventTime, inputEventSourceToString(source).c_str(),
+ displayId.toString().c_str(), KeyEvent::actionToString(action), flags,
+ KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState, repeatCount,
+ policyFlags);
}
std::ostream& operator<<(std::ostream& out, const KeyEntry& keyEntry) {
@@ -171,7 +173,8 @@
// --- TouchModeEntry ---
-TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
+TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode,
+ ui::LogicalDisplayId displayId)
: EventEntry(id, Type::TOUCH_MODE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER),
inTouchMode(inTouchMode),
displayId(displayId) {}
@@ -183,12 +186,13 @@
// --- MotionEntry ---
MotionEntry::MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState,
- nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId,
- uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags,
- int32_t metaState, int32_t buttonState,
- MotionClassification classification, int32_t edgeFlags, float xPrecision,
- float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
+ nsecs_t eventTime, int32_t deviceId, uint32_t source,
+ ui::LogicalDisplayId displayId, uint32_t policyFlags, 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,
+ const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords)
: EventEntry(id, Type::MOTION, eventTime, policyFlags),
deviceId(deviceId),
@@ -217,15 +221,16 @@
}
std::string msg;
msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
- ", source=%s, displayId=%" PRId32
- ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
+ ", source=%s, displayId=%s, action=%s, actionButton=0x%08x, flags=0x%08x,"
+ " metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
- MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
- buttonState, motionClassificationToString(classification), edgeFlags,
- xPrecision, yPrecision, xCursorPosition, yCursorPosition);
+ deviceId, eventTime, inputEventSourceToString(source).c_str(),
+ displayId.toString().c_str(), MotionEvent::actionToString(action).c_str(),
+ actionButton, flags, metaState, buttonState,
+ motionClassificationToString(classification), edgeFlags, xPrecision,
+ yPrecision, xCursorPosition, yCursorPosition);
for (uint32_t i = 0; i < getPointerCount(); i++) {
if (i) {
@@ -261,9 +266,10 @@
std::string SensorEntry::getDescription() const {
std::string msg;
msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
- "accuracy=0x%08x, hwTimestamp=%" PRId64,
+ "accuracy=%s, hwTimestamp=%" PRId64,
deviceId, inputEventSourceToString(source).c_str(),
- ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
+ ftl::enum_string(sensorType).c_str(), ftl::enum_string(accuracy).c_str(),
+ hwTimestamp);
if (IS_DEBUGGABLE_BUILD) {
for (size_t i = 0; i < values.size(); i++) {
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 06d5c7d..f2f31d8 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -120,7 +120,7 @@
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
int32_t action;
int32_t keyCode;
int32_t scanCode;
@@ -144,9 +144,9 @@
mutable int32_t repeatCount;
KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- int32_t repeatCount, nsecs_t downTime);
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId,
+ uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, int32_t repeatCount, nsecs_t downTime);
std::string getDescription() const override;
};
@@ -155,7 +155,7 @@
struct MotionEntry : EventEntry {
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
int32_t action;
int32_t actionButton;
int32_t flags;
@@ -175,11 +175,12 @@
size_t getPointerCount() const { return pointerProperties.size(); }
MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
- int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
- int32_t buttonState, MotionClassification classification, int32_t edgeFlags,
- float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition,
- nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties,
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId,
+ uint32_t policyFlags, 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,
+ const std::vector<PointerProperties>& pointerProperties,
const std::vector<PointerCoords>& pointerCoords);
std::string getDescription() const override;
};
@@ -205,9 +206,9 @@
struct TouchModeEntry : EventEntry {
bool inTouchMode;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
- TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId);
+ TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, ui::LogicalDisplayId displayId);
std::string getDescription() const override;
};
diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp
index 0e4e79e..b374fad 100644
--- a/services/inputflinger/dispatcher/FocusResolver.cpp
+++ b/services/inputflinger/dispatcher/FocusResolver.cpp
@@ -41,12 +41,12 @@
size_t operator()(const sp<T>& k) const { return std::hash<T*>()(k.get()); }
};
-sp<IBinder> FocusResolver::getFocusedWindowToken(int32_t displayId) const {
+sp<IBinder> FocusResolver::getFocusedWindowToken(ui::LogicalDisplayId displayId) const {
auto it = mFocusedWindowTokenByDisplay.find(displayId);
return it != mFocusedWindowTokenByDisplay.end() ? it->second.second : nullptr;
}
-std::optional<FocusRequest> FocusResolver::getFocusRequest(int32_t displayId) {
+std::optional<FocusRequest> FocusResolver::getFocusRequest(ui::LogicalDisplayId displayId) {
auto it = mFocusRequestByDisplay.find(displayId);
return it != mFocusRequestByDisplay.end() ? std::make_optional<>(it->second) : std::nullopt;
}
@@ -58,7 +58,7 @@
* we will check if the previous focus request is eligible to receive focus.
*/
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
- int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
+ ui::LogicalDisplayId displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
std::string removeFocusReason;
const std::optional<FocusRequest> request = getFocusRequest(displayId);
@@ -94,12 +94,11 @@
std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
- const int32_t displayId = request.displayId;
+ const ui::LogicalDisplayId displayId = ui::LogicalDisplayId{request.displayId};
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
if (currentFocus == request.token) {
- ALOGD_IF(DEBUG_FOCUS,
- "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
- request.windowName.c_str(), displayId);
+ ALOGD_IF(DEBUG_FOCUS, "setFocusedWindow %s on display %s ignored, reason: already focused",
+ request.windowName.c_str(), displayId.toString().c_str());
return std::nullopt;
}
@@ -193,7 +192,7 @@
}
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
- int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
+ ui::LogicalDisplayId displayId, const std::string& reason, const sp<IBinder>& newFocus,
const std::string& tokenName) {
sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
if (newFocus == oldFocus) {
@@ -216,8 +215,8 @@
std::string dump;
dump += INDENT "FocusedWindows:\n";
for (const auto& [displayId, namedToken] : mFocusedWindowTokenByDisplay) {
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s'\n", displayId,
- namedToken.first.c_str());
+ dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s'\n",
+ displayId.toString().c_str(), namedToken.first.c_str());
}
return dump;
}
@@ -233,13 +232,14 @@
auto it = mLastFocusResultByDisplay.find(displayId);
std::string result =
it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : "";
- dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n",
- displayId, request.windowName.c_str(), result.c_str());
+ dump += base::StringPrintf(INDENT2 "displayId=%s, name='%s' result='%s'\n",
+ displayId.toString().c_str(), request.windowName.c_str(),
+ result.c_str());
}
return dump;
}
-void FocusResolver::displayRemoved(int32_t displayId) {
+void FocusResolver::displayRemoved(ui::LogicalDisplayId displayId) {
mFocusRequestByDisplay.erase(displayId);
mLastFocusResultByDisplay.erase(displayId);
}
diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h
index 5bb157b..2910ba4 100644
--- a/services/inputflinger/dispatcher/FocusResolver.h
+++ b/services/inputflinger/dispatcher/FocusResolver.h
@@ -49,22 +49,23 @@
class FocusResolver {
public:
// Returns the focused window token on the specified display.
- sp<IBinder> getFocusedWindowToken(int32_t displayId) const;
+ sp<IBinder> getFocusedWindowToken(ui::LogicalDisplayId displayId) const;
struct FocusChanges {
sp<IBinder> oldFocus;
sp<IBinder> newFocus;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
std::string reason;
};
std::optional<FocusResolver::FocusChanges> setInputWindows(
- int32_t displayId, const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
+ ui::LogicalDisplayId displayId,
+ const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
std::optional<FocusResolver::FocusChanges> setFocusedWindow(
const android::gui::FocusRequest& request,
const std::vector<sp<android::gui::WindowInfoHandle>>& windows);
// Display has been removed from the system, clean up old references.
- void displayRemoved(int32_t displayId);
+ void displayRemoved(ui::LogicalDisplayId displayId);
// exposed for debugging
bool hasFocusedWindowTokens() const { return !mFocusedWindowTokenByDisplay.empty(); }
@@ -105,20 +106,23 @@
// the same token. Focus is tracked by the token per display and the events are dispatched
// to the channel associated by this token.
typedef std::pair<std::string /* name */, sp<IBinder>> NamedToken;
- std::unordered_map<int32_t /* displayId */, NamedToken> mFocusedWindowTokenByDisplay;
+ std::unordered_map<ui::LogicalDisplayId /* displayId */, NamedToken>
+ mFocusedWindowTokenByDisplay;
// This map will store the focus request per display. When the input window handles are updated,
// the current request will be checked to see if it can be processed at that time.
- std::unordered_map<int32_t /* displayId */, android::gui::FocusRequest> mFocusRequestByDisplay;
+ std::unordered_map<ui::LogicalDisplayId /* displayId */, android::gui::FocusRequest>
+ mFocusRequestByDisplay;
// Last reason for not granting a focus request. This is used to add more debug information
// in the event logs.
- std::unordered_map<int32_t /* displayId */, Focusability> mLastFocusResultByDisplay;
+ std::unordered_map<ui::LogicalDisplayId /* displayId */, Focusability>
+ mLastFocusResultByDisplay;
std::optional<FocusResolver::FocusChanges> updateFocusedWindow(
- int32_t displayId, const std::string& reason, const sp<IBinder>& token,
+ ui::LogicalDisplayId displayId, const std::string& reason, const sp<IBinder>& token,
const std::string& tokenName = "");
- std::optional<android::gui::FocusRequest> getFocusRequest(int32_t displayId);
+ std::optional<android::gui::FocusRequest> getFocusRequest(ui::LogicalDisplayId displayId);
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 2df86a0..8e0da14 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -88,13 +88,12 @@
}
// Create the input tracing backend that writes to perfetto from a single thread.
-std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled(
- trace::impl::PerfettoBackend::GetPackageUid getPackageUid) {
+std::unique_ptr<trace::InputTracingBackendInterface> createInputTracingBackendIfEnabled() {
if (!isInputTracingEnabled()) {
return nullptr;
}
return std::make_unique<trace::impl::ThreadedBackend<trace::impl::PerfettoBackend>>(
- trace::impl::PerfettoBackend(getPackageUid));
+ trace::impl::PerfettoBackend());
}
template <class Entry>
@@ -572,8 +571,8 @@
}
// Returns true if the given window can accept pointer events at the given display location.
-bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
- bool isStylus, const ui::Transform& displayTransform) {
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, ui::LogicalDisplayId displayId, float x,
+ float y, bool isStylus, const ui::Transform& displayTransform) {
const auto inputConfig = windowInfo.inputConfig;
if (windowInfo.displayId != displayId ||
inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
@@ -601,8 +600,8 @@
// Returns true if the given window's frame can occlude pointer events at the given display
// location.
-bool windowOccludesTouchAt(const WindowInfo& windowInfo, int displayId, float x, float y,
- const ui::Transform& displayTransform) {
+bool windowOccludesTouchAt(const WindowInfo& windowInfo, ui::LogicalDisplayId displayId, float x,
+ float y, const ui::Transform& displayTransform) {
if (windowInfo.displayId != displayId) {
return false;
}
@@ -816,9 +815,9 @@
/**
* Return true if stylus is currently down anywhere on the specified display, and false otherwise.
*/
-bool isStylusActiveInDisplay(
- int32_t displayId,
- const std::unordered_map<int32_t /*displayId*/, TouchState>& touchStatesByDisplay) {
+bool isStylusActiveInDisplay(ui::LogicalDisplayId displayId,
+ const std::unordered_map<ui::LogicalDisplayId /*displayId*/,
+ TouchState>& touchStatesByDisplay) {
const auto it = touchStatesByDisplay.find(displayId);
if (it == touchStatesByDisplay.end()) {
return false;
@@ -904,9 +903,7 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
- : InputDispatcher(policy, createInputTracingBackendIfEnabled([&policy](std::string pkg) {
- return policy.getPackageUid(pkg);
- })) {}
+ : InputDispatcher(policy, createInputTracingBackendIfEnabled()) {}
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
@@ -921,8 +918,9 @@
mDispatchFrozen(false),
mInputFilterEnabled(false),
mMaximumObscuringOpacityForTouch(1.0f),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
+ mFocusedDisplayId(ui::ADISPLAY_ID_DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
+ mAwaitedApplicationDisplayId(ui::ADISPLAY_ID_NONE),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator) {
mLooper = sp<Looper>::make(false);
@@ -1289,7 +1287,7 @@
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
if (isPointerDownEvent && mAwaitedFocusedApplication != nullptr) {
- const int32_t displayId = motionEntry.displayId;
+ const ui::LogicalDisplayId displayId = motionEntry.displayId;
const auto [x, y] = resolveTouchedPosition(motionEntry);
const bool isStylus = isPointerFromStylus(motionEntry, /*pointerIndex=*/0);
@@ -1414,8 +1412,8 @@
}
}
-sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y,
- bool isStylus,
+sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(ui::LogicalDisplayId displayId,
+ float x, float y, bool isStylus,
bool ignoreDragWindow) const {
// Traverse windows from front to back to find touched window.
const auto& windowHandles = getWindowHandlesLocked(displayId);
@@ -1434,7 +1432,8 @@
}
std::vector<InputTarget> InputDispatcher::findOutsideTargetsLocked(
- int32_t displayId, const sp<WindowInfoHandle>& touchedWindow, int32_t pointerId) const {
+ ui::LogicalDisplayId displayId, const sp<WindowInfoHandle>& touchedWindow,
+ int32_t pointerId) const {
if (touchedWindow == nullptr) {
return {};
}
@@ -1461,7 +1460,7 @@
}
std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(
- int32_t displayId, float x, float y, bool isStylus) const {
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const {
// Traverse windows from front to back and gather the touched spy windows.
std::vector<sp<WindowInfoHandle>> spyWindows;
const auto& windowHandles = getWindowHandlesLocked(displayId);
@@ -1954,12 +1953,12 @@
void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", "
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%s, "
"policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, "
"metaState=0x%x, repeatCount=%d, downTime=%" PRId64,
- prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId,
- entry.policyFlags, entry.action, entry.flags, entry.keyCode, entry.scanCode,
- entry.metaState, entry.repeatCount, entry.downTime);
+ prefix, entry.eventTime, entry.deviceId, entry.source,
+ entry.displayId.toString().c_str(), entry.policyFlags, entry.action, entry.flags,
+ entry.keyCode, entry.scanCode, entry.metaState, entry.repeatCount, entry.downTime);
}
}
@@ -2108,16 +2107,15 @@
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
- ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%" PRId32
- ", policyFlags=0x%x, "
+ ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, "
"metaState=0x%x, buttonState=0x%x,"
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64,
prefix, entry.eventTime, entry.deviceId,
- inputEventSourceToString(entry.source).c_str(), entry.displayId, entry.policyFlags,
- MotionEvent::actionToString(entry.action).c_str(), entry.actionButton, entry.flags,
- entry.metaState, entry.buttonState, entry.edgeFlags, entry.xPrecision,
- entry.yPrecision, entry.downTime);
+ inputEventSourceToString(entry.source).c_str(), entry.displayId.toString().c_str(),
+ entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(),
+ entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags,
+ entry.xPrecision, entry.yPrecision, entry.downTime);
for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, "
@@ -2202,8 +2200,8 @@
* then it should be dispatched to that display. Otherwise, the event goes to the focused display.
* Focused display is the display that the user most recently interacted with.
*/
-int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
- int32_t displayId;
+ui::LogicalDisplayId InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
switch (entry.type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
@@ -2223,10 +2221,10 @@
case EventEntry::Type::SENSOR:
case EventEntry::Type::DRAG: {
ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str());
- return ADISPLAY_ID_NONE;
+ return ui::ADISPLAY_ID_NONE;
}
}
- return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
+ return displayId == ui::ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
}
bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
@@ -2265,7 +2263,7 @@
InputEventInjectionResult& outInjectionResult) {
outInjectionResult = InputEventInjectionResult::FAILED; // Default result
- int32_t displayId = getTargetDisplayId(entry);
+ ui::LogicalDisplayId displayId = getTargetDisplayId(entry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
std::shared_ptr<InputApplicationHandle> focusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
@@ -2274,8 +2272,8 @@
// then drop the event.
if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) {
ALOGI("Dropping %s event because there is no focused window or focused application in "
- "display %" PRId32 ".",
- ftl::enum_string(entry.type).c_str(), displayId);
+ "display %s.",
+ ftl::enum_string(entry.type).c_str(), displayId.toString().c_str());
return nullptr;
}
@@ -2383,7 +2381,7 @@
std::vector<InputTarget> targets;
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
- const int32_t displayId = entry.displayId;
+ const ui::LogicalDisplayId displayId = entry.displayId;
const int32_t action = entry.action;
const int32_t maskedAction = MotionEvent::getActionMasked(action);
@@ -2453,9 +2451,10 @@
}
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
- ALOGD("No new touched window at (%.1f, %.1f) in display %" PRId32, x, y, displayId);
+ ALOGD("No new touched window at (%.1f, %.1f) in display %s", x, y,
+ displayId.toString().c_str());
// Try to assign the pointer to the first foreground window we find, if there is one.
- newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
+ newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
}
// Verify targeted injection.
@@ -2494,8 +2493,8 @@
if (newTouchedWindows.empty()) {
ALOGI("Dropping event because there is no touchable window at (%.1f, %.1f) on display "
- "%d.",
- x, y, displayId);
+ "%s.",
+ x, y, displayId.toString().c_str());
outInjectionResult = InputEventInjectionResult::FAILED;
return {};
}
@@ -2532,11 +2531,19 @@
if (!isHoverAction) {
const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;
- tempTouchState.addOrUpdateWindow(windowHandle, InputTarget::DispatchMode::AS_IS,
- targetFlags, entry.deviceId, {pointer},
- isDownOrPointerDown
- ? std::make_optional(entry.eventTime)
- : std::nullopt);
+ Result<void> addResult =
+ tempTouchState.addOrUpdateWindow(windowHandle,
+ InputTarget::DispatchMode::AS_IS,
+ targetFlags, entry.deviceId, {pointer},
+ isDownOrPointerDown
+ ? std::make_optional(
+ entry.eventTime)
+ : std::nullopt);
+ if (!addResult.ok()) {
+ LOG(ERROR) << "Error while processing " << entry << " for "
+ << windowHandle->getName();
+ logDispatchStateLocked();
+ }
// If this is the pointer going down and the touched window has a wallpaper
// then also add the touched wallpaper windows so they are locked in for the
// duration of the touch gesture. We do not collect wallpapers during HOVER_MOVE or
@@ -2611,11 +2618,11 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.getPointerCount() == 1 &&
- tempTouchState.isSlippery()) {
+ tempTouchState.isSlippery(entry.deviceId)) {
const auto [x, y] = resolveTouchedPosition(entry);
const bool isStylus = isPointerFromStylus(entry, /*pointerIndex=*/0);
sp<WindowInfoHandle> oldTouchedWindowHandle =
- tempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
sp<WindowInfoHandle> newTouchedWindowHandle =
findTouchedWindowAtLocked(displayId, x, y, isStylus);
@@ -2635,9 +2642,9 @@
if (newTouchedWindowHandle != nullptr &&
!haveSameToken(oldTouchedWindowHandle, newTouchedWindowHandle)) {
- ALOGI("Touch is slipping out of window %s into window %s in display %" PRId32,
+ ALOGI("Touch is slipping out of window %s into window %s in display %s",
oldTouchedWindowHandle->getName().c_str(),
- newTouchedWindowHandle->getName().c_str(), displayId);
+ newTouchedWindowHandle->getName().c_str(), displayId.toString().c_str());
// Make a slippery exit from the old window.
std::bitset<MAX_POINTER_ID + 1> pointerIds;
@@ -2733,7 +2740,7 @@
// has a different UID, then we will not reveal coordinate information to this window.
if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
sp<WindowInfoHandle> foregroundWindowHandle =
- tempTouchState.getFirstForegroundWindowHandle();
+ tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
if (foregroundWindowHandle) {
const auto foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
for (InputTarget& target : targets) {
@@ -2822,7 +2829,7 @@
// Save changes unless the action was scroll in which case the temporary touch
// state was only valid for this one action.
if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
- if (displayId >= 0) {
+ if (displayId >= ui::ADISPLAY_ID_DEFAULT) {
tempTouchState.clearWindowsWithoutPointers();
mTouchStatesByDisplay[displayId] = tempTouchState;
} else {
@@ -2837,7 +2844,7 @@
return targets;
}
-void InputDispatcher::finishDragAndDrop(int32_t displayId, float x, float y) {
+void InputDispatcher::finishDragAndDrop(ui::LogicalDisplayId displayId, float x, float y) {
// Prevent stylus interceptor windows from affecting drag and drop behavior for now, until we
// have an explicit reason to support it.
constexpr bool isStylus = false;
@@ -3046,11 +3053,15 @@
<< ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor;
}
- it->addPointers(pointerIds, windowInfo->transform);
+ Result<void> result = it->addPointers(pointerIds, windowInfo->transform);
+ if (!result.ok()) {
+ logDispatchStateLocked();
+ LOG(FATAL) << result.error().message();
+ }
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
- int32_t displayId) {
+ ui::LogicalDisplayId displayId) {
auto monitorsIt = mGlobalMonitorsByDisplay.find(displayId);
if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
@@ -3122,7 +3133,7 @@
InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
const sp<WindowInfoHandle>& windowHandle, float x, float y) const {
const WindowInfo* windowInfo = windowHandle->getInfo();
- int32_t displayId = windowInfo->displayId;
+ ui::LogicalDisplayId displayId = windowInfo->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
TouchOcclusionInfo info;
info.hasBlockingOcclusion = false;
@@ -3206,7 +3217,7 @@
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
float x, float y) const {
- int32_t displayId = windowHandle->getInfo()->displayId;
+ ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
@@ -3222,7 +3233,7 @@
}
bool InputDispatcher::isWindowObscuredLocked(const sp<WindowInfoHandle>& windowHandle) const {
- int32_t displayId = windowHandle->getInfo()->displayId;
+ ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
const WindowInfo* windowInfo = windowHandle->getInfo();
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
@@ -3230,8 +3241,7 @@
break; // All future windows are below us. Exit early.
}
const WindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) &&
- otherInfo->overlaps(windowInfo)) {
+ if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->overlaps(windowInfo)) {
return true;
}
}
@@ -3273,7 +3283,7 @@
}
}
- int32_t displayId = getTargetDisplayId(eventEntry);
+ ui::LogicalDisplayId displayId = getTargetDisplayId(eventEntry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
const WindowInfo* windowDisablingUserActivityInfo = nullptr;
if (focusedWindowHandle != nullptr) {
@@ -4383,7 +4393,7 @@
// different pointer ids than we expected based on the previous ACTION_DOWN
// or ACTION_POINTER_DOWN events that caused us to decide to split the pointers
// in this way.
- ALOGW("Dropping split motion event because the pointer count is %d but "
+ ALOGW("Dropping split motion event because the pointer count is %zu but "
"we expected there to be %zu pointers. This probably means we received "
"a broken sequence of pointer ids from the input device: %s",
pointerCoords.size(), pointerIds.count(),
@@ -4456,12 +4466,13 @@
void InputDispatcher::notifyKey(const NotifyKeyArgs& args) {
ALOGD_IF(debugInboundEventDetails(),
"notifyKey - id=%" PRIx32 ", eventTime=%" PRId64
- ", deviceId=%d, source=%s, displayId=%" PRId32
- "policyFlags=0x%x, action=%s, flags=0x%x, keyCode=%s, scanCode=0x%x, metaState=0x%x, "
+ ", deviceId=%d, source=%s, displayId=%s, policyFlags=0x%x, action=%s, flags=0x%x, "
+ "keyCode=%s, scanCode=0x%x, metaState=0x%x, "
"downTime=%" PRId64,
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
- args.displayId, args.policyFlags, KeyEvent::actionToString(args.action), args.flags,
- KeyEvent::getLabel(args.keyCode), args.scanCode, args.metaState, args.downTime);
+ args.displayId.toString().c_str(), args.policyFlags,
+ KeyEvent::actionToString(args.action), args.flags, KeyEvent::getLabel(args.keyCode),
+ args.scanCode, args.metaState, args.downTime);
Result<void> keyCheck = validateKeyEvent(args.action);
if (!keyCheck.ok()) {
LOG(ERROR) << "invalid key event: " << keyCheck.error();
@@ -4537,15 +4548,15 @@
void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
if (debugInboundEventDetails()) {
ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
- "displayId=%" PRId32 ", policyFlags=0x%x, "
+ "displayId=%s, policyFlags=0x%x, "
"action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
"edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
"yCursorPosition=%f, downTime=%" PRId64,
args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
- args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),
- args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
- args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
- args.downTime);
+ args.displayId.toString().c_str(), args.policyFlags,
+ MotionEvent::actionToString(args.action).c_str(), args.actionButton, args.flags,
+ args.metaState, args.buttonState, args.edgeFlags, args.xPrecision, args.yPrecision,
+ args.xCursorPosition, args.yCursorPosition, args.downTime);
for (uint32_t i = 0; i < args.getPointerCount(); i++) {
ALOGD(" Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
"touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
@@ -4574,7 +4585,8 @@
if (DEBUG_VERIFY_EVENTS) {
auto [it, _] =
mVerifiersByDisplay.try_emplace(args.displayId,
- StringPrintf("display %" PRId32, args.displayId));
+ StringPrintf("display %s",
+ args.displayId.toString().c_str()));
Result<void> result =
it->second.processMovement(args.deviceId, args.source, args.action,
args.getPointerCount(), args.pointerProperties.data(),
@@ -4720,6 +4732,7 @@
}
void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) {
+ // TODO(b/308677868) Remove device reset from the InputListener interface
if (debugInboundEventDetails()) {
ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime,
args.deviceId);
@@ -4848,8 +4861,9 @@
const bool isPointerEvent =
isFromSource(event->getSource(), AINPUT_SOURCE_CLASS_POINTER);
// If a pointer event has no displayId specified, inject it to the default display.
- const uint32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
- ? ADISPLAY_ID_DEFAULT
+ const ui::LogicalDisplayId displayId =
+ isPointerEvent && (event->getDisplayId() == ui::ADISPLAY_ID_NONE)
+ ? ui::ADISPLAY_ID_DEFAULT
: event->getDisplayId();
int32_t flags = motionEvent.getFlags();
@@ -4870,6 +4884,30 @@
}
mLock.lock();
+
+ if (policyFlags & POLICY_FLAG_FILTERED) {
+ // The events from InputFilter impersonate real hardware devices. Check these
+ // events for consistency and print an error. An inconsistent event sent from
+ // InputFilter could cause a crash in the later stages of dispatching pipeline.
+ auto [it, _] =
+ mInputFilterVerifiersByDisplay
+ .try_emplace(displayId,
+ StringPrintf("Injection on %" PRId32, displayId));
+ InputVerifier& verifier = it->second;
+
+ Result<void> result =
+ verifier.processMovement(resolvedDeviceId, motionEvent.getSource(),
+ motionEvent.getAction(),
+ motionEvent.getPointerCount(),
+ motionEvent.getPointerProperties(),
+ motionEvent.getSamplePointerCoords(), flags);
+ if (!result.ok()) {
+ logDispatchStateLocked();
+ LOG(ERROR) << "Inconsistent event: " << motionEvent
+ << ", reason: " << result.error();
+ }
+ }
+
const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes();
const size_t pointerCount = motionEvent.getPointerCount();
const std::vector<PointerProperties>
@@ -5116,22 +5154,21 @@
}
const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
- int32_t displayId) const {
+ ui::LogicalDisplayId displayId) const {
static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
auto it = mWindowHandlesByDisplay.find(displayId);
return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
}
sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId) const {
+ const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId) const {
if (windowHandleToken == nullptr) {
return nullptr;
}
if (!displayId) {
// Look through all displays.
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+ for (const auto& [_, windowHandles] : mWindowHandlesByDisplay) {
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (windowHandle->getToken() == windowHandleToken) {
return windowHandle;
@@ -5152,16 +5189,15 @@
sp<WindowInfoHandle> InputDispatcher::getWindowHandleLocked(
const sp<WindowInfoHandle>& windowHandle) const {
- for (auto& it : mWindowHandlesByDisplay) {
- const std::vector<sp<WindowInfoHandle>>& windowHandles = it.second;
+ for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
for (const sp<WindowInfoHandle>& handle : windowHandles) {
if (handle->getId() == windowHandle->getId() &&
handle->getToken() == windowHandle->getToken()) {
- if (windowHandle->getInfo()->displayId != it.first) {
- ALOGE("Found window %s in display %" PRId32
- ", but it should belong to display %" PRId32,
- windowHandle->getName().c_str(), it.first,
- windowHandle->getInfo()->displayId);
+ if (windowHandle->getInfo()->displayId != displayId) {
+ ALOGE("Found window %s in display %s"
+ ", but it should belong to display %s",
+ windowHandle->getName().c_str(), displayId.toString().c_str(),
+ windowHandle->getInfo()->displayId.toString().c_str());
}
return handle;
}
@@ -5170,12 +5206,13 @@
return nullptr;
}
-sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(int displayId) const {
+sp<WindowInfoHandle> InputDispatcher::getFocusedWindowHandleLocked(
+ ui::LogicalDisplayId displayId) const {
sp<IBinder> focusedToken = mFocusResolver.getFocusedWindowToken(displayId);
return getWindowHandleLocked(focusedToken, displayId);
}
-ui::Transform InputDispatcher::getTransformLocked(int32_t displayId) const {
+ui::Transform InputDispatcher::getTransformLocked(ui::LogicalDisplayId displayId) const {
auto displayInfoIt = mDisplayInfos.find(displayId);
return displayInfoIt != mDisplayInfos.end() ? displayInfoIt->second.transform
: kIdentityTransform;
@@ -5244,7 +5281,8 @@
}
void InputDispatcher::updateWindowHandlesForDisplayLocked(
- const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
+ const std::vector<sp<WindowInfoHandle>>& windowInfoHandles,
+ ui::LogicalDisplayId displayId) {
if (windowInfoHandles.empty()) {
// Remove all handles on a display if there are no windows left.
mWindowHandlesByDisplay.erase(displayId);
@@ -5276,13 +5314,14 @@
}
if (info->displayId != displayId) {
- ALOGE("Window %s updated by wrong display %d, should belong to display %d",
- handle->getName().c_str(), displayId, info->displayId);
+ ALOGE("Window %s updated by wrong display %s, should belong to display %s",
+ handle->getName().c_str(), displayId.toString().c_str(),
+ info->displayId.toString().c_str());
continue;
}
if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
- (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
+ (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
const sp<WindowInfoHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
@@ -5303,7 +5342,8 @@
* For removed handle, check if need to send a cancel event if already in touch.
*/
void InputDispatcher::setInputWindowsLocked(
- const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
+ const std::vector<sp<WindowInfoHandle>>& windowInfoHandles,
+ ui::LogicalDisplayId displayId) {
if (DEBUG_FOCUS) {
std::string windowList;
for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
@@ -5354,31 +5394,32 @@
onFocusChangedLocked(*changes, traceContext.getTracker(), removedFocusedWindowHandle);
}
- std::unordered_map<int32_t, TouchState>::iterator stateIt =
- mTouchStatesByDisplay.find(displayId);
- if (stateIt != mTouchStatesByDisplay.end()) {
- TouchState& state = stateIt->second;
+ if (const auto& it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+ TouchState& state = it->second;
for (size_t i = 0; i < state.windows.size();) {
TouchedWindow& touchedWindow = state.windows[i];
- if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
- LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
- << " in display %" << displayId;
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "touched window was removed", traceContext.getTracker());
- 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) {
+ if (getWindowHandleLocked(touchedWindow.windowHandle) != nullptr) {
+ i++;
+ continue;
+ }
+ LOG(INFO) << "Touched window was removed: " << touchedWindow.windowHandle->getName()
+ << " in display %" << displayId;
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
+ "touched window was removed", traceContext.getTracker());
+ 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)) {
+ for (const DeviceId deviceId : touchedWindow.getTouchingDeviceIds()) {
+ if (const auto& ww = state.getWallpaperWindow(deviceId); ww != nullptr) {
+ options.deviceId = deviceId;
synthesizeCancelationEventsForWindowLocked(ww, options);
}
}
- state.windows.erase(state.windows.begin() + i);
- } else {
- ++i;
}
+ state.windows.erase(state.windows.begin() + i);
}
// If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
@@ -5407,9 +5448,10 @@
}
void InputDispatcher::setFocusedApplication(
- int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
+ ui::LogicalDisplayId displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
if (DEBUG_FOCUS) {
- ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
+ ALOGD("setFocusedApplication displayId=%s %s", displayId.toString().c_str(),
inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
}
{ // acquire lock
@@ -5422,7 +5464,8 @@
}
void InputDispatcher::setFocusedApplicationLocked(
- int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
+ ui::LogicalDisplayId displayId,
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
@@ -5459,9 +5502,9 @@
* cancel all the unreleased display-unspecified events for the focused window on the old focused
* display. The display-specified events won't be affected.
*/
-void InputDispatcher::setFocusedDisplay(int32_t displayId) {
+void InputDispatcher::setFocusedDisplay(ui::LogicalDisplayId displayId) {
if (DEBUG_FOCUS) {
- ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
+ ALOGD("setFocusedDisplay displayId=%s", displayId.toString().c_str());
}
{ // acquire lock
std::scoped_lock _l(mLock);
@@ -5480,7 +5523,7 @@
options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"The display which contains this window no longer has focus.",
traceContext.getTracker());
- options.displayId = ADISPLAY_ID_NONE;
+ options.displayId = ui::ADISPLAY_ID_NONE;
synthesizeCancelationEventsForWindowLocked(windowHandle, options);
}
mFocusedDisplayId = displayId;
@@ -5490,7 +5533,8 @@
sendFocusChangedCommandLocked(oldFocusedWindowToken, newFocusedWindowToken);
if (newFocusedWindowToken == nullptr) {
- ALOGW("Focused display #%" PRId32 " does not have a focused window.", displayId);
+ ALOGW("Focused display #%s does not have a focused window.",
+ displayId.toString().c_str());
if (mFocusResolver.hasFocusedWindowTokens()) {
ALOGE("But another display has a focused window\n%s",
mFocusResolver.dumpFocusedWindows().c_str());
@@ -5556,15 +5600,15 @@
}
bool InputDispatcher::setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid,
- bool hasPermission, int32_t displayId) {
+ bool hasPermission, ui::LogicalDisplayId displayId) {
bool needWake = false;
{
std::scoped_lock lock(mLock);
ALOGD_IF(DEBUG_TOUCH_MODE,
"Request to change touch mode to %s (calling pid=%s, uid=%s, "
- "hasPermission=%s, target displayId=%d, mTouchModePerDisplay[displayId]=%s)",
+ "hasPermission=%s, target displayId=%s, mTouchModePerDisplay[displayId]=%s)",
toString(inTouchMode), pid.toString().c_str(), uid.toString().c_str(),
- toString(hasPermission), displayId,
+ toString(hasPermission), displayId.toString().c_str(),
mTouchModePerDisplay.count(displayId) == 0
? "not set"
: std::to_string(mTouchModePerDisplay[displayId]).c_str());
@@ -5622,7 +5666,7 @@
mMaximumObscuringOpacityForTouch = opacity;
}
-std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
+std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
InputDispatcher::findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) {
for (auto& [displayId, state] : mTouchStatesByDisplay) {
for (TouchedWindow& w : state.windows) {
@@ -5631,7 +5675,7 @@
}
}
}
- return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT);
+ return std::make_tuple(nullptr, nullptr, ui::ADISPLAY_ID_DEFAULT);
}
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
@@ -5653,13 +5697,13 @@
ALOGD("Touch transfer failed because from window is not being touched.");
return false;
}
- std::set<int32_t> deviceIds = touchedWindow->getTouchingDeviceIds();
+ std::set<DeviceId> deviceIds = touchedWindow->getTouchingDeviceIds();
if (deviceIds.size() != 1) {
LOG(INFO) << "Can't transfer touch. Currently touching devices: " << dumpSet(deviceIds)
<< " for window: " << touchedWindow->dump();
return false;
}
- const int32_t deviceId = *deviceIds.begin();
+ const DeviceId deviceId = *deviceIds.begin();
const sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle;
const sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(toToken, displayId);
@@ -5713,13 +5757,18 @@
"transferring touch from this window to another window",
traceContext.getTracker());
synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection);
- synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
- newTargetFlags,
- traceContext.getTracker());
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
*state, deviceId, pointers, traceContext.getTracker());
+
+ // Because new window may have a wallpaper window, it will merge input state from it
+ // parent window, after this the firstNewPointerIdx in input state will be reset, then
+ // it will cause new move event be thought inconsistent, so we should synthesize the
+ // down event after it reset.
+ synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
+ newTargetFlags,
+ traceContext.getTracker());
}
} // release lock
@@ -5733,10 +5782,11 @@
* Return null if there are no windows touched on that display, or if more than one foreground
* window is being touched.
*/
-sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(int32_t displayId) const {
+sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(
+ ui::LogicalDisplayId displayId) const {
auto stateIt = mTouchStatesByDisplay.find(displayId);
if (stateIt == mTouchStatesByDisplay.end()) {
- ALOGI("No touch state on display %" PRId32, displayId);
+ ALOGI("No touch state on display %s", displayId.toString().c_str());
return nullptr;
}
@@ -5759,14 +5809,14 @@
// Binder call
bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken,
- int32_t displayId) {
+ ui::LogicalDisplayId displayId) {
sp<IBinder> fromToken;
{ // acquire lock
std::scoped_lock _l(mLock);
sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
if (toWindowHandle == nullptr) {
- ALOGW("Could not find window associated with token=%p on display %" PRId32,
- destChannelToken.get(), displayId);
+ ALOGW("Could not find window associated with token=%p on display %s",
+ destChannelToken.get(), displayId.toString().c_str());
return false;
}
@@ -5835,18 +5885,19 @@
dump += StringPrintf(INDENT "DispatchEnabled: %s\n", toString(mDispatchEnabled));
dump += StringPrintf(INDENT "DispatchFrozen: %s\n", toString(mDispatchFrozen));
dump += StringPrintf(INDENT "InputFilterEnabled: %s\n", toString(mInputFilterEnabled));
- dump += StringPrintf(INDENT "FocusedDisplayId: %" PRId32 "\n", mFocusedDisplayId);
+ dump += StringPrintf(INDENT "FocusedDisplayId: %s\n", mFocusedDisplayId.toString().c_str());
if (!mFocusedApplicationHandlesByDisplay.empty()) {
dump += StringPrintf(INDENT "FocusedApplications:\n");
for (auto& it : mFocusedApplicationHandlesByDisplay) {
- const int32_t displayId = it.first;
+ const ui::LogicalDisplayId displayId = it.first;
const std::shared_ptr<InputApplicationHandle>& applicationHandle = it.second;
const std::chrono::duration timeout =
applicationHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT);
- dump += StringPrintf(INDENT2 "displayId=%" PRId32
- ", name='%s', dispatchingTimeout=%" PRId64 "ms\n",
- displayId, applicationHandle->getName().c_str(), millis(timeout));
+ dump += StringPrintf(INDENT2 "displayId=%s, name='%s', dispatchingTimeout=%" PRId64
+ "ms\n",
+ displayId.toString().c_str(), applicationHandle->getName().c_str(),
+ millis(timeout));
}
} else {
dump += StringPrintf(INDENT "FocusedApplications: <none>\n");
@@ -5859,7 +5910,7 @@
dump += StringPrintf(INDENT "TouchStatesByDisplay:\n");
for (const auto& [displayId, state] : mTouchStatesByDisplay) {
std::string touchStateDump = addLinePrefix(state.dump(), INDENT2);
- dump += INDENT2 + std::to_string(displayId) + " : " + touchStateDump;
+ dump += INDENT2 + displayId.toString() + " : " + touchStateDump;
}
} else {
dump += INDENT "TouchStates: <no displays touched>\n";
@@ -5872,7 +5923,7 @@
if (!mWindowHandlesByDisplay.empty()) {
for (const auto& [displayId, windowHandles] : mWindowHandlesByDisplay) {
- dump += StringPrintf(INDENT "Display: %" PRId32 "\n", displayId);
+ dump += StringPrintf(INDENT "Display: %s\n", displayId.toString().c_str());
if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
const auto& displayInfo = it->second;
dump += StringPrintf(INDENT2 "logicalSize=%dx%d\n", displayInfo.logicalWidth,
@@ -5898,7 +5949,8 @@
if (!mGlobalMonitorsByDisplay.empty()) {
for (const auto& [displayId, monitors] : mGlobalMonitorsByDisplay) {
- dump += StringPrintf(INDENT "Global monitors on display %d:\n", displayId);
+ dump += StringPrintf(INDENT "Global monitors on display %s:\n",
+ displayId.toString().c_str());
dumpMonitors(dump, monitors);
}
} else {
@@ -5987,8 +6039,8 @@
if (!mTouchModePerDisplay.empty()) {
dump += INDENT "TouchModePerDisplay:\n";
for (const auto& [displayId, touchMode] : mTouchModePerDisplay) {
- dump += StringPrintf(INDENT2 "Display: %" PRId32 " TouchMode: %s\n", displayId,
- std::to_string(touchMode).c_str());
+ dump += StringPrintf(INDENT2 "Display: %s TouchMode: %s\n",
+ displayId.toString().c_str(), std::to_string(touchMode).c_str());
}
} else {
dump += INDENT "TouchModePerDisplay: <none>\n";
@@ -6061,9 +6113,8 @@
return clientChannel;
}
-Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(int32_t displayId,
- const std::string& name,
- gui::Pid pid) {
+Result<std::unique_ptr<InputChannel>> InputDispatcher::createInputMonitor(
+ ui::LogicalDisplayId displayId, const std::string& name, gui::Pid pid) {
std::unique_ptr<InputChannel> serverChannel;
std::unique_ptr<InputChannel> clientChannel;
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
@@ -6074,7 +6125,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- if (displayId < 0) {
+ if (displayId < ui::ADISPLAY_ID_DEFAULT) {
return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
<< " without a specified display.";
}
@@ -6259,7 +6310,8 @@
mLooper->wake();
}
-void InputDispatcher::setDisplayEligibilityForPointerCapture(int32_t displayId, bool isEligible) {
+void InputDispatcher::setDisplayEligibilityForPointerCapture(ui::LogicalDisplayId displayId,
+ bool isEligible) {
{ // acquire lock
std::scoped_lock _l(mLock);
std::erase(mIneligibleDisplaysForPointerCapture, displayId);
@@ -6835,7 +6887,9 @@
{ // acquire lock
std::scoped_lock _l(mLock);
std::optional<FocusResolver::FocusChanges> changes =
- mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
+ mFocusResolver.setFocusedWindow(request,
+ getWindowHandlesLocked(
+ ui::LogicalDisplayId{request.displayId}));
ScopedSyntheticEventTracer traceContext(mTracer);
if (changes) {
onFocusChangedLocked(*changes, traceContext.getTracker());
@@ -6919,7 +6973,7 @@
postCommandLocked(std::move(command));
}
-void InputDispatcher::displayRemoved(int32_t displayId) {
+void InputDispatcher::displayRemoved(ui::LogicalDisplayId displayId) {
{ // acquire lock
std::scoped_lock _l(mLock);
// Set an empty list to remove all handles from the specific display.
@@ -6933,6 +6987,7 @@
// Remove the associated touch mode state.
mTouchModePerDisplay.erase(displayId);
mVerifiersByDisplay.erase(displayId);
+ mInputFilterVerifiersByDisplay.erase(displayId);
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
@@ -6951,7 +7006,7 @@
};
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
- std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
+ std::unordered_map<ui::LogicalDisplayId, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
@@ -6993,10 +7048,10 @@
WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED) &&
isWindowObscuredLocked(windowHandle))) {
ALOGW("Dropping %s event targeting %s as requested by the input configuration {%s} on "
- "display %" PRId32 ".",
+ "display %s.",
ftl::enum_string(entry.type).c_str(), windowHandle->getName().c_str(),
windowHandle->getInfo()->inputConfig.string().c_str(),
- windowHandle->getInfo()->displayId);
+ windowHandle->getInfo()->displayId.toString().c_str());
return true;
}
return false;
@@ -7030,7 +7085,7 @@
void InputDispatcher::slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<WindowInfoHandle>& oldWindowHandle,
const sp<WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId,
+ TouchState& state, DeviceId deviceId,
const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const {
std::vector<PointerProperties> pointers{pointerProperties};
@@ -7040,7 +7095,7 @@
newWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const sp<WindowInfoHandle> oldWallpaper =
- oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+ oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
@@ -7066,7 +7121,7 @@
void InputDispatcher::transferWallpaperTouch(
ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<WindowInfoHandle> fromWindowHandle,
- const sp<WindowInfoHandle> toWindowHandle, TouchState& state, int32_t deviceId,
+ const sp<WindowInfoHandle> toWindowHandle, TouchState& state, DeviceId deviceId,
const std::vector<PointerProperties>& pointers,
const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) {
const bool oldHasWallpaper = oldTargetFlags.test(InputTarget::Flags::FOREGROUND) &&
@@ -7077,7 +7132,7 @@
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
const sp<WindowInfoHandle> oldWallpaper =
- oldHasWallpaper ? state.getWallpaperWindow() : nullptr;
+ oldHasWallpaper ? state.getWallpaperWindow(deviceId) : nullptr;
const sp<WindowInfoHandle> newWallpaper =
newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr;
if (oldWallpaper == newWallpaper) {
@@ -7140,8 +7195,9 @@
mConfig.keyRepeatDelay = delay.count();
}
-bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token, int32_t displayId,
- DeviceId deviceId, int32_t pointerId) {
+bool InputDispatcher::isPointerInWindow(const sp<android::IBinder>& token,
+ ui::LogicalDisplayId displayId, DeviceId deviceId,
+ int32_t pointerId) {
std::scoped_lock _l(mLock);
auto touchStateIt = mTouchStatesByDisplay.find(displayId);
if (touchStateIt == mTouchStatesByDisplay.end()) {
@@ -7157,4 +7213,11 @@
return false;
}
+void InputDispatcher::setInputMethodConnectionIsActive(bool isActive) {
+ std::scoped_lock _l(mLock);
+ if (mTracer) {
+ mTracer->setInputMethodConnectionIsActive(isActive);
+ }
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index edca63e..6240e7f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -114,35 +114,37 @@
std::unique_ptr<VerifiedInputEvent> verifyInputEvent(const InputEvent& event) override;
void setFocusedApplication(
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) override;
- void setFocusedDisplay(int32_t displayId) override;
+ void setFocusedDisplay(ui::LogicalDisplayId displayId) override;
void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) override;
void setInputDispatchMode(bool enabled, bool frozen) override;
void setInputFilterEnabled(bool enabled) override;
bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
- int32_t displayId) override;
+ ui::LogicalDisplayId displayId) override;
void setMaximumObscuringOpacityForTouch(float opacity) override;
bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
bool isDragDrop = false) override;
- bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, int32_t displayId) override;
+ bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken,
+ ui::LogicalDisplayId displayId) override;
base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
void setFocusedWindow(const android::gui::FocusRequest&) override;
- base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
+ base::Result<std::unique_ptr<InputChannel>> createInputMonitor(ui::LogicalDisplayId displayId,
const std::string& name,
gui::Pid pid) override;
status_t removeInputChannel(const sp<IBinder>& connectionToken) override;
status_t pilferPointers(const sp<IBinder>& token) override;
void requestPointerCapture(const sp<IBinder>& windowToken, bool enabled) override;
bool flushSensor(int deviceId, InputDeviceSensorType sensorType) override;
- void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) override;
+ void setDisplayEligibilityForPointerCapture(ui::LogicalDisplayId displayId,
+ bool isEligible) override;
std::array<uint8_t, 32> sign(const VerifiedInputEvent& event) const;
- void displayRemoved(int32_t displayId) override;
+ void displayRemoved(ui::LogicalDisplayId displayId) override;
// Public because it's also used by tests to simulate the WindowInfosListener callback
void onWindowInfosChanged(const gui::WindowInfosUpdate&);
@@ -155,8 +157,10 @@
void setKeyRepeatConfiguration(std::chrono::nanoseconds timeout,
std::chrono::nanoseconds delay) override;
- bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
- int32_t pointerId) override;
+ bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId,
+ DeviceId deviceId, int32_t pointerId) override;
+
+ void setInputMethodConnectionIsActive(bool isActive) override;
private:
enum class DropReason {
@@ -247,17 +251,17 @@
std::shared_ptr<const EventEntry> mNextUnblockedEvent GUARDED_BY(mLock);
sp<android::gui::WindowInfoHandle> findTouchedWindowAtLocked(
- int32_t displayId, float x, float y, bool isStylus = false,
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus = false,
bool ignoreDragWindow = false) const REQUIRES(mLock);
std::vector<InputTarget> findOutsideTargetsLocked(
- int32_t displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
+ ui::LogicalDisplayId displayId, const sp<android::gui::WindowInfoHandle>& touchedWindow,
int32_t pointerId) const REQUIRES(mLock);
std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
- int32_t displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
+ ui::LogicalDisplayId displayId, float x, float y, bool isStylus) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
- REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(
+ ui::LogicalDisplayId displayId) const REQUIRES(mLock);
std::shared_ptr<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
@@ -281,7 +285,8 @@
std::optional<gui::Pid> findMonitorPidByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Input channels that will receive a copy of all input events sent to the provided display.
- std::unordered_map<int32_t, std::vector<Monitor>> mGlobalMonitorsByDisplay GUARDED_BY(mLock);
+ std::unordered_map<ui::LogicalDisplayId, std::vector<Monitor>> mGlobalMonitorsByDisplay
+ GUARDED_BY(mLock);
const HmacKeyManager mHmacKeyManager;
const std::array<uint8_t, 32> getSignature(const MotionEntry& motionEntry,
@@ -296,7 +301,9 @@
void transformMotionEntryForInjectionLocked(MotionEntry&,
const ui::Transform& injectedTransform) const
REQUIRES(mLock);
-
+ // Per-display correction of injected events
+ std::map<android::ui::LogicalDisplayId, InputVerifier> mInputFilterVerifiersByDisplay
+ GUARDED_BY(mLock);
std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(const EventEntry& entry);
void decrementPendingForegroundDispatches(const EventEntry& entry);
@@ -340,7 +347,8 @@
// This map is not really needed, but it helps a lot with debugging (dumpsys input).
// In the java layer, touch mode states are spread across multiple DisplayContent objects,
// making harder to snapshot and retrieve them.
- std::map<int32_t /*displayId*/, bool /*inTouchMode*/> mTouchModePerDisplay GUARDED_BY(mLock);
+ std::map<ui::LogicalDisplayId /*displayId*/, bool /*inTouchMode*/> mTouchModePerDisplay
+ GUARDED_BY(mLock);
class DispatcherWindowListener : public gui::WindowInfosListener {
public:
@@ -352,25 +360,26 @@
};
sp<gui::WindowInfosListener> mWindowInfoListener;
- std::unordered_map<int32_t /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>>
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/,
+ std::vector<sp<android::gui::WindowInfoHandle>>>
mWindowHandlesByDisplay GUARDED_BY(mLock);
- std::unordered_map<int32_t /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/, android::gui::DisplayInfo> mDisplayInfos
GUARDED_BY(mLock);
void setInputWindowsLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
- int32_t displayId) REQUIRES(mLock);
+ ui::LogicalDisplayId displayId) REQUIRES(mLock);
// Get a reference to window handles by display, return an empty vector if not found.
const std::vector<sp<android::gui::WindowInfoHandle>>& getWindowHandlesLocked(
- int32_t displayId) const REQUIRES(mLock);
- ui::Transform getTransformLocked(int32_t displayId) const REQUIRES(mLock);
+ ui::LogicalDisplayId displayId) const REQUIRES(mLock);
+ ui::Transform getTransformLocked(ui::LogicalDisplayId displayId) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
- const sp<IBinder>& windowHandleToken, std::optional<int32_t> displayId = {}) const
- REQUIRES(mLock);
+ const sp<IBinder>& windowHandleToken,
+ std::optional<ui::LogicalDisplayId> displayId = {}) const REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> getWindowHandleLocked(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
- sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(int displayId) const
- REQUIRES(mLock);
+ sp<android::gui::WindowInfoHandle> getFocusedWindowHandleLocked(
+ ui::LogicalDisplayId displayId) const REQUIRES(mLock);
bool canWindowReceiveMotionLocked(const sp<android::gui::WindowInfoHandle>& window,
const MotionEntry& motionEntry) const REQUIRES(mLock);
@@ -385,20 +394,21 @@
*/
void updateWindowHandlesForDisplayLocked(
const std::vector<sp<android::gui::WindowInfoHandle>>& inputWindowHandles,
- int32_t displayId) REQUIRES(mLock);
+ ui::LogicalDisplayId displayId) REQUIRES(mLock);
- std::unordered_map<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock);
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/, TouchState> mTouchStatesByDisplay
+ GUARDED_BY(mLock);
std::unique_ptr<DragState> mDragState GUARDED_BY(mLock);
void setFocusedApplicationLocked(
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) REQUIRES(mLock);
// Focused applications.
- std::unordered_map<int32_t, std::shared_ptr<InputApplicationHandle>>
+ std::unordered_map<ui::LogicalDisplayId /*displayId*/, std::shared_ptr<InputApplicationHandle>>
mFocusedApplicationHandlesByDisplay GUARDED_BY(mLock);
// Top focused display.
- int32_t mFocusedDisplayId GUARDED_BY(mLock);
+ ui::LogicalDisplayId mFocusedDisplayId GUARDED_BY(mLock);
// Keeps track of the focused window per display and determines focus changes.
FocusResolver mFocusResolver GUARDED_BY(mLock);
@@ -415,7 +425,8 @@
// Displays that are ineligible for pointer capture.
// TODO(b/214621487): Remove or move to a display flag.
- std::vector<int32_t> mIneligibleDisplaysForPointerCapture GUARDED_BY(mLock);
+ std::vector<ui::LogicalDisplayId /*displayId*/> mIneligibleDisplaysForPointerCapture
+ GUARDED_BY(mLock);
// Disable Pointer Capture as a result of loss of window focus.
void disablePointerCaptureForcedLocked() REQUIRES(mLock);
@@ -490,7 +501,7 @@
/**
* The displayId that the focused application is associated with.
*/
- int32_t mAwaitedApplicationDisplayId GUARDED_BY(mLock);
+ ui::LogicalDisplayId mAwaitedApplicationDisplayId GUARDED_BY(mLock);
void processNoFocusedWindowAnrLocked() REQUIRES(mLock);
/**
@@ -524,7 +535,7 @@
// shade is pulled down while we are counting down the timeout).
void resetNoFocusedWindowTimeoutLocked() REQUIRES(mLock);
- int32_t getTargetDisplayId(const EventEntry& entry);
+ ui::LogicalDisplayId getTargetDisplayId(const EventEntry& entry);
sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked(
nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime,
android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock);
@@ -549,13 +560,13 @@
std::bitset<MAX_POINTER_ID + 1> pointerIds,
std::optional<nsecs_t> firstDownTimeInTarget,
std::vector<InputTarget>& inputTargets) const REQUIRES(mLock);
- void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId)
- REQUIRES(mLock);
+ void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
+ ui::LogicalDisplayId displayId) REQUIRES(mLock);
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
// Enqueue a drag event if needed, and update the touch state.
// Uses findTouchedWindowTargetsLocked to make the decision
void addDragEventLocked(const MotionEntry& entry) REQUIRES(mLock);
- void finishDragAndDrop(int32_t displayId, float x, float y) REQUIRES(mLock);
+ void finishDragAndDrop(ui::LogicalDisplayId displayId, float x, float y) REQUIRES(mLock);
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
@@ -673,14 +684,14 @@
const std::string& reason) REQUIRES(mLock);
void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason)
REQUIRES(mLock);
- std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay;
+ std::map<ui::LogicalDisplayId /*displayId*/, InputVerifier> mVerifiersByDisplay;
// Returns a fallback KeyEntry that should be sent to the connection, if required.
std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable(
const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
const KeyEntry& keyEntry, bool handled) REQUIRES(mLock);
// Find touched state and touched window by token.
- std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/>
+ std::tuple<TouchState*, TouchedWindow*, ui::LogicalDisplayId /*displayId*/>
findTouchStateWindowAndDisplayLocked(const sp<IBinder>& token) REQUIRES(mLock);
// Statistics gathering.
@@ -699,14 +710,14 @@
void slipWallpaperTouch(ftl::Flags<InputTarget::Flags> targetFlags,
const sp<android::gui::WindowInfoHandle>& oldWindowHandle,
const sp<android::gui::WindowInfoHandle>& newWindowHandle,
- TouchState& state, int32_t deviceId,
+ TouchState& state, DeviceId deviceId,
const PointerProperties& pointerProperties,
std::vector<InputTarget>& targets) const REQUIRES(mLock);
void transferWallpaperTouch(ftl::Flags<InputTarget::Flags> oldTargetFlags,
ftl::Flags<InputTarget::Flags> newTargetFlags,
const sp<android::gui::WindowInfoHandle> fromWindowHandle,
const sp<android::gui::WindowInfoHandle> toWindowHandle,
- TouchState& state, int32_t deviceId,
+ TouchState& state, DeviceId deviceId,
const std::vector<PointerProperties>& pointers,
const std::unique_ptr<trace::EventTrackerInterface>& traceTracker)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 1fec9b7..46e7e8b 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -28,7 +28,8 @@
InputState::~InputState() {}
-bool InputState::isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const {
+bool InputState::isHovering(DeviceId deviceId, uint32_t source,
+ ui::LogicalDisplayId displayId) const {
for (const MotionMemento& memento : mMotionMementos) {
if (memento.deviceId == deviceId && memento.source == source &&
memento.displayId == displayId && memento.hovering) {
@@ -95,12 +96,14 @@
return true;
}
- if (!mMotionMementos.empty()) {
- const MotionMemento& lastMemento = mMotionMementos.back();
- if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
- !isStylusEvent(entry.source, entry.pointerProperties)) {
- // We already have a stylus stream, and the new event is not from stylus.
- return false;
+ if (!input_flags::enable_multi_device_same_window_stream()) {
+ if (!mMotionMementos.empty()) {
+ const MotionMemento& lastMemento = mMotionMementos.back();
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties) &&
+ !isStylusEvent(entry.source, entry.pointerProperties)) {
+ // We already have a stylus stream, and the new event is not from stylus.
+ return false;
+ }
}
}
@@ -336,33 +339,34 @@
// it's unlikely that those two streams would be consistent with each other. Therefore,
// cancel the previous gesture if the display id changes.
if (motionEntry.displayId != lastMemento.displayId) {
- LOG(INFO) << "Canceling stream: last displayId was "
- << inputEventSourceToString(lastMemento.displayId) << " and new event is "
- << motionEntry;
+ LOG(INFO) << "Canceling stream: last displayId was " << lastMemento.displayId
+ << " and new event is " << motionEntry;
return true;
}
return false;
}
- if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) {
- // A stylus is already active.
- if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
- actionMasked == AMOTION_EVENT_ACTION_DOWN) {
- // If this new event is from a different device, then cancel the old
- // stylus and allow the new stylus to take over, but only if it's going down.
- // Otherwise, they will start to race each other.
- return true;
+ if (!input_flags::enable_multi_device_same_window_stream()) {
+ if (isStylusEvent(lastMemento.source, lastMemento.pointerProperties)) {
+ // A stylus is already active.
+ if (isStylusEvent(motionEntry.source, motionEntry.pointerProperties) &&
+ actionMasked == AMOTION_EVENT_ACTION_DOWN) {
+ // If this new event is from a different device, then cancel the old
+ // stylus and allow the new stylus to take over, but only if it's going down.
+ // Otherwise, they will start to race each other.
+ return true;
+ }
+
+ // Keep the current stylus gesture.
+ return false;
}
- // Keep the current stylus gesture.
- return false;
- }
-
- // Cancel the current gesture if this is a start of a new gesture from a new device.
- if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
- actionMasked == AMOTION_EVENT_ACTION_HOVER_ENTER) {
- return true;
+ // Cancel the current gesture if this is a start of a new gesture from a new device.
+ if (actionMasked == AMOTION_EVENT_ACTION_DOWN ||
+ actionMasked == AMOTION_EVENT_ACTION_HOVER_ENTER) {
+ return true;
+ }
}
// By default, don't cancel any events.
return false;
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d49469d..ab83ef9 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -36,7 +36,7 @@
// Returns true if the specified source is known to have received a hover enter
// motion event.
- bool isHovering(DeviceId deviceId, uint32_t source, int32_t displayId) const;
+ bool isHovering(DeviceId deviceId, uint32_t source, ui::LogicalDisplayId displayId) const;
// Records tracking information for a key event that has just been published.
// Returns true if the event should be delivered, false if it is inconsistent
@@ -90,7 +90,7 @@
struct KeyMemento {
DeviceId deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
@@ -102,7 +102,7 @@
struct MotionMemento {
DeviceId deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
int32_t flags;
float xPrecision;
float yPrecision;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 35ad858..f9a2855 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -22,6 +22,8 @@
#include <inttypes.h>
#include <string>
+using android::base::Error;
+using android::base::Result;
using android::base::StringPrintf;
namespace android::inputdispatcher {
@@ -35,28 +37,29 @@
InputTarget::InputTarget(const std::shared_ptr<Connection>& connection, ftl::Flags<Flags> flags)
: connection(connection), flags(flags) {}
-void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
- const ui::Transform& transform) {
+Result<void> InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
+ const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
// valid pointer property from the input event.
if (newPointerIds.none()) {
setDefaultPointerTransform(transform);
- return;
+ return {};
}
// Ensure that the new set of pointers doesn't overlap with the current set of pointers.
if ((getPointerIds() & newPointerIds).any()) {
- LOG(FATAL) << __func__ << " - overlap with incoming pointers "
- << bitsetToString(newPointerIds) << " in " << *this;
+ return Error() << __func__ << " - overlap with incoming pointers "
+ << bitsetToString(newPointerIds) << " in " << *this;
}
for (auto& [existingTransform, existingPointers] : mPointerTransforms) {
if (transform == existingTransform) {
existingPointers |= newPointerIds;
- return;
+ return {};
}
}
mPointerTransforms.emplace_back(transform, newPointerIds);
+ return {};
}
void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 058639a..90374f1 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -18,7 +18,6 @@
#include <ftl/flags.h>
#include <gui/WindowInfo.h>
-#include <gui/constants.h>
#include <ui/Transform.h>
#include <utils/BitSet.h>
#include <bitset>
@@ -92,7 +91,8 @@
InputTarget() = default;
InputTarget(const std::shared_ptr<Connection>&, ftl::Flags<Flags> = {});
- void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform);
+ android::base::Result<void> addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds,
+ const ui::Transform& transform);
void setDefaultPointerTransform(const ui::Transform& transform);
/**
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index f8aa625..0c9ad3c 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -70,14 +70,14 @@
});
}
-void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode,
- ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget) {
+android::base::Result<void> TouchState::addOrUpdateWindow(
+ const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode,
+ ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
+ const std::vector<PointerProperties>& touchingPointers,
+ std::optional<nsecs_t> firstDownTimeInTarget) {
if (touchingPointers.empty()) {
LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName();
- return;
+ return android::base::Error();
}
for (TouchedWindow& touchedWindow : windows) {
// We do not compare windows by token here because two windows that share the same token
@@ -91,11 +91,12 @@
// For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have
// downTime set initially. Need to update existing window when a pointer is down for the
// window.
- touchedWindow.addTouchingPointers(deviceId, touchingPointers);
+ android::base::Result<void> addResult =
+ touchedWindow.addTouchingPointers(deviceId, touchingPointers);
if (firstDownTimeInTarget) {
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
- return;
+ return addResult;
}
}
TouchedWindow touchedWindow;
@@ -107,6 +108,7 @@
touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget);
}
windows.push_back(touchedWindow);
+ return {};
}
void TouchState::addHoveringPointerToWindow(const sp<WindowInfoHandle>& windowHandle,
@@ -178,9 +180,11 @@
clearWindowsWithoutPointers();
}
-sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows[i];
+sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle(DeviceId deviceId) const {
+ for (const auto& window : windows) {
+ if (!window.hasTouchingPointers(deviceId)) {
+ continue;
+ }
if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
return window.windowHandle;
}
@@ -188,10 +192,13 @@
return nullptr;
}
-bool TouchState::isSlippery() const {
+bool TouchState::isSlippery(DeviceId deviceId) const {
// Must have exactly one foreground window.
bool haveSlipperyForegroundWindow = false;
for (const TouchedWindow& window : windows) {
+ if (!window.hasTouchingPointers(deviceId)) {
+ continue;
+ }
if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
if (haveSlipperyForegroundWindow ||
!window.windowHandle->getInfo()->inputConfig.test(
@@ -204,9 +211,11 @@
return haveSlipperyForegroundWindow;
}
-sp<WindowInfoHandle> TouchState::getWallpaperWindow() const {
- for (size_t i = 0; i < windows.size(); i++) {
- const TouchedWindow& window = windows[i];
+sp<WindowInfoHandle> TouchState::getWallpaperWindow(DeviceId deviceId) const {
+ for (const auto& window : windows) {
+ if (!window.hasTouchingPointers(deviceId)) {
+ continue;
+ }
if (window.windowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::IS_WALLPAPER)) {
return window.windowHandle;
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 3d534bc..9d4bb3d 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -43,11 +43,11 @@
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointerFromWindow(DeviceId deviceId, int32_t pointerId,
const sp<android::gui::WindowInfoHandle>& windowHandle);
- void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
- InputTarget::DispatchMode dispatchMode,
- ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId,
- const std::vector<PointerProperties>& touchingPointers,
- std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
+ android::base::Result<void> addOrUpdateWindow(
+ const sp<android::gui::WindowInfoHandle>& windowHandle,
+ InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags,
+ DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers,
+ std::optional<nsecs_t> firstDownTimeInTarget = std::nullopt);
void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
DeviceId deviceId, const PointerProperties& pointer);
void removeHoveringPointer(DeviceId deviceId, int32_t pointerId);
@@ -64,9 +64,9 @@
// set to false.
void cancelPointersForNonPilferingWindows();
- sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
- bool isSlippery() const;
- sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
+ sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle(DeviceId deviceId) const;
+ bool isSlippery(DeviceId deviceId) const;
+ sp<android::gui::WindowInfoHandle> getWallpaperWindow(DeviceId deviceId) const;
const TouchedWindow& getTouchedWindow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const;
// Whether any of the windows are currently being touched
diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp
index 037d7c8..1f86f66 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.cpp
+++ b/services/inputflinger/dispatcher/TouchedWindow.cpp
@@ -20,6 +20,7 @@
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
+using android::base::Result;
using android::base::StringPrintf;
namespace android {
@@ -89,8 +90,8 @@
hoveringPointers.push_back(pointer);
}
-void TouchedWindow::addTouchingPointers(DeviceId deviceId,
- const std::vector<PointerProperties>& pointers) {
+Result<void> TouchedWindow::addTouchingPointers(DeviceId deviceId,
+ const std::vector<PointerProperties>& pointers) {
std::vector<PointerProperties>& touchingPointers = mDeviceStates[deviceId].touchingPointers;
const size_t initialSize = touchingPointers.size();
for (const PointerProperties& pointer : pointers) {
@@ -98,11 +99,14 @@
return properties.id == pointer.id;
});
}
- if (touchingPointers.size() != initialSize) {
+ const bool foundInconsistentState = touchingPointers.size() != initialSize;
+ touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end());
+ if (foundInconsistentState) {
LOG(ERROR) << __func__ << ": " << dumpVector(pointers, streamableToString) << ", device "
<< deviceId << " already in " << *this;
+ return android::base::Error();
}
- touchingPointers.insert(touchingPointers.end(), pointers.begin(), pointers.end());
+ return {};
}
bool TouchedWindow::hasTouchingPointers() const {
diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h
index 0d1531f..4f0ad16 100644
--- a/services/inputflinger/dispatcher/TouchedWindow.h
+++ b/services/inputflinger/dispatcher/TouchedWindow.h
@@ -46,7 +46,8 @@
bool hasTouchingPointers() const;
bool hasTouchingPointers(DeviceId deviceId) const;
std::vector<PointerProperties> getTouchingPointers(DeviceId deviceId) const;
- void addTouchingPointers(DeviceId deviceId, const std::vector<PointerProperties>& pointers);
+ android::base::Result<void> addTouchingPointers(DeviceId deviceId,
+ const std::vector<PointerProperties>& pointers);
void removeTouchingPointer(DeviceId deviceId, int32_t pointerId);
void removeTouchingPointers(DeviceId deviceId, std::bitset<MAX_POINTER_ID + 1> pointers);
bool hasActiveStylus() const;
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 7c440e8..653f595 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -92,14 +92,14 @@
* This method may be called on any thread (usually by the input manager).
*/
virtual void setFocusedApplication(
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) = 0;
/* Sets the focused display.
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual void setFocusedDisplay(int32_t displayId) = 0;
+ virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
/** Sets the minimum time between user activity pokes. */
virtual void setMinTimeBetweenUserActivityPokes(std::chrono::milliseconds interval) = 0;
@@ -130,7 +130,7 @@
* Returns true when changing touch mode state.
*/
virtual bool setInTouchMode(bool inTouchMode, gui::Pid pid, gui::Uid uid, bool hasPermission,
- int32_t displayId) = 0;
+ ui::LogicalDisplayId displayId) = 0;
/**
* Sets the maximum allowed obscuring opacity by UID to propagate touches.
@@ -156,7 +156,8 @@
* Returns true on success, false if there was no on-going touch on the display.
* @deprecated
*/
- virtual bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, int32_t displayId) = 0;
+ virtual bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken,
+ ui::LogicalDisplayId displayId) = 0;
/**
* Sets focus on the specified window.
@@ -179,9 +180,8 @@
*
* This method may be called on any thread (usually by the input manager).
*/
- virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(int32_t displayId,
- const std::string& name,
- gui::Pid pid) = 0;
+ virtual base::Result<std::unique_ptr<InputChannel>> createInputMonitor(
+ ui::LogicalDisplayId displayId, const std::string& name, gui::Pid pid) = 0;
/* Removes input channels that will no longer receive input events.
*
@@ -207,7 +207,8 @@
* ineligible, all attempts to request pointer capture for windows on that display will fail.
* TODO(b/214621487): Remove or move to a display flag.
*/
- virtual void setDisplayEligibilityForPointerCapture(int displayId, bool isEligible) = 0;
+ virtual void setDisplayEligibilityForPointerCapture(ui::LogicalDisplayId displayId,
+ bool isEligible) = 0;
/* Flush input device motion sensor.
*
@@ -218,7 +219,7 @@
/**
* Called when a display has been removed from the system.
*/
- virtual void displayRemoved(int32_t displayId) = 0;
+ virtual void displayRemoved(ui::LogicalDisplayId displayId) = 0;
/*
* Abort the current touch stream.
@@ -234,8 +235,13 @@
/*
* Determine if a pointer from a device is being dispatched to the given window.
*/
- virtual bool isPointerInWindow(const sp<IBinder>& token, int32_t displayId, DeviceId deviceId,
- int32_t pointerId) = 0;
+ virtual bool isPointerInWindow(const sp<IBinder>& token, ui::LogicalDisplayId displayId,
+ DeviceId deviceId, int32_t pointerId) = 0;
+
+ /*
+ * Notify the dispatcher that the state of the input method connection changed.
+ */
+ virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
};
} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
index 91a3e3f..0f03620 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -99,8 +99,9 @@
* This method is expected to set the POLICY_FLAG_PASS_TO_USER policy flag if the event
* should be dispatched to applications.
*/
- virtual void interceptMotionBeforeQueueing(int32_t displayId, uint32_t source, int32_t action,
- nsecs_t when, uint32_t& policyFlags) = 0;
+ virtual void interceptMotionBeforeQueueing(ui::LogicalDisplayId displayId, uint32_t source,
+ int32_t action, nsecs_t when,
+ uint32_t& policyFlags) = 0;
/* Allows the policy a chance to intercept a key before dispatching. */
virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token,
@@ -119,7 +120,8 @@
uint32_t policyFlags) = 0;
/* Poke user activity for an event dispatched to a window. */
- virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0;
+ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType,
+ ui::LogicalDisplayId displayId) = 0;
/*
* Return true if the provided event is stale, and false otherwise. Used for determining
@@ -163,9 +165,6 @@
virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) = 0;
- /* Get the UID associated with the given package. */
- virtual gui::Uid getPackageUid(std::string package) = 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,
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index c431fb7..2d7554c 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -47,7 +47,7 @@
outProto.set_source(event.source);
outProto.set_action(event.action);
outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId);
+ outProto.set_display_id(event.displayId.val());
outProto.set_classification(static_cast<int32_t>(event.classification));
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
@@ -88,7 +88,7 @@
outProto.set_source(event.source);
outProto.set_action(event.action);
outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId);
+ outProto.set_display_id(event.displayId.val());
outProto.set_repeat_count(event.repeatCount);
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 55ed5c6..a1a87af 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -61,10 +61,10 @@
e.downTime, e.flags, e.repeatCount, type};
}
-void writeEventToBackend(const TracedEvent& event, const TracedEventArgs args,
+void writeEventToBackend(const TracedEvent& event, const TracedEventMetadata metadata,
InputTracingBackendInterface& backend) {
- std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e, args); },
- [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e, args); }},
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e, metadata); },
+ [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e, metadata); }},
event);
}
@@ -86,8 +86,10 @@
// This is a global monitor, assume its target is the system.
return {.uid = gui::Uid{AID_SYSTEM}, .isSecureWindow = false};
}
- return {target.windowHandle->getInfo()->ownerUid,
- target.windowHandle->getInfo()->layoutParamsFlags.test(gui::WindowInfo::Flag::SECURE)};
+ const auto& info = *target.windowHandle->getInfo();
+ const bool isSensitiveTarget =
+ info.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY);
+ return {target.windowHandle->getInfo()->ownerUid, isSensitiveTarget};
}
} // namespace
@@ -126,22 +128,21 @@
const InputTargetInfo& targetInfo = getTargetInfo(target);
if (eventState->isEventProcessingComplete) {
// Disallow adding new targets after eventProcessingComplete() is called.
- if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+ if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
LOG(FATAL) << __func__ << ": Cannot add new target after eventProcessingComplete";
}
return;
}
if (isDerivedCookie(cookie)) {
// Disallow adding new targets from a derived cookie.
- if (eventState->targets.find(targetInfo.uid) == eventState->targets.end()) {
+ if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
LOG(FATAL) << __func__ << ": Cannot add new target from a derived cookie";
}
return;
}
- eventState->targets.emplace(targetInfo.uid);
- eventState->isSecure |= targetInfo.isSecureWindow;
- // TODO(b/210460522): Set events as sensitive when the IME connection is active.
+ eventState->metadata.targets.emplace(targetInfo.uid);
+ eventState->metadata.isSecure |= targetInfo.isSecureWindow;
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
@@ -177,9 +178,7 @@
// is dispatched, such as in the case of key fallback events. To account for these cases,
// derived events can be traced after the processing is complete for the original event.
const auto& event = eventState->events.back();
- const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
- .targets = eventState->targets};
- writeEventToBackend(event, std::move(traceArgs), *mBackend);
+ writeEventToBackend(event, eventState->metadata, *mBackend);
}
return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
}
@@ -206,7 +205,7 @@
<< ": Failed to find a previously traced event that matches the dispatched event";
}
- if (eventState->targets.count(dispatchEntry.targetUid) == 0) {
+ if (eventState->metadata.targets.count(dispatchEntry.targetUid) == 0) {
LOG(FATAL) << __func__ << ": Event is being dispatched to UID that it is not targeting";
}
@@ -226,9 +225,7 @@
/*hmac=*/{},
resolvedKeyRepeatCount};
if (eventState->isEventProcessingComplete) {
- const TracedEventArgs traceArgs{.isSecure = eventState->isSecure,
- .targets = eventState->targets};
- mBackend->traceWindowDispatch(std::move(windowDispatchArgs), std::move(traceArgs));
+ mBackend->traceWindowDispatch(std::move(windowDispatchArgs), eventState->metadata);
} else {
eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs));
}
@@ -246,10 +243,11 @@
// --- InputTracer::EventState ---
void InputTracer::EventState::onEventProcessingComplete() {
+ metadata.isImeConnectionActive = tracer.mIsImeConnectionActive;
+
// Write all of the events known so far to the trace.
for (const auto& event : events) {
- const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
- writeEventToBackend(event, traceArgs, *tracer.mBackend);
+ writeEventToBackend(event, metadata, *tracer.mBackend);
}
// Write all pending dispatch args to the trace.
for (const auto& windowDispatchArgs : pendingDispatchArgs) {
@@ -263,8 +261,7 @@
<< ": Failed to find a previously traced event that matches the dispatched "
"event";
}
- const TracedEventArgs traceArgs{.isSecure = isSecure, .targets = targets};
- tracer.mBackend->traceWindowDispatch(windowDispatchArgs, std::move(traceArgs));
+ tracer.mBackend->traceWindowDispatch(windowDispatchArgs, metadata);
}
pendingDispatchArgs.clear();
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 717bc1f..ab175be 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -48,9 +48,13 @@
std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&,
const EventTrackerInterface&) override;
void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) override;
+ void setInputMethodConnectionIsActive(bool isActive) override {
+ mIsImeConnectionActive = isActive;
+ }
private:
std::unique_ptr<InputTracingBackendInterface> mBackend;
+ bool mIsImeConnectionActive{false};
// The state of a tracked event, shared across all events derived from the original event.
struct EventState {
@@ -64,10 +68,8 @@
bool isEventProcessingComplete{false};
// A queue to hold dispatch args from being traced until event processing is complete.
std::vector<const WindowDispatchArgs> pendingDispatchArgs;
- // True if the event is targeting at least one secure window;
- bool isSecure{false};
- // The list of all possible UIDs that this event could be targeting.
- std::set<gui::Uid> targets;
+ // The metadata should not be modified after event processing is complete.
+ TracedEventMetadata metadata{};
};
// Get the event state associated with a tracking cookie.
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index 609d10c..af6eefb 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -103,6 +103,11 @@
* provided.
*/
virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface&) = 0;
+
+ /**
+ * Notify that the state of the input method connection changed.
+ */
+ virtual void setInputMethodConnectionIsActive(bool isActive) = 0;
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
index 3ff7fab..25099c3 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -50,7 +50,7 @@
uint32_t policyFlags;
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
int32_t action;
int32_t keyCode;
int32_t scanCode;
@@ -70,7 +70,7 @@
uint32_t policyFlags;
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
int32_t action;
int32_t actionButton;
int32_t flags;
@@ -92,11 +92,13 @@
using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>;
/** Additional information about an input event being traced. */
-struct TracedEventArgs {
+struct TracedEventMetadata {
// True if the event is targeting at least one secure window.
bool isSecure;
// The list of possible UIDs that this event could be targeting.
std::set<gui::Uid> targets;
+ // True if the there was an active input method connection while this event was processed.
+ bool isImeConnectionActive;
};
/** Additional information about an input event being dispatched to a window. */
@@ -121,13 +123,13 @@
virtual ~InputTracingBackendInterface() = default;
/** Trace a KeyEvent. */
- virtual void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) = 0;
+ virtual void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) = 0;
/** Trace a MotionEvent. */
- virtual void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) = 0;
+ virtual void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) = 0;
/** Trace an event being sent to a window. */
- virtual void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) = 0;
+ virtual void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) = 0;
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
index b76bec3..9b9633a 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -21,8 +21,10 @@
#include "AndroidInputEventProtoConverter.h"
#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
#include <perfetto/trace/android/android_input_event.pbzero.h>
#include <private/android_filesystem_config.h>
+#include <utils/String16.h>
namespace android::inputdispatcher::trace::impl {
@@ -41,6 +43,34 @@
}
}
+sp<content::pm::IPackageManagerNative> getPackageManager() {
+ sp<IServiceManager> serviceManager = defaultServiceManager();
+ if (!serviceManager) {
+ LOG(ERROR) << __func__ << ": unable to access native ServiceManager";
+ return nullptr;
+ }
+
+ sp<IBinder> binder = serviceManager->waitForService(String16("package_native"));
+ auto packageManager = interface_cast<content::pm::IPackageManagerNative>(binder);
+ if (!packageManager) {
+ LOG(ERROR) << ": unable to access native PackageManager";
+ return nullptr;
+ }
+ return packageManager;
+}
+
+gui::Uid getPackageUid(const sp<content::pm::IPackageManagerNative>& pm,
+ const std::string& package) {
+ int32_t outUid = -1;
+ if (auto status = pm->getPackageUid(package, /*flags=*/0, AID_SYSTEM, &outUid);
+ !status.isOk()) {
+ LOG(INFO) << "Failed to get package UID from native package manager for package '"
+ << package << "': " << status;
+ return gui::Uid::INVALID;
+ }
+ return gui::Uid{static_cast<uid_t>(outUid)};
+}
+
} // namespace
// --- PerfettoBackend::InputEventDataSource ---
@@ -67,18 +97,24 @@
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); });
}
-void PerfettoBackend::InputEventDataSource::initializeUidMap(GetPackageUid getPackageUid) {
+void PerfettoBackend::InputEventDataSource::initializeUidMap() {
if (mUidMap.has_value()) {
return;
}
mUidMap = {{}};
+ auto packageManager = PerfettoBackend::sPackageManagerProvider();
+ if (!packageManager) {
+ LOG(ERROR) << "Failed to initialize UID map: Could not get native package manager";
+ return;
+ }
+
for (const auto& rule : mConfig.rules) {
for (const auto& package : rule.matchAllPackages) {
- mUidMap->emplace(package, getPackageUid(package));
+ mUidMap->emplace(package, getPackageUid(packageManager, package));
}
for (const auto& package : rule.matchAnyPackages) {
- mUidMap->emplace(package, getPackageUid(package));
+ mUidMap->emplace(package, getPackageUid(packageManager, package));
}
}
}
@@ -98,10 +134,10 @@
}
TraceLevel PerfettoBackend::InputEventDataSource::resolveTraceLevel(
- const TracedEventArgs& args) const {
+ const TracedEventMetadata& metadata) const {
// Check for matches with the rules in the order that they are defined.
for (const auto& rule : mConfig.rules) {
- if (ruleMatches(rule, args)) {
+ if (ruleMatches(rule, metadata)) {
return rule.level;
}
}
@@ -110,17 +146,23 @@
}
bool PerfettoBackend::InputEventDataSource::ruleMatches(const TraceRule& rule,
- const TracedEventArgs& args) const {
+ const TracedEventMetadata& metadata) const {
// By default, a rule will match all events. Return early if the rule does not match.
// Match the event if it is directed to a secure window.
- if (rule.matchSecure.has_value() && *rule.matchSecure != args.isSecure) {
+ if (rule.matchSecure.has_value() && *rule.matchSecure != metadata.isSecure) {
+ return false;
+ }
+
+ // Match the event if it was processed while there was an active InputMethod connection.
+ if (rule.matchImeConnectionActive.has_value() &&
+ *rule.matchImeConnectionActive != metadata.isImeConnectionActive) {
return false;
}
// Match the event if all of its target packages are explicitly allowed in the "match all" list.
if (!rule.matchAllPackages.empty() &&
- !std::all_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+ !std::all_of(metadata.targets.begin(), metadata.targets.end(), [&](const auto& uid) {
return isPermanentlyAllowed(uid) ||
std::any_of(rule.matchAllPackages.begin(), rule.matchAllPackages.end(),
[&](const auto& pkg) { return uid == mUidMap->at(pkg); });
@@ -130,7 +172,7 @@
// Match the event if any of its target packages are allowed in the "match any" list.
if (!rule.matchAnyPackages.empty() &&
- !std::any_of(args.targets.begin(), args.targets.end(), [&](const auto& uid) {
+ !std::any_of(metadata.targets.begin(), metadata.targets.end(), [&](const auto& uid) {
return std::any_of(rule.matchAnyPackages.begin(), rule.matchAnyPackages.end(),
[&](const auto& pkg) { return uid == mUidMap->at(pkg); });
})) {
@@ -143,17 +185,22 @@
// --- PerfettoBackend ---
+bool PerfettoBackend::sUseInProcessBackendForTest{false};
+
+std::function<sp<content::pm::IPackageManagerNative>()> PerfettoBackend::sPackageManagerProvider{
+ &getPackageManager};
+
std::once_flag PerfettoBackend::sDataSourceRegistrationFlag{};
std::atomic<int32_t> PerfettoBackend::sNextInstanceId{1};
-PerfettoBackend::PerfettoBackend(GetPackageUid getPackagesForUid)
- : mGetPackageUid(getPackagesForUid) {
+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;
+ args.backends = sUseInProcessBackendForTest ? perfetto::kInProcessBackend
+ : perfetto::kSystemBackend;
perfetto::Tracing::Initialize(args);
// Register our custom data source for input event tracing.
@@ -166,14 +213,17 @@
}
void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event,
- const TracedEventArgs& args) {
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto dataSource = ctx.GetDataSourceLocked();
- dataSource->initializeUidMap(mGetPackageUid);
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
return;
}
- const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
return;
}
@@ -186,14 +236,18 @@
});
}
-void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, const TracedEventArgs& args) {
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event,
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto dataSource = ctx.GetDataSourceLocked();
- dataSource->initializeUidMap(mGetPackageUid);
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
return;
}
- const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
return;
}
@@ -207,14 +261,17 @@
}
void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
- const TracedEventArgs& args) {
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
auto dataSource = ctx.GetDataSourceLocked();
- dataSource->initializeUidMap(mGetPackageUid);
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
if (!dataSource->getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH)) {
return;
}
- const TraceLevel traceLevel = dataSource->resolveTraceLevel(args);
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
return;
}
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index af1c6b7..d0bab06 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -20,6 +20,7 @@
#include "InputTracingPerfettoBackendConfig.h"
+#include <android/content/pm/IPackageManagerNative.h>
#include <ftl/flags.h>
#include <perfetto/tracing.h>
#include <mutex>
@@ -49,18 +50,20 @@
*/
class PerfettoBackend : public InputTracingBackendInterface {
public:
- using GetPackageUid = std::function<gui::Uid(std::string)>;
+ static bool sUseInProcessBackendForTest;
+ static std::function<sp<content::pm::IPackageManagerNative>()> sPackageManagerProvider;
- explicit PerfettoBackend(GetPackageUid);
+ explicit PerfettoBackend();
~PerfettoBackend() override = default;
- void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) override;
- void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) override;
- void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) override;
+ void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) override;
+ void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) override;
+ void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) override;
private:
// Implementation of the perfetto data source.
// Each instance of the InputEventDataSource represents a different tracing session.
+ // Its lifecycle is controlled by perfetto.
class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
public:
explicit InputEventDataSource();
@@ -69,24 +72,20 @@
void OnStart(const StartArgs&) override;
void OnStop(const StopArgs&) override;
- void initializeUidMap(GetPackageUid);
+ void initializeUidMap();
bool shouldIgnoreTracedInputEvent(const EventType&) const;
inline ftl::Flags<TraceFlag> getFlags() const { return mConfig.flags; }
- TraceLevel resolveTraceLevel(const TracedEventArgs&) const;
+ TraceLevel resolveTraceLevel(const TracedEventMetadata&) const;
private:
const int32_t mInstanceId;
TraceConfig mConfig;
- bool ruleMatches(const TraceRule&, const TracedEventArgs&) const;
+ bool ruleMatches(const TraceRule&, const TracedEventMetadata&) const;
std::optional<std::map<std::string, gui::Uid>> mUidMap;
};
- // TODO(b/330360505): Query the native package manager directly from the data source,
- // and remove this.
- GetPackageUid mGetPackageUid;
-
static std::once_flag sDataSourceRegistrationFlag;
static std::atomic<int32_t> sNextInstanceId;
};
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
index 1614789..3c3c15a 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -54,25 +54,28 @@
template <typename Backend>
void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event,
- const TracedEventArgs& traceArgs) {
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(event, traceArgs);
+ mQueue.emplace_back(event, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event,
- const TracedEventArgs& traceArgs) {
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(event, traceArgs);
+ mQueue.emplace_back(event, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
- const TracedEventArgs& traceArgs) {
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(dispatchArgs, traceArgs);
+ mQueue.emplace_back(dispatchArgs, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
@@ -84,10 +87,15 @@
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
+ if (mQueue.empty()) {
+ setIdleStatus(true);
+ }
+
// Wait until we need to process more events or exit.
mThreadWakeCondition.wait(lock,
[&]() REQUIRES(mLock) { return mThreadExit || !mQueue.empty(); });
if (mThreadExit) {
+ setIdleStatus(true);
return;
}
@@ -109,6 +117,36 @@
entries.clear();
}
+template <typename Backend>
+std::function<void()> ThreadedBackend<Backend>::getIdleWaiterForTesting() {
+ std::scoped_lock lock(mLock);
+ if (!mIdleWaiter) {
+ mIdleWaiter = std::make_shared<IdleWaiter>();
+ }
+
+ // Return a lambda that holds a strong reference to the idle waiter, whose lifetime can extend
+ // beyond this threaded backend object.
+ return [idleWaiter = mIdleWaiter]() {
+ std::unique_lock idleLock(idleWaiter->idleLock);
+ base::ScopedLockAssertion assumeLocked(idleWaiter->idleLock);
+ idleWaiter->threadIdleCondition.wait(idleLock, [&]() REQUIRES(idleWaiter->idleLock) {
+ return idleWaiter->isIdle;
+ });
+ };
+}
+
+template <typename Backend>
+void ThreadedBackend<Backend>::setIdleStatus(bool isIdle) {
+ if (!mIdleWaiter) {
+ return;
+ }
+ std::scoped_lock idleLock(mIdleWaiter->idleLock);
+ mIdleWaiter->isIdle = isIdle;
+ if (isIdle) {
+ mIdleWaiter->threadIdleCondition.notify_all();
+ }
+}
+
// Explicit template instantiation for the PerfettoBackend.
template class ThreadedBackend<PerfettoBackend>;
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.h b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
index 3bfc72b..52a84c4 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -38,9 +38,12 @@
ThreadedBackend(Backend&& innerBackend);
~ThreadedBackend() override;
- void traceKeyEvent(const TracedKeyEvent&, const TracedEventArgs&) override;
- void traceMotionEvent(const TracedMotionEvent&, const TracedEventArgs&) override;
- void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventArgs&) override;
+ void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) override;
+ void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) override;
+ void traceWindowDispatch(const WindowDispatchArgs&, const TracedEventMetadata&) override;
+
+ /** Returns a function that, when called, will block until the tracing thread is idle. */
+ std::function<void()> getIdleWaiterForTesting();
private:
std::mutex mLock;
@@ -49,15 +52,24 @@
Backend mBackend;
using TraceEntry =
std::pair<std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>,
- TracedEventArgs>;
+ TracedEventMetadata>;
std::vector<TraceEntry> mQueue GUARDED_BY(mLock);
+ struct IdleWaiter {
+ std::mutex idleLock;
+ std::condition_variable threadIdleCondition;
+ bool isIdle GUARDED_BY(idleLock){false};
+ };
+ // The lazy-initialized object used to wait for the tracing thread to idle.
+ std::shared_ptr<IdleWaiter> mIdleWaiter GUARDED_BY(mLock);
+
// InputThread stops when its destructor is called. Initialize it last so that it is the
// first thing to be destructed. This will guarantee the thread will not access other
// members that have already been destructed.
InputThread mTracerThread;
void threadLoop();
+ void setIdleStatus(bool isIdle) REQUIRES(mLock);
};
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 79c8a4b..e5c3aa0 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -61,9 +61,6 @@
// The display size or orientation changed.
DISPLAY_INFO = 1u << 2,
- // The visible touches option changed.
- SHOW_TOUCHES = 1u << 3,
-
// The keyboard layouts must be reloaded.
KEYBOARD_LAYOUTS = 1u << 4,
@@ -111,9 +108,13 @@
// Used to determine which DisplayViewport should be tied to which InputDevice.
std::unordered_map<std::string, uint8_t> portAssociations;
- // The associations between input device physical port locations and display unique ids.
+ // The associations between input device ports and display unique ids.
// Used to determine which DisplayViewport should be tied to which InputDevice.
- std::unordered_map<std::string, std::string> uniqueIdAssociations;
+ std::unordered_map<std::string, std::string> uniqueIdAssociationsByPort;
+
+ // The associations between input device descriptor and display unique ids.
+ // Used to determine which DisplayViewport should be tied to which InputDevice.
+ std::unordered_map<std::string, std::string> uniqueIdAssociationsByDescriptor;
// The associations between input device ports device types.
// This is used to determine which device type and source should be tied to which InputDevice.
@@ -124,7 +125,7 @@
std::unordered_map<std::string, KeyboardLayoutInfo> keyboardLayoutAssociations;
// The suggested display ID to show the cursor.
- int32_t defaultPointerDisplayId;
+ ui::LogicalDisplayId defaultPointerDisplayId;
// The mouse pointer speed, as a number from -7 (slowest) to 7 (fastest).
//
@@ -134,7 +135,7 @@
// Displays on which an acceleration curve shouldn't be applied for pointer movements from mice.
//
// Currently only used when the enable_new_mouse_pointer_ballistics flag is enabled.
- std::set<int32_t> displaysWithMousePointerAccelerationDisabled;
+ std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled;
// Velocity control parameters for mouse pointer movements.
//
@@ -210,9 +211,6 @@
// will cover this portion of the display diagonal.
float pointerGestureZoomSpeedRatio;
- // True to show the location of touches on the touch screen as spots.
- bool showTouches;
-
// The latest request to enable or disable Pointer Capture.
PointerCaptureRequest pointerCaptureRequest;
@@ -245,6 +243,7 @@
InputReaderConfiguration()
: virtualKeyQuietTime(0),
+ defaultPointerDisplayId(ui::ADISPLAY_ID_DEFAULT),
mousePointerSpeed(0),
displaysWithMousePointerAccelerationDisabled(),
pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f,
@@ -264,7 +263,6 @@
pointerGestureSwipeMaxWidthRatio(0.25f),
pointerGestureMovementSpeedRatio(0.8f),
pointerGestureZoomSpeedRatio(0.3f),
- showTouches(false),
pointerCaptureRequest(),
touchpadPointerSpeed(0),
touchpadNaturalScrollingEnabled(true),
@@ -278,7 +276,7 @@
std::optional<DisplayViewport> getDisplayViewportByUniqueId(const std::string& uniqueDisplayId)
const;
std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t physicalPort) const;
- std::optional<DisplayViewport> getDisplayViewportById(int32_t displayId) const;
+ std::optional<DisplayViewport> getDisplayViewportById(ui::LogicalDisplayId displayId) const;
void setDisplayViewports(const std::vector<DisplayViewport>& viewports);
void dump(std::string& dump) const;
@@ -369,7 +367,7 @@
virtual std::vector<InputDeviceSensorInfo> getSensors(int32_t deviceId) = 0;
/* Return true if the device can send input events to the specified display. */
- virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
+ virtual bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) = 0;
/* Enable sensor in input reader mapper. */
virtual bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
@@ -445,10 +443,6 @@
/* Gets the input reader configuration. */
virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0;
- /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */
- virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(
- int32_t deviceId) = 0;
-
/* Notifies the input reader policy that some input devices have changed
* and provides information about all current input devices.
*/
@@ -478,7 +472,7 @@
* be used as the range of possible values for pointing devices, like mice and touchpads.
*/
virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
- int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0;
+ ui::LogicalDisplayId associatedDisplayId = ui::ADISPLAY_ID_NONE) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 736b1e0..865f3d0 100644
--- a/services/inputflinger/include/NotifyArgs.h
+++ b/services/inputflinger/include/NotifyArgs.h
@@ -61,7 +61,7 @@
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
uint32_t policyFlags;
int32_t action;
int32_t flags;
@@ -74,9 +74,9 @@
inline NotifyKeyArgs() {}
NotifyKeyArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
- uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState,
- nsecs_t downTime);
+ uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
+ int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode,
+ int32_t metaState, nsecs_t downTime);
bool operator==(const NotifyKeyArgs& rhs) const = default;
@@ -91,7 +91,7 @@
int32_t deviceId;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
uint32_t policyFlags;
int32_t action;
int32_t actionButton;
@@ -123,12 +123,12 @@
inline NotifyMotionArgs() {}
NotifyMotionArgs(int32_t id, nsecs_t eventTime, nsecs_t readTime, int32_t deviceId,
- uint32_t source, int32_t displayId, uint32_t policyFlags, int32_t action,
- int32_t actionButton, int32_t flags, int32_t metaState, int32_t buttonState,
- MotionClassification classification, int32_t edgeFlags, uint32_t pointerCount,
- const PointerProperties* pointerProperties, const PointerCoords* pointerCoords,
- float xPrecision, float yPrecision, float xCursorPosition,
- float yCursorPosition, nsecs_t downTime,
+ uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
+ int32_t action, int32_t actionButton, int32_t flags, int32_t metaState,
+ int32_t buttonState, MotionClassification classification, int32_t edgeFlags,
+ uint32_t pointerCount, const PointerProperties* pointerProperties,
+ const PointerCoords* pointerCoords, float xPrecision, float yPrecision,
+ float xCursorPosition, float yCursorPosition, nsecs_t downTime,
const std::vector<TouchVideoFrame>& videoFrames);
NotifyMotionArgs(const NotifyMotionArgs& other) = default;
diff --git a/services/inputflinger/include/NotifyArgsBuilders.h b/services/inputflinger/include/NotifyArgsBuilders.h
index 8ffbc11..1ba0cfd 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -19,7 +19,6 @@
#include <NotifyArgs.h>
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <input/InputEventBuilders.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
@@ -30,8 +29,11 @@
class MotionArgsBuilder {
public:
- MotionArgsBuilder(int32_t action, int32_t source) {
+ MotionArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
mAction = action;
+ if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
+ addFlag(AMOTION_EVENT_FLAG_CANCELED);
+ }
mSource = source;
mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
mDownTime = mEventTime;
@@ -52,7 +54,7 @@
return *this;
}
- MotionArgsBuilder& displayId(int32_t displayId) {
+ MotionArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -97,7 +99,7 @@
return *this;
}
- NotifyMotionArgs build() {
+ NotifyMotionArgs build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -106,19 +108,17 @@
}
// Set mouse cursor position for the most common cases to avoid boilerplate.
+ float resolvedCursorX = mRawXCursorPosition;
+ float resolvedCursorY = mRawYCursorPosition;
if (mSource == AINPUT_SOURCE_MOUSE &&
!MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_X) &&
BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_Y)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ resolvedCursorX = pointerCoords[0].getX();
+ resolvedCursorY = pointerCoords[0].getY();
}
- if (mAction == AMOTION_EVENT_ACTION_CANCEL) {
- addFlag(AMOTION_EVENT_FLAG_CANCELED);
- }
-
- return {InputEvent::nextId(),
+ return {mEventId,
mEventTime,
/*readTime=*/mEventTime,
mDeviceId,
@@ -137,19 +137,20 @@
pointerCoords.data(),
/*xPrecision=*/0,
/*yPrecision=*/0,
- mRawXCursorPosition,
- mRawYCursorPosition,
+ resolvedCursorX,
+ resolvedCursorY,
mDownTime,
/*videoFrames=*/{}};
}
private:
+ const int32_t mEventId;
int32_t mAction;
int32_t mDeviceId{DEFAULT_DEVICE_ID};
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::ADISPLAY_ID_DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mActionButton{0};
int32_t mButtonState{0};
@@ -163,7 +164,7 @@
class KeyArgsBuilder {
public:
- KeyArgsBuilder(int32_t action, int32_t source) {
+ KeyArgsBuilder(int32_t action, int32_t source) : mEventId(InputEvent::nextId()) {
mAction = action;
mSource = source;
mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -185,7 +186,7 @@
return *this;
}
- KeyArgsBuilder& displayId(int32_t displayId) {
+ KeyArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -206,7 +207,7 @@
}
NotifyKeyArgs build() const {
- return {InputEvent::nextId(),
+ return {mEventId,
mEventTime,
/*readTime=*/mEventTime,
mDeviceId,
@@ -222,12 +223,13 @@
}
private:
+ const int32_t mEventId;
int32_t mAction;
int32_t mDeviceId = DEFAULT_DEVICE_ID;
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::ADISPLAY_ID_DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mFlags{0};
int32_t mKeyCode{AKEYCODE_UNKNOWN};
diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
index 462aedc..f6dc109 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -53,7 +53,8 @@
* @param displayId The updated display on which the mouse cursor is shown
* @param position The new position of the mouse cursor on the logical display
*/
- virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0;
+ virtual void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
+ const FloatPoint& position) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index c44486f..cee44fc 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -61,8 +61,6 @@
* TODO(b/293587049): Refactor the PointerController class into different controller types.
*/
enum class ControllerType {
- // The PointerController that is responsible for drawing all icons.
- LEGACY,
// Represents a single mouse pointer.
MOUSE,
// Represents multiple touch spots.
@@ -127,13 +125,13 @@
* pressed (not hovering).
*/
virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) = 0;
+ BitSet32 spotIdBits, ui::LogicalDisplayId displayId) = 0;
/* Removes all spots. */
virtual void clearSpots() = 0;
/* Gets the id of the display where the pointer should be shown. */
- virtual int32_t getDisplayId() const = 0;
+ virtual ui::LogicalDisplayId getDisplayId() const = 0;
/* Sets the associated display of this pointer. Pointer should show on that display. */
virtual void setDisplayViewport(const DisplayViewport& displayViewport) = 0;
@@ -143,6 +141,11 @@
/* Sets the custom pointer icon for mice or styluses. */
virtual void setCustomPointerIcon(const SpriteIcon& icon) = 0;
+
+ /* Sets the flag to skip screenshot of the pointer indicators on the display matching the
+ * provided displayId.
+ */
+ virtual void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 3ca691e..fe70a51 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -123,7 +123,8 @@
{"multi_index", InputLightClass::MULTI_INDEX},
{"multi_intensity", InputLightClass::MULTI_INTENSITY},
{"max_brightness", InputLightClass::MAX_BRIGHTNESS},
- {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT}};
+ {"kbd_backlight", InputLightClass::KEYBOARD_BACKLIGHT},
+ {"mic_mute", InputLightClass::KEYBOARD_MIC_MUTE}};
// Mapping for input multicolor led class node names.
// https://www.kernel.org/doc/html/latest/leds/leds-class-multicolor.html
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index 2baf576..15586e2 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -77,11 +77,11 @@
// If a device is associated with a specific display but there is no
// associated DisplayViewport, don't enable the device.
- if (enable && (mAssociatedDisplayPort || mAssociatedDisplayUniqueId) &&
+ if (enable && (mAssociatedDisplayPort || mAssociatedDisplayUniqueIdByPort) &&
!mAssociatedViewport) {
const std::string desc = mAssociatedDisplayPort
? "port " + std::to_string(*mAssociatedDisplayPort)
- : "uniqueId " + *mAssociatedDisplayUniqueId;
+ : "uniqueId " + *mAssociatedDisplayUniqueIdByPort;
ALOGW("Cannot enable input device %s because it is associated "
"with %s, but the corresponding viewport is not found",
getName().c_str(), desc.c_str());
@@ -124,9 +124,15 @@
} else {
dump += "<none>\n";
}
- dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueId: ");
- if (mAssociatedDisplayUniqueId) {
- dump += StringPrintf("%s\n", mAssociatedDisplayUniqueId->c_str());
+ dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueIdByPort: ");
+ if (mAssociatedDisplayUniqueIdByPort) {
+ dump += StringPrintf("%s\n", mAssociatedDisplayUniqueIdByPort->c_str());
+ } else {
+ dump += "<none>\n";
+ }
+ dump += StringPrintf(INDENT2 "AssociatedDisplayUniqueIdByDescriptor: ");
+ if (mAssociatedDisplayUniqueIdByDescriptor) {
+ dump += StringPrintf("%s\n", mAssociatedDisplayUniqueIdByDescriptor->c_str());
} else {
dump += "<none>\n";
}
@@ -269,8 +275,28 @@
// In most situations, no port or name will be specified.
mAssociatedDisplayPort = std::nullopt;
- mAssociatedDisplayUniqueId = std::nullopt;
+ mAssociatedDisplayUniqueIdByPort = std::nullopt;
mAssociatedViewport = std::nullopt;
+ // Find the display port that corresponds to the current input device descriptor
+ const std::string& inputDeviceDescriptor = mIdentifier.descriptor;
+ if (!inputDeviceDescriptor.empty()) {
+ const std::unordered_map<std::string, uint8_t>& ports =
+ readerConfig.portAssociations;
+ const auto& displayPort = ports.find(inputDeviceDescriptor);
+ if (displayPort != ports.end()) {
+ mAssociatedDisplayPort = std::make_optional(displayPort->second);
+ } else {
+ const std::unordered_map<std::string, std::string>&
+ displayUniqueIdsByDescriptor =
+ readerConfig.uniqueIdAssociationsByDescriptor;
+ const auto& displayUniqueIdByDescriptor =
+ displayUniqueIdsByDescriptor.find(inputDeviceDescriptor);
+ if (displayUniqueIdByDescriptor != displayUniqueIdsByDescriptor.end()) {
+ mAssociatedDisplayUniqueIdByDescriptor =
+ displayUniqueIdByDescriptor->second;
+ }
+ }
+ }
// Find the display port that corresponds to the current input port.
const std::string& inputPort = mIdentifier.location;
if (!inputPort.empty()) {
@@ -280,11 +306,11 @@
if (displayPort != ports.end()) {
mAssociatedDisplayPort = std::make_optional(displayPort->second);
} else {
- const std::unordered_map<std::string, std::string>& displayUniqueIds =
- readerConfig.uniqueIdAssociations;
- const auto& displayUniqueId = displayUniqueIds.find(inputPort);
- if (displayUniqueId != displayUniqueIds.end()) {
- mAssociatedDisplayUniqueId = displayUniqueId->second;
+ const std::unordered_map<std::string, std::string>& displayUniqueIdsByPort =
+ readerConfig.uniqueIdAssociationsByPort;
+ const auto& displayUniqueIdByPort = displayUniqueIdsByPort.find(inputPort);
+ if (displayUniqueIdByPort != displayUniqueIdsByPort.end()) {
+ mAssociatedDisplayUniqueIdByPort = displayUniqueIdByPort->second;
}
}
}
@@ -299,13 +325,21 @@
"but the corresponding viewport is not found.",
getName().c_str(), *mAssociatedDisplayPort);
}
- } else if (mAssociatedDisplayUniqueId != std::nullopt) {
- mAssociatedViewport =
- readerConfig.getDisplayViewportByUniqueId(*mAssociatedDisplayUniqueId);
+ } else if (mAssociatedDisplayUniqueIdByDescriptor != std::nullopt) {
+ mAssociatedViewport = readerConfig.getDisplayViewportByUniqueId(
+ *mAssociatedDisplayUniqueIdByDescriptor);
if (!mAssociatedViewport) {
ALOGW("Input device %s should be associated with display %s but the "
"corresponding viewport cannot be found",
- getName().c_str(), mAssociatedDisplayUniqueId->c_str());
+ getName().c_str(), mAssociatedDisplayUniqueIdByDescriptor->c_str());
+ }
+ } else if (mAssociatedDisplayUniqueIdByPort != std::nullopt) {
+ mAssociatedViewport = readerConfig.getDisplayViewportByUniqueId(
+ *mAssociatedDisplayUniqueIdByPort);
+ if (!mAssociatedViewport) {
+ ALOGW("Input device %s should be associated with display %s but the "
+ "corresponding viewport cannot be found",
+ getName().c_str(), mAssociatedDisplayUniqueIdByPort->c_str());
}
}
@@ -408,8 +442,8 @@
InputDeviceInfo InputDevice::getDeviceInfo() {
InputDeviceInfo outDeviceInfo;
outDeviceInfo.initialize(mId, mGeneration, mControllerNumber, mIdentifier, mAlias, mIsExternal,
- mHasMic, getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE),
- {mShouldSmoothScroll});
+ mHasMic, getAssociatedDisplayId().value_or(ui::ADISPLAY_ID_NONE),
+ {mShouldSmoothScroll}, isEnabled());
for_each_mapper(
[&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
@@ -507,15 +541,7 @@
}
// Touchscreens and touchpad devices.
- static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
- sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
- // TODO(b/272518665): Fix the new touchpad stack for Sony DualShock 4 (5c4, 9cc) touchpads, or
- // at least load this setting from the IDC file.
- const InputDeviceIdentifier identifier = contextPtr.getDeviceIdentifier();
- const bool isSonyDualShock4Touchpad = identifier.vendor == 0x054c &&
- (identifier.product == 0x05c4 || identifier.product == 0x09cc);
- if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
- classes.test(InputDeviceClass::TOUCH_MT) && !isSonyDualShock4Touchpad) {
+ if (classes.test(InputDeviceClass::TOUCHPAD) && classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(createInputMapper<TouchpadInputMapper>(contextPtr, readerConfig));
} else if (classes.test(InputDeviceClass::TOUCH_MT)) {
mappers.push_back(createInputMapper<MultiTouchInputMapper>(contextPtr, readerConfig));
@@ -673,14 +699,14 @@
return NotifyDeviceResetArgs(mContext->getNextId(), when, mId);
}
-std::optional<int32_t> InputDevice::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> InputDevice::getAssociatedDisplayId() {
// Check if we had associated to the specific display.
if (mAssociatedViewport) {
return mAssociatedViewport->displayId;
}
// No associated display port, check if some InputMapper is associated.
- return first_in_mappers<int32_t>(
+ return first_in_mappers<ui::LogicalDisplayId>(
[](InputMapper& mapper) { return mapper.getAssociatedDisplayId(); });
}
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 9608210..12f52b8 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -402,10 +402,6 @@
ALOGI("Reconfiguring input devices, changes=%s", changes.string().c_str());
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (changes.test(Change::DISPLAY_INFO)) {
- updatePointerDisplayLocked();
- }
-
if (changes.test(Change::MUST_REOPEN)) {
mEventHub->requestReopenDevices();
} else {
@@ -490,47 +486,6 @@
}
}
-std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked(
- int32_t deviceId) {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller == nullptr) {
- controller = mPolicy->obtainPointerController(deviceId);
- mPointerController = controller;
- updatePointerDisplayLocked();
- }
- return controller;
-}
-
-void InputReader::updatePointerDisplayLocked() {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller == nullptr) {
- return;
- }
-
- std::optional<DisplayViewport> viewport =
- mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
- if (!viewport) {
- ALOGW("Can't find the designated viewport with ID %" PRId32 " to update cursor input "
- "mapper. Fall back to default display",
- mConfig.defaultPointerDisplayId);
- viewport = mConfig.getDisplayViewportById(ADISPLAY_ID_DEFAULT);
- }
- if (!viewport) {
- ALOGE("Still can't find a viable viewport to update cursor input mapper. Skip setting it to"
- " PointerController.");
- return;
- }
-
- controller->setDisplayViewport(*viewport);
-}
-
-void InputReader::fadePointerLocked() {
- std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock();
- if (controller != nullptr) {
- controller->fade(PointerControllerInterface::Transition::GRADUAL);
- }
-}
-
void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) {
if (when < mNextTimeout) {
mNextTimeout = when;
@@ -901,7 +856,7 @@
return false;
}
-bool InputReader::canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+bool InputReader::canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) {
std::scoped_lock _l(mLock);
InputDevice* device = findInputDeviceLocked(deviceId);
@@ -915,10 +870,9 @@
return false;
}
- std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
+ std::optional<ui::LogicalDisplayId> associatedDisplayId = device->getAssociatedDisplayId();
// No associated display. By default, can dispatch to all displays.
- if (!associatedDisplayId ||
- *associatedDisplayId == ADISPLAY_ID_NONE) {
+ if (!associatedDisplayId || !associatedDisplayId->isValid()) {
return true;
}
@@ -1067,17 +1021,6 @@
return mReader->shouldDropVirtualKeyLocked(now, keyCode, scanCode);
}
-void InputReader::ContextImpl::fadePointer() {
- // lock is already held by the input loop
- mReader->fadePointerLocked();
-}
-
-std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController(
- int32_t deviceId) {
- // lock is already held by the input loop
- return mReader->getPointerControllerLocked(deviceId);
-}
-
void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) {
// lock is already held by the input loop
mReader->requestTimeoutAtTimeLocked(when);
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index eabf591..27b9d23 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -505,9 +505,14 @@
// Check the rest of raw light infos
for (const auto& [rawId, rawInfo] : rawInfos) {
- InputDeviceLightType type = keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()
- ? InputDeviceLightType::KEYBOARD_BACKLIGHT
- : InputDeviceLightType::INPUT;
+ InputDeviceLightType type;
+ if (keyboardBacklightIds.find(rawId) != keyboardBacklightIds.end()) {
+ type = InputDeviceLightType::KEYBOARD_BACKLIGHT;
+ } else if (rawInfo.flags.test(InputLightClass::KEYBOARD_MIC_MUTE)) {
+ type = InputDeviceLightType::KEYBOARD_MIC_MUTE;
+ } else {
+ type = InputDeviceLightType::INPUT;
+ }
// If the node is multi-color led, construct a MULTI_COLOR light
if (rawInfo.flags.test(InputLightClass::MULTI_INDEX) &&
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index a7e0675..39d2f5b 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -177,6 +177,8 @@
MAX_BRIGHTNESS = 0x00000080,
/* The input light has kbd_backlight name */
KEYBOARD_BACKLIGHT = 0x00000100,
+ /* The input light has mic_mute name */
+ KEYBOARD_MIC_MUTE = 0x00000200,
};
enum class InputBatteryClass : uint32_t {
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 0719b0c..4c9af2e 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -63,8 +63,11 @@
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mAssociatedDisplayPort;
}
- inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
- return mAssociatedDisplayUniqueId;
+ inline std::optional<std::string> getAssociatedDisplayUniqueIdByPort() const {
+ return mAssociatedDisplayUniqueIdByPort;
+ }
+ inline std::optional<std::string> getAssociatedDisplayUniqueIdByDescriptor() const {
+ return mAssociatedDisplayUniqueIdByDescriptor;
}
inline std::optional<std::string> getDeviceTypeAssociation() const {
return mAssociatedDeviceType;
@@ -128,7 +131,7 @@
inline const PropertyMap& getConfiguration() { return mConfiguration; }
inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
- std::optional<int32_t> getAssociatedDisplayId();
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId();
void updateLedState(bool reset);
@@ -194,7 +197,8 @@
bool mIsWaking;
bool mIsExternal;
std::optional<uint8_t> mAssociatedDisplayPort;
- std::optional<std::string> mAssociatedDisplayUniqueId;
+ std::optional<std::string> mAssociatedDisplayUniqueIdByPort;
+ std::optional<std::string> mAssociatedDisplayUniqueIdByDescriptor;
std::optional<std::string> mAssociatedDeviceType;
std::optional<DisplayViewport> mAssociatedViewport;
bool mHasMic;
@@ -449,8 +453,11 @@
inline std::optional<uint8_t> getAssociatedDisplayPort() const {
return mDevice.getAssociatedDisplayPort();
}
- inline std::optional<std::string> getAssociatedDisplayUniqueId() const {
- return mDevice.getAssociatedDisplayUniqueId();
+ inline std::optional<std::string> getAssociatedDisplayUniqueIdByPort() const {
+ return mDevice.getAssociatedDisplayUniqueIdByPort();
+ }
+ inline std::optional<std::string> getAssociatedDisplayUniqueIdByDescriptor() const {
+ return mDevice.getAssociatedDisplayUniqueIdByDescriptor();
}
inline std::optional<std::string> getDeviceTypeAssociation() const {
return mDevice.getDeviceTypeAssociation();
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4c78db3..d9ac917 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -16,7 +16,6 @@
#pragma once
-#include <PointerControllerInterface.h>
#include <android-base/thread_annotations.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
@@ -87,7 +86,7 @@
std::vector<int32_t> getVibratorIds(int32_t deviceId) override;
- bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) override;
+ bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) override;
bool enableSensor(int32_t deviceId, InputDeviceSensorType sensorType,
std::chrono::microseconds samplingPeriod,
@@ -141,9 +140,6 @@
void disableVirtualKeysUntil(nsecs_t time) REQUIRES(mReader->mLock) override;
bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode)
REQUIRES(mReader->mLock) override;
- void fadePointer() REQUIRES(mReader->mLock) override;
- std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId)
- REQUIRES(mReader->mLock) override;
void requestTimeoutAtTime(nsecs_t when) REQUIRES(mReader->mLock) override;
int32_t bumpGeneration() NO_THREAD_SAFETY_ANALYSIS override;
void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices)
@@ -230,13 +226,6 @@
[[nodiscard]] std::list<NotifyArgs> dispatchExternalStylusStateLocked(const StylusState& state)
REQUIRES(mLock);
- // The PointerController that is shared among all the input devices that need it.
- std::weak_ptr<PointerControllerInterface> mPointerController;
- std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId)
- REQUIRES(mLock);
- void updatePointerDisplayLocked() REQUIRES(mLock);
- void fadePointerLocked() REQUIRES(mLock);
-
int32_t mGeneration GUARDED_BY(mLock);
int32_t bumpGenerationLocked() REQUIRES(mLock);
diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h
index 69b2315..907a49f 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -28,7 +28,6 @@
class InputListenerInterface;
class InputMapper;
class InputReaderPolicyInterface;
-class PointerControllerInterface;
struct StylusState;
/* Internal interface used by individual input devices to access global input device state
@@ -45,9 +44,6 @@
virtual void disableVirtualKeysUntil(nsecs_t time) = 0;
virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0;
- virtual void fadePointer() = 0;
- virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) = 0;
-
virtual void requestTimeoutAtTime(nsecs_t when) = 0;
virtual int32_t bumpGeneration() = 0;
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index 061c6a3..21dec6f 100644
--- a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
+++ b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
@@ -19,7 +19,6 @@
#include <sstream>
#include <android-base/stringprintf.h>
-#include <gui/constants.h>
#include <input/PrintTools.h>
#include <linux/input-event-codes.h>
#include <log/log_main.h>
@@ -279,7 +278,7 @@
LOG_ALWAYS_FATAL_IF(coords.size() != properties.size(),
"Mismatched coords and properties arrays.");
return NotifyMotionArgs(mReaderContext.getNextId(), when, readTime, mDeviceId, SOURCE,
- ADISPLAY_ID_NONE, /*policyFlags=*/POLICY_FLAG_WAKE, action,
+ ui::ADISPLAY_ID_NONE, /*policyFlags=*/POLICY_FLAG_WAKE, action,
/*actionButton=*/actionButton, flags,
mReaderContext.getGlobalMetaState(), mButtonState,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, coords.size(),
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index c8cc5dc..d5fe040 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -40,8 +40,6 @@
// The default velocity control parameters that has no effect.
static const VelocityControlParameters FLAT_VELOCITY_CONTROL_PARAMS{};
-static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
-
// --- CursorMotionAccumulator ---
CursorMotionAccumulator::CursorMotionAccumulator() {
@@ -78,22 +76,10 @@
CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
- : CursorInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {}
-
-CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig,
- bool enablePointerChoreographer)
: InputMapper(deviceContext, readerConfig),
mLastEventTime(std::numeric_limits<nsecs_t>::min()),
- mEnablePointerChoreographer(enablePointerChoreographer),
mEnableNewMousePointerBallistics(input_flags::enable_new_mouse_pointer_ballistics()) {}
-CursorInputMapper::~CursorInputMapper() {
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
- }
-}
-
uint32_t CursorInputMapper::getSources() const {
return mSource;
}
@@ -143,7 +129,8 @@
mWheelXVelocityControl.getParameters().dump().c_str());
dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
- dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n",
+ toString(mDisplayId, streamableToString).c_str());
dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(mOrientation).c_str());
dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
@@ -304,22 +291,6 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mSource == AINPUT_SOURCE_MOUSE) {
- if (!mEnablePointerChoreographer) {
- if (moved || scrolled || buttonsChanged) {
- mPointerController->setPresentation(
- PointerControllerInterface::Presentation::POINTER);
-
- if (moved) {
- mPointerController->move(deltaX, deltaY);
- }
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- }
-
- std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
-
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
- }
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
} else {
@@ -447,7 +418,7 @@
}
}
-std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> CursorInputMapper::getAssociatedDisplayId() {
return mDisplayId;
}
@@ -470,7 +441,6 @@
mYPrecision = 1.0f;
mXScale = 1.0f;
mYScale = 1.0f;
- mPointerController = getContext()->getPointerController(getDeviceId());
break;
case Parameters::Mode::NAVIGATION:
mSource = AINPUT_SOURCE_TRACKBALL;
@@ -490,8 +460,6 @@
if (mParameters.mode == Parameters::Mode::POINTER) {
mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
- // Keep PointerController around in order to preserve the pointer position.
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
} else {
ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
}
@@ -520,13 +488,13 @@
if (mEnableNewMousePointerBallistics) {
mNewPointerVelocityControl.setAccelerationEnabled(
config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0);
+ mDisplayId.value_or(ui::ADISPLAY_ID_NONE)) == 0);
mNewPointerVelocityControl.setCurve(
createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
} else {
mOldPointerVelocityControl.setParameters(
(config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0)
+ mDisplayId.value_or(ui::ADISPLAY_ID_NONE)) == 0)
? config.pointerVelocityControlParameters
: FLAT_VELOCITY_CONTROL_PARAMS);
}
@@ -538,40 +506,19 @@
void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
- mDisplayId = ADISPLAY_ID_NONE;
+ mDisplayId = ui::ADISPLAY_ID_NONE;
std::optional<DisplayViewport> resolvedViewport;
- bool isBoundsSet = false;
if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
// This InputDevice is associated with a viewport.
// Only generate events for the associated display.
mDisplayId = assocViewport->displayId;
resolvedViewport = *assocViewport;
- if (!mEnablePointerChoreographer) {
- const bool mismatchedPointerDisplay =
- isPointer && (assocViewport->displayId != mPointerController->getDisplayId());
- if (mismatchedPointerDisplay) {
- // This device's associated display doesn't match PointerController's current
- // display. Do not associate it with any display.
- mDisplayId.reset();
- }
- }
} else if (isPointer) {
// The InputDevice is not associated with a viewport, but it controls the mouse pointer.
- if (mEnablePointerChoreographer) {
- // Always use DISPLAY_ID_NONE for mouse events.
- // PointerChoreographer will make it target the correct the displayId later.
- resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
- mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
- } else {
- mDisplayId = mPointerController->getDisplayId();
- if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
- resolvedViewport = *v;
- }
- if (auto bounds = mPointerController->getBounds(); bounds) {
- mBoundsInLogicalDisplay = *bounds;
- isBoundsSet = true;
- }
- }
+ // Always use DISPLAY_ID_NONE for mouse events.
+ // PointerChoreographer will make it target the correct the displayId later.
+ resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ui::ADISPLAY_ID_NONE) : std::nullopt;
}
mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) ||
@@ -579,14 +526,12 @@
? ui::ROTATION_0
: getInverseRotation(resolvedViewport->orientation);
- if (!isBoundsSet) {
- mBoundsInLogicalDisplay = resolvedViewport
- ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft),
- static_cast<float>(resolvedViewport->logicalTop),
- static_cast<float>(resolvedViewport->logicalRight - 1),
- static_cast<float>(resolvedViewport->logicalBottom - 1)}
- : FloatRect{0, 0, 0, 0};
- }
+ mBoundsInLogicalDisplay = resolvedViewport
+ ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft),
+ static_cast<float>(resolvedViewport->logicalTop),
+ static_cast<float>(resolvedViewport->logicalRight - 1),
+ static_cast<float>(resolvedViewport->logicalBottom - 1)}
+ : FloatRect{0, 0, 0, 0};
bumpGeneration();
}
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index ca541d9..e45105a 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -20,14 +20,11 @@
#include "CursorScrollAccumulator.h"
#include "InputMapper.h"
-#include <PointerControllerInterface.h>
#include <input/VelocityControl.h>
#include <ui/Rotation.h>
namespace android {
-class PointerControllerInterface;
-
class CursorButtonAccumulator;
class CursorScrollAccumulator;
@@ -56,7 +53,7 @@
friend std::unique_ptr<T> createInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
Args... args);
- virtual ~CursorInputMapper();
+ virtual ~CursorInputMapper() = default;
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
@@ -69,7 +66,7 @@
virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
- virtual std::optional<int32_t> getAssociatedDisplayId() override;
+ virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
private:
// Amount that trackball needs to move in order to generate a key event.
@@ -118,25 +115,18 @@
// The display that events generated by this mapper should target. This can be set to
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
- std::optional<int32_t> mDisplayId;
+ std::optional<ui::LogicalDisplayId> mDisplayId;
ui::Rotation mOrientation{ui::ROTATION_0};
FloatRect mBoundsInLogicalDisplay{};
- std::shared_ptr<PointerControllerInterface> mPointerController;
-
int32_t mButtonState;
nsecs_t mDownTime;
nsecs_t mLastEventTime;
- const bool mEnablePointerChoreographer;
const bool mEnableNewMousePointerBallistics;
explicit CursorInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- // Constructor for testing.
- explicit CursorInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig,
- bool enablePointerChoreographer);
void dumpParameters(std::string& dump);
void configureBasicParams();
void configureOnPointerCapture(const InputReaderConfiguration& config);
diff --git a/services/inputflinger/reader/mapper/InputMapper.cpp b/services/inputflinger/reader/mapper/InputMapper.cpp
index 0692dbb..b6c5c98 100644
--- a/services/inputflinger/reader/mapper/InputMapper.cpp
+++ b/services/inputflinger/reader/mapper/InputMapper.cpp
@@ -20,6 +20,8 @@
#include <sstream>
+#include <ftl/enum.h>
+
#include "InputDevice.h"
#include "input/PrintTools.h"
@@ -133,7 +135,7 @@
dump += StringPrintf(INDENT4 "When: %" PRId64 "\n", state.when);
dump += StringPrintf(INDENT4 "Pressure: %s\n", toString(state.pressure).c_str());
dump += StringPrintf(INDENT4 "Button State: 0x%08x\n", state.buttons);
- dump += StringPrintf(INDENT4 "Tool Type: %" PRId32 "\n", state.toolType);
+ dump += StringPrintf(INDENT4 "Tool Type: %s\n", ftl::enum_string(state.toolType).c_str());
}
} // namespace android
diff --git a/services/inputflinger/reader/mapper/InputMapper.h b/services/inputflinger/reader/mapper/InputMapper.h
index 06de4c2..c7eea0e 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -117,7 +117,7 @@
[[nodiscard]] virtual std::list<NotifyArgs> updateExternalStylusState(const StylusState& state);
- virtual std::optional<int32_t> getAssociatedDisplayId() { return std::nullopt; }
+ virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() { return std::nullopt; }
virtual void updateLedState(bool reset) {}
protected:
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
index 8a9ea75..7fcff95 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -341,7 +341,7 @@
// button will likely wake the device.
// TODO: Use the input device configuration to control this behavior more finely.
uint32_t policyFlags = 0;
- int32_t displayId = ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE;
if (getDeviceContext().getAssociatedViewport()) {
displayId = getDeviceContext().getAssociatedViewport()->displayId;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 738517b..be8d183 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -110,11 +110,11 @@
return ui::ROTATION_0;
}
-int32_t KeyboardInputMapper::getDisplayId() {
+ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() {
if (mViewport) {
return mViewport->displayId;
}
- return ADISPLAY_ID_NONE;
+ return ui::ADISPLAY_ID_NONE;
}
std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
@@ -457,7 +457,7 @@
}
}
-std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() {
if (mViewport) {
return std::make_optional(mViewport->displayId);
}
@@ -483,16 +483,11 @@
void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) {
InputReaderContext& context = *getContext();
context.setLastKeyDownTimestamp(downTime);
- if (context.isPreventingTouchpadTaps()) {
- // avoid pinging java service unnecessarily, just fade pointer again if it became visible
- context.fadePointer();
- return;
- }
+ // TODO(b/338652288): Move cursor fading logic into PointerChoreographer.
// Ignore meta keys or multiple simultaneous down keys as they are likely to be keyboard
// shortcuts
bool shouldHideCursor = mKeyDowns.size() == 1 && !isMetaKey(mKeyDowns[0].keyCode);
if (shouldHideCursor && context.getPolicy()->isInputMethodConnectionActive()) {
- context.fadePointer();
context.setPreventingTouchpadTaps(true);
}
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 500256b..f2d3f4d 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -46,7 +46,7 @@
int32_t getMetaState() override;
bool updateMetaState(int32_t keyCode) override;
- std::optional<int32_t> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
void updateLedState(bool reset) override;
private:
@@ -91,7 +91,7 @@
void dumpParameters(std::string& dump) const;
ui::Rotation getOrientation();
- int32_t getDisplayId();
+ ui::LogicalDisplayId getDisplayId();
[[nodiscard]] std::list<NotifyArgs> processKey(nsecs_t when, nsecs_t readTime, bool down,
int32_t scanCode, int32_t usageCode);
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 07ae5b1..a3206c2 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -125,7 +125,7 @@
if (scrolled) {
int32_t metaState = getContext()->getGlobalMetaState();
// This is not a pointer, so it's not associated with a display.
- int32_t displayId = ADISPLAY_ID_NONE;
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE;
if (mOrientation == ui::ROTATION_180) {
scroll = -scroll;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
index c12e95d..d60dc55 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.cpp
@@ -28,7 +28,7 @@
[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKey(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
int32_t lastButtonState, int32_t currentButtonState, int32_t buttonState, int32_t keyCode) {
std::list<NotifyArgs> out;
if ((action == AKEY_EVENT_ACTION_DOWN && !(lastButtonState & buttonState) &&
@@ -88,7 +88,7 @@
[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
int32_t lastButtonState, int32_t currentButtonState) {
std::list<NotifyArgs> out;
out += synthesizeButtonKey(context, action, when, readTime, deviceId, source, displayId,
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 3023e68..13d952b 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -36,7 +36,7 @@
[[nodiscard]] std::list<NotifyArgs> synthesizeButtonKeys(
InputReaderContext* context, int32_t action, nsecs_t when, nsecs_t readTime,
- int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags,
+ int32_t deviceId, uint32_t source, ui::LogicalDisplayId displayId, uint32_t policyFlags,
int32_t lastButtonState, int32_t currentButtonState);
// For devices connected over Bluetooth, although they may produce events at a consistent rate,
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 7d27d4a..489bea8 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -20,8 +20,24 @@
#include "TouchInputMapper.h"
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstddef>
+#include <tuple>
+
+#include <math.h>
+
+#include <android-base/stringprintf.h>
+#include <android/input.h>
#include <ftl/enum.h>
#include <input/PrintTools.h>
+#include <input/PropertyMap.h>
+#include <input/VirtualKeyMap.h>
+#include <linux/input-event-codes.h>
+#include <log/log_main.h>
+#include <math/vec2.h>
+#include <ui/FloatRect.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
@@ -147,20 +163,6 @@
info.addMotionRange(mOrientedRanges.y);
info.addMotionRange(mOrientedRanges.pressure);
- if (mDeviceMode == DeviceMode::UNSCALED && mSource == AINPUT_SOURCE_TOUCHPAD) {
- // Populate RELATIVE_X and RELATIVE_Y motion ranges for touchpad capture mode.
- //
- // RELATIVE_X and RELATIVE_Y motion ranges should be the largest possible relative
- // motion, i.e. the hardware dimensions, as the finger could move completely across the
- // touchpad in one sample cycle.
- const InputDeviceInfo::MotionRange& x = mOrientedRanges.x;
- const InputDeviceInfo::MotionRange& y = mOrientedRanges.y;
- info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -x.max, x.max, x.flat, x.fuzz,
- x.resolution);
- info.addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -y.max, y.max, y.flat, y.fuzz,
- y.resolution);
- }
-
if (mOrientedRanges.size) {
info.addMotionRange(*mOrientedRanges.size);
}
@@ -334,7 +336,6 @@
changes.any(InputReaderConfiguration::Change::DISPLAY_INFO |
InputReaderConfiguration::Change::POINTER_CAPTURE |
InputReaderConfiguration::Change::POINTER_GESTURE_ENABLEMENT |
- InputReaderConfiguration::Change::SHOW_TOUCHES |
InputReaderConfiguration::Change::EXTERNAL_STYLUS_PRESENCE |
InputReaderConfiguration::Change::DEVICE_TYPE)) {
// Configure device sources, display dimensions, orientation and
@@ -531,14 +532,20 @@
* 4. Otherwise, use a non-display viewport.
*/
std::optional<DisplayViewport> TouchInputMapper::findViewport() {
- if (mParameters.hasAssociatedDisplay && mDeviceMode != DeviceMode::UNSCALED) {
+ if (mParameters.hasAssociatedDisplay) {
if (getDeviceContext().getAssociatedViewport()) {
return getDeviceContext().getAssociatedViewport();
}
- const std::optional<std::string> associatedDisplayUniqueId =
- getDeviceContext().getAssociatedDisplayUniqueId();
- if (associatedDisplayUniqueId) {
+ const std::optional<std::string> associatedDisplayUniqueIdByDescriptor =
+ getDeviceContext().getAssociatedDisplayUniqueIdByDescriptor();
+ if (associatedDisplayUniqueIdByDescriptor) {
+ return getDeviceContext().getAssociatedViewport();
+ }
+
+ const std::optional<std::string> associatedDisplayUniqueIdByPort =
+ getDeviceContext().getAssociatedDisplayUniqueIdByPort();
+ if (associatedDisplayUniqueIdByPort) {
return getDeviceContext().getAssociatedViewport();
}
@@ -548,8 +555,8 @@
if (viewport) {
return viewport;
} else {
- ALOGW("Can't find designated display viewport with ID %" PRId32 " for pointers.",
- mConfig.defaultPointerDisplayId);
+ ALOGW("Can't find designated display viewport with ID %s for pointers.",
+ mConfig.defaultPointerDisplayId.toString().c_str());
}
}
@@ -939,8 +946,10 @@
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
mDeviceMode = DeviceMode::NAVIGATION;
} else {
- mSource = AINPUT_SOURCE_TOUCHPAD;
- mDeviceMode = DeviceMode::UNSCALED;
+ ALOGW("Touch device '%s' has invalid parameters or configuration. The device will be "
+ "inoperable.",
+ getDeviceName().c_str());
+ mDeviceMode = DeviceMode::DISABLED;
}
const std::optional<DisplayViewport> newViewportOpt = findViewport();
@@ -1032,37 +1041,12 @@
mOrientedRanges.clear();
}
- // Create and preserve the pointer controller in the following cases:
- const bool isPointerControllerNeeded =
- // - when the device is in pointer mode, to show the mouse cursor;
- (mDeviceMode == DeviceMode::POINTER) ||
- // - when pointer capture is enabled, to preserve the mouse cursor position;
- (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerCaptureRequest.isEnable()) ||
- // - when we should be showing touches;
- (mDeviceMode == DeviceMode::DIRECT && mConfig.showTouches) ||
- // - when we should be showing a pointer icon for direct styluses.
- (mDeviceMode == DeviceMode::DIRECT && mConfig.stylusPointerIconEnabled && hasStylus());
- if (isPointerControllerNeeded) {
- if (mPointerController == nullptr) {
- mPointerController = getContext()->getPointerController(getDeviceId());
- }
- if (mConfig.pointerCaptureRequest.isEnable()) {
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
- }
- } else {
- if (mPointerController != nullptr && mDeviceMode == DeviceMode::DIRECT &&
- !mConfig.showTouches) {
- mPointerController->clearSpots();
- }
- mPointerController.reset();
- }
-
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
- ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %d, mode %d, "
- "display id %d",
+ ALOGI("Device reconfigured: id=%d, name='%s', size %s, orientation %s, mode %s, "
+ "display id %s",
getDeviceId(), getDeviceName().c_str(), toString(mDisplayBounds).c_str(),
- mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
+ ftl::enum_string(mInputDeviceOrientation).c_str(),
+ ftl::enum_string(mDeviceMode).c_str(), mViewport.displayId.toString().c_str());
configureVirtualKeys();
@@ -1113,7 +1097,8 @@
dump += StringPrintf(INDENT3 "DisplayBounds: %s\n", toString(mDisplayBounds).c_str());
dump += StringPrintf(INDENT3 "PhysicalFrameInRotatedDisplay: %s\n",
toString(mPhysicalFrameInRotatedDisplay).c_str());
- dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
+ dump += StringPrintf(INDENT3 "InputDeviceOrientation: %s\n",
+ ftl::enum_string(mInputDeviceOrientation).c_str());
}
void TouchInputMapper::configureVirtualKeys() {
@@ -1388,7 +1373,6 @@
std::list<NotifyArgs> TouchInputMapper::reset(nsecs_t when) {
std::list<NotifyArgs> out = cancelTouch(when, when);
- updateTouchSpots();
mCursorButtonAccumulator.reset(getDeviceContext());
mCursorScrollAccumulator.reset(getDeviceContext());
@@ -1415,11 +1399,6 @@
mPointerSimple.reset();
resetExternalStylus();
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- mPointerController->clearSpots();
- }
-
return out += InputMapper::reset(when);
}
@@ -1574,11 +1553,6 @@
uint32_t policyFlags = 0;
bool buttonsPressed = mCurrentRawState.buttonState & ~mLastRawState.buttonState;
if (initialDown || buttonsPressed) {
- // If this is a touch screen, hide the pointer on an initial down.
- if (mDeviceMode == DeviceMode::DIRECT) {
- getContext()->fadePointer();
- }
-
if (mParameters.wake) {
policyFlags |= POLICY_FLAG_WAKE;
}
@@ -1646,7 +1620,6 @@
out += dispatchPointerUsage(when, readTime, policyFlags, pointerUsage);
} else {
if (!mCurrentMotionAborted) {
- updateTouchSpots();
out += dispatchButtonRelease(when, readTime, policyFlags);
out += dispatchHoverExit(when, readTime, policyFlags);
out += dispatchTouches(when, readTime, policyFlags);
@@ -1678,28 +1651,6 @@
return out;
}
-void TouchInputMapper::updateTouchSpots() {
- if (!mConfig.showTouches || mPointerController == nullptr) {
- return;
- }
-
- // Update touch spots when this is a touchscreen even when it's not enabled so that we can
- // clear touch spots.
- if (mDeviceMode != DeviceMode::DIRECT &&
- (mDeviceMode != DeviceMode::DISABLED || !isTouchScreen())) {
- return;
- }
-
- mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT);
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
-
- mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords.cbegin(),
- mCurrentCookedState.cookedPointerData.idToIndex.cbegin(),
- mCurrentCookedState.cookedPointerData.touchingIdBits |
- mCurrentCookedState.cookedPointerData.hoveringIdBits,
- mViewport.displayId);
-}
-
bool TouchInputMapper::isTouchScreen() {
return mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN &&
mParameters.hasAssociatedDisplay;
@@ -1884,8 +1835,7 @@
}
if (!mCurrentRawState.rawPointerData.hoveringIdBits.isEmpty() &&
- mCurrentRawState.rawPointerData.touchingIdBits.isEmpty() &&
- mDeviceMode != DeviceMode::UNSCALED) {
+ mCurrentRawState.rawPointerData.touchingIdBits.isEmpty()) {
// We have hovering pointers, and there are no touching pointers.
bool hoveringPointersInFrame = false;
auto hoveringIds = mCurrentRawState.rawPointerData.hoveringIdBits;
@@ -1912,7 +1862,7 @@
// Skip checking whether the pointer is inside the physical frame if the device is in
// unscaled or pointer mode.
if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
- mDeviceMode != DeviceMode::UNSCALED && mDeviceMode != DeviceMode::POINTER) {
+ mDeviceMode != DeviceMode::POINTER) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise, we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -2549,54 +2499,6 @@
cancelPreviousGesture = false;
}
- // Update the pointer presentation and spots.
- if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- if (finishPreviousGesture || cancelPreviousGesture) {
- mPointerController->clearSpots();
- }
-
- if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- mPointerController->setSpots(mPointerGesture.currentGestureCoords.cbegin(),
- mPointerGesture.currentGestureIdToIndex.cbegin(),
- mPointerGesture.currentGestureIdBits,
- mPointerController->getDisplayId());
- }
- } else {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- }
-
- // Show or hide the pointer if needed.
- switch (mPointerGesture.currentGestureMode) {
- case PointerGesture::Mode::NEUTRAL:
- case PointerGesture::Mode::QUIET:
- if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH &&
- mPointerGesture.lastGestureMode == PointerGesture::Mode::FREEFORM) {
- // Remind the user of where the pointer is after finishing a gesture with spots.
- mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL);
- }
- break;
- case PointerGesture::Mode::TAP:
- case PointerGesture::Mode::TAP_DRAG:
- case PointerGesture::Mode::BUTTON_CLICK_OR_DRAG:
- case PointerGesture::Mode::HOVER:
- case PointerGesture::Mode::PRESS:
- case PointerGesture::Mode::SWIPE:
- // Unfade the pointer when the current gesture manipulates the
- // area directly under the pointer.
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- break;
- case PointerGesture::Mode::FREEFORM:
- // Fade the pointer when the current gesture manipulates a different
- // area and there are spots to guide the user experience.
- if (mParameters.gestureMode == Parameters::GestureMode::MULTI_TOUCH) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- } else {
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- }
- break;
- }
-
// Send events!
int32_t metaState = getContext()->getGlobalMetaState();
int32_t buttonState = mCurrentCookedState.buttonState;
@@ -2735,7 +2637,6 @@
// the pointer is hovering again even if the user is not currently touching
// the touch pad. This ensures that a view will receive a fresh hover enter
// event after a tap.
- const auto [x, y] = mPointerController->getPosition();
PointerProperties pointerProperties;
pointerProperties.clear();
@@ -2744,16 +2645,12 @@
PointerCoords pointerCoords;
pointerCoords.clear();
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-
- const int32_t displayId = mPointerController->getDisplayId();
out.push_back(NotifyMotionArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, displayId, policyFlags,
+ mSource, ui::ADISPLAY_ID_NONE, policyFlags,
AMOTION_EVENT_ACTION_HOVER_MOVE, 0, flags, metaState,
buttonState, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
- &pointerCoords, 0, 0, x, y, mPointerGesture.downTime,
+ &pointerCoords, 0, 0, 0.f, 0.f, mPointerGesture.downTime,
/*videoFrames=*/{}));
}
@@ -2799,12 +2696,6 @@
// Reset the current pointer gesture.
mPointerGesture.reset();
mPointerVelocityControl.reset();
-
- // Remove any current spots.
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- mPointerController->clearSpots();
- }
return out;
}
@@ -2940,8 +2831,6 @@
mPointerVelocityControl.reset();
}
- const auto [x, y] = mPointerController->getPosition();
-
mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -2950,8 +2839,6 @@
mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
mPointerGesture.currentGestureProperties[0].toolType = ToolType::FINGER;
mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
} else if (currentFingerCount == 0) {
// Case 3. No fingers down and button is not pressed. (NEUTRAL)
@@ -2966,9 +2853,8 @@
mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- const auto [x, y] = mPointerController->getPosition();
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
- fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+ if (fabs(0.f - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+ fabs(0.f - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
ALOGD_IF(DEBUG_GESTURES, "Gestures: TAP");
mPointerGesture.tapUpTime = when;
@@ -2995,7 +2881,7 @@
tapped = true;
} else {
ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+ 0.f - mPointerGesture.tapX, 0.f - mPointerGesture.tapY);
}
} else {
if (DEBUG_GESTURES) {
@@ -3027,13 +2913,12 @@
mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- const auto [x, y] = mPointerController->getPosition();
- if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
- fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
+ if (fabs(0.f - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
+ fabs(0.f - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
} else {
ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+ 0.f - mPointerGesture.tapX, 0.f - mPointerGesture.tapY);
}
} else {
ALOGD_IF(DEBUG_GESTURES, "Gestures: Not a TAP_DRAG, %0.3fms time since up",
@@ -3063,8 +2948,6 @@
down = false;
}
- const auto [x, y] = mPointerController->getPosition();
-
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -3072,16 +2955,14 @@
mPointerGesture.currentGestureProperties[0].id = mPointerGesture.activeGestureId;
mPointerGesture.currentGestureProperties[0].toolType = ToolType::FINGER;
mPointerGesture.currentGestureCoords[0].clear();
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
down ? 1.0f : 0.0f);
if (lastFingerCount == 0 && currentFingerCount != 0) {
mPointerGesture.resetTap();
mPointerGesture.tapDownTime = when;
- mPointerGesture.tapX = x;
- mPointerGesture.tapY = y;
+ mPointerGesture.tapX = 0.f;
+ mPointerGesture.tapY = 0.f;
}
} else {
// Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
@@ -3090,11 +2971,13 @@
if (DEBUG_GESTURES) {
ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
- "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
- "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+ "currentGestureMode=%s, currentGestureIdBits=0x%08x, "
+ "lastGestureMode=%s, lastGestureIdBits=0x%08x",
toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
- mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
- mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+ ftl::enum_string(mPointerGesture.currentGestureMode).c_str(),
+ mPointerGesture.currentGestureIdBits.value,
+ ftl::enum_string(mPointerGesture.lastGestureMode).c_str(),
+ mPointerGesture.lastGestureIdBits.value);
for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
uint32_t id = idBits.clearFirstMarkedBit();
uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
@@ -3230,8 +3113,8 @@
mCurrentRawState.rawPointerData
.getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
&mPointerGesture.referenceTouchY);
- std::tie(mPointerGesture.referenceGestureX, mPointerGesture.referenceGestureY) =
- mPointerController->getPosition();
+ mPointerGesture.referenceGestureX = 0.f;
+ mPointerGesture.referenceGestureY = 0.f;
}
// Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3526,8 +3409,6 @@
rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
-
- mPointerController->move(deltaX, deltaY);
}
std::list<NotifyArgs> TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime,
@@ -3544,13 +3425,6 @@
float x = mCurrentCookedState.cookedPointerData.pointerCoords[index].getX();
float y = mCurrentCookedState.cookedPointerData.pointerCoords[index].getY();
- // Styluses are configured specifically for one display. We only update the
- // PointerController for this stylus if the PointerController is configured for
- // the same display as this stylus,
- if (getAssociatedDisplayId() == mViewport.displayId) {
- mPointerController->setPosition(x, y);
- std::tie(x, y) = mPointerController->getPosition();
- }
mPointerSimple.currentCoords = mCurrentCookedState.cookedPointerData.pointerCoords[index];
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3588,12 +3462,9 @@
down = isPointerDown(mCurrentRawState.buttonState);
hovering = !down;
- const auto [x, y] = mPointerController->getPosition();
const uint32_t currentIndex = mCurrentRawState.rawPointerData.idToIndex[id];
mPointerSimple.currentCoords =
mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex];
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
- mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE,
hovering ? 0.0f : 1.0f);
mPointerSimple.currentProperties.id = 0;
@@ -3606,8 +3477,7 @@
hovering = false;
}
- const int32_t displayId = mPointerController->getDisplayId();
- return dispatchPointerSimple(when, readTime, policyFlags, down, hovering, displayId);
+ return dispatchPointerSimple(when, readTime, policyFlags, down, hovering, ui::ADISPLAY_ID_NONE);
}
std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
@@ -3621,24 +3491,14 @@
std::list<NotifyArgs> TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
uint32_t policyFlags, bool down,
- bool hovering, int32_t displayId) {
+ bool hovering,
+ ui::LogicalDisplayId displayId) {
LOG_ALWAYS_FATAL_IF(mDeviceMode != DeviceMode::POINTER,
"%s cannot be used when the device is not in POINTER mode.", __func__);
std::list<NotifyArgs> out;
int32_t metaState = getContext()->getGlobalMetaState();
auto cursorPosition = mPointerSimple.currentCoords.getXYValue();
- if (displayId == mPointerController->getDisplayId()) {
- std::tie(cursorPosition.x, cursorPosition.y) = mPointerController->getPosition();
- if (down || hovering) {
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- mPointerController->clearSpots();
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- }
- }
-
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3774,9 +3634,6 @@
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
/*videoFrames=*/{}));
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- }
}
mPointerSimple.reset();
return out;
@@ -3827,35 +3684,14 @@
source |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
}
- const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
- const bool showDirectStylusPointer = mConfig.stylusPointerIconEnabled &&
- mDeviceMode == DeviceMode::DIRECT && isStylusEvent(source, pointerProperties) &&
- mPointerController && displayId != ADISPLAY_ID_NONE &&
- displayId == mPointerController->getDisplayId();
- if (showDirectStylusPointer) {
- switch (action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_HOVER_ENTER:
- case AMOTION_EVENT_ACTION_HOVER_MOVE:
- mPointerController->setPresentation(
- PointerControllerInterface::Presentation::STYLUS_HOVER);
- mPointerController
- ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[0].getX(),
- mCurrentCookedState.cookedPointerData.pointerCoords[0]
- .getY());
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
- break;
- case AMOTION_EVENT_ACTION_HOVER_EXIT:
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
- break;
- }
- }
+ const ui::LogicalDisplayId displayId = getAssociatedDisplayId().value_or(ui::ADISPLAY_ID_NONE);
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition();
+ xCursorPosition = yCursorPosition = 0.f;
}
- const int32_t deviceId = getDeviceId();
+ const DeviceId deviceId = getDeviceId();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
[this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
@@ -4122,10 +3958,10 @@
return true;
}
-std::optional<int32_t> TouchInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchInputMapper::getAssociatedDisplayId() {
if (mParameters.hasAssociatedDisplay) {
if (mDeviceMode == DeviceMode::POINTER) {
- return std::make_optional(mPointerController->getDisplayId());
+ return ui::ADISPLAY_ID_NONE;
} else {
return std::make_optional(mViewport.displayId);
}
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 4b39e40..0c9ffa2 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -16,17 +16,36 @@
#pragma once
+#include <array>
+#include <climits>
+#include <limits>
+#include <list>
+#include <memory>
#include <optional>
#include <string>
+#include <utility>
+#include <vector>
+#include <input/DisplayViewport.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+#include <input/VelocityControl.h>
+#include <input/VelocityTracker.h>
#include <stdint.h>
+#include <ui/Rect.h>
#include <ui/Rotation.h>
+#include <ui/Size.h>
+#include <ui/Transform.h>
+#include <utils/BitSet.h>
+#include <utils/Timers.h>
#include "CursorButtonAccumulator.h"
#include "CursorScrollAccumulator.h"
#include "EventHub.h"
#include "InputMapper.h"
#include "InputReaderBase.h"
+#include "NotifyArgs.h"
+#include "StylusState.h"
#include "TouchButtonAccumulator.h"
namespace android {
@@ -166,7 +185,7 @@
[[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
[[nodiscard]] std::list<NotifyArgs> updateExternalStylusState(
const StylusState& state) override;
- std::optional<int32_t> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
protected:
CursorButtonAccumulator mCursorButtonAccumulator;
@@ -195,7 +214,6 @@
enum class DeviceMode {
DISABLED, // input is disabled
DIRECT, // direct mapping (touchscreen)
- UNSCALED, // unscaled mapping (e.g. captured touchpad)
NAVIGATION, // unscaled mapping with assist gesture (touch navigation)
POINTER, // pointer mapping (e.g. uncaptured touchpad, drawing tablet)
@@ -372,9 +390,6 @@
// The time the primary pointer last went down.
nsecs_t mDownTime{0};
- // The pointer controller, or null if the device is not a pointer.
- std::shared_ptr<PointerControllerInterface> mPointerController;
-
std::vector<VirtualKey> mVirtualKeys;
explicit TouchInputMapper(InputDeviceContext& deviceContext,
@@ -569,6 +584,8 @@
// Waiting for quiet time to end before starting the next gesture.
QUIET,
+
+ ftl_last = QUIET,
};
// When a gesture is sent to an unfocused window, return true if it can bring that window
@@ -688,7 +705,7 @@
// Values reported for the last pointer event.
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId{ui::ADISPLAY_ID_NONE};
float lastCursorX;
float lastCursorY;
@@ -701,7 +718,7 @@
hovering = false;
downTime = 0;
source = 0;
- displayId = ADISPLAY_ID_NONE;
+ displayId = ui::ADISPLAY_ID_NONE;
lastCursorX = 0.f;
lastCursorY = 0.f;
}
@@ -792,7 +809,8 @@
[[nodiscard]] std::list<NotifyArgs> dispatchPointerSimple(nsecs_t when, nsecs_t readTime,
uint32_t policyFlags, bool down,
- bool hovering, int32_t displayId);
+ bool hovering,
+ ui::LogicalDisplayId displayId);
[[nodiscard]] std::list<NotifyArgs> abortPointerSimple(nsecs_t when, nsecs_t readTime,
uint32_t policyFlags);
@@ -815,9 +833,6 @@
// Returns if this touch device is a touch screen with an associated display.
bool isTouchScreen();
- // Updates touch spots if they are enabled. Should only be used when this device is a
- // touchscreen.
- void updateTouchSpots();
bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const;
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 99f9e24..e157862 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -47,8 +47,6 @@
namespace {
-static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
-
/**
* Log details of each gesture output by the gestures library.
* Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
@@ -185,6 +183,7 @@
static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
AStatsEventList* outEventList,
void* cookie) {
+ ALOGI("Received pull request for touchpad usage atom");
LOG_ALWAYS_FATAL_IF(atomTag != android::util::TOUCHPAD_USAGE);
MetricsAccumulator& accumulator = MetricsAccumulator::getInstance();
accumulator.produceAtomsAndReset(*outEventList);
@@ -192,12 +191,14 @@
}
void produceAtomsAndReset(AStatsEventList& outEventList) {
+ ALOGI("Acquiring lock for touchpad usage metrics...");
std::scoped_lock lock(mLock);
produceAtomsLocked(outEventList);
resetCountersLocked();
}
void produceAtomsLocked(AStatsEventList& outEventList) const REQUIRES(mLock) {
+ ALOGI("Producing touchpad usage atoms for %zu counters", mCounters.size());
for (auto& [id, counters] : mCounters) {
auto [busId, vendorId, productId, versionId] = id;
addAStatsEvent(&outEventList, android::util::TOUCHPAD_USAGE, vendorId, productId,
@@ -234,20 +235,13 @@
TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig)
- : TouchpadInputMapper(deviceContext, readerConfig, ENABLE_POINTER_CHOREOGRAPHER) {}
-
-TouchpadInputMapper::TouchpadInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig,
- bool enablePointerChoreographer)
: InputMapper(deviceContext, readerConfig),
mGestureInterpreter(NewGestureInterpreter(), DeleteGestureInterpreter),
- mPointerController(getContext()->getPointerController(getDeviceId())),
mTimerProvider(*getContext()),
mStateConverter(deviceContext, mMotionAccumulator),
mGestureConverter(*getContext(), deviceContext, getDeviceId()),
mCapturedEventConverter(*getContext(), deviceContext, mMotionAccumulator, getDeviceId()),
- mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())),
- mEnablePointerChoreographer(enablePointerChoreographer) {
+ mMetricsId(metricsIdFromInputDeviceIdentifier(deviceContext.getDeviceIdentifier())) {
RawAbsoluteAxisInfo slotAxisInfo;
deviceContext.getAbsoluteAxisInfo(ABS_MT_SLOT, &slotAxisInfo);
if (!slotAxisInfo.valid || slotAxisInfo.maxValue <= 0) {
@@ -274,10 +268,6 @@
}
TouchpadInputMapper::~TouchpadInputMapper() {
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
- }
-
// The gesture interpreter's destructor will try to free its property and timer providers,
// calling PropertyProvider::freeProperty and TimerProvider::freeTimer using a raw pointers.
// Depending on the declaration order in TouchpadInputMapper.h, those providers may have already
@@ -319,7 +309,8 @@
}
dump += INDENT3 "Captured event converter:\n";
dump += addLinePrefix(mCapturedEventConverter.dump(), INDENT4);
- dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
+ dump += StringPrintf(INDENT3 "DisplayId: %s\n",
+ toString(mDisplayId, streamableToString).c_str());
}
std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when,
@@ -331,7 +322,7 @@
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- mDisplayId = ADISPLAY_ID_NONE;
+ mDisplayId = ui::ADISPLAY_ID_NONE;
std::optional<DisplayViewport> resolvedViewport;
std::optional<FloatRect> boundsInLogicalDisplay;
if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
@@ -339,33 +330,12 @@
// Only generate events for the associated display.
mDisplayId = assocViewport->displayId;
resolvedViewport = *assocViewport;
- if (!mEnablePointerChoreographer) {
- const bool mismatchedPointerDisplay =
- (assocViewport->displayId != mPointerController->getDisplayId());
- if (mismatchedPointerDisplay) {
- ALOGW("Touchpad \"%s\" associated viewport display does not match pointer "
- "controller",
- mDeviceContext.getName().c_str());
- mDisplayId.reset();
- }
- }
} else {
// The InputDevice is not associated with a viewport, but it controls the mouse pointer.
- if (mEnablePointerChoreographer) {
- // Always use DISPLAY_ID_NONE for touchpad events.
- // PointerChoreographer will make it target the correct the displayId later.
- resolvedViewport =
- getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
- mDisplayId = resolvedViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt;
- } else {
- mDisplayId = mPointerController->getDisplayId();
- if (auto v = config.getDisplayViewportById(*mDisplayId); v) {
- resolvedViewport = *v;
- }
- if (auto bounds = mPointerController->getBounds(); bounds) {
- boundsInLogicalDisplay = *bounds;
- }
- }
+ // Always use DISPLAY_ID_NONE for touchpad events.
+ // PointerChoreographer will make it target the correct the displayId later.
+ resolvedViewport = getContext()->getPolicy()->getPointerViewportForAssociatedDisplay();
+ mDisplayId = resolvedViewport ? std::make_optional(ui::ADISPLAY_ID_NONE) : std::nullopt;
}
mGestureConverter.setDisplayId(mDisplayId);
@@ -419,7 +389,6 @@
// The touchpad is being captured, so we need to tidy up any fake fingers etc. that are
// still being reported for a gesture in progress.
out += reset(when);
- mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
} else {
// We're transitioning from captured to uncaptured.
mCapturedEventConverter.reset();
@@ -529,7 +498,7 @@
return out;
}
-std::optional<int32_t> TouchpadInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> TouchpadInputMapper::getAssociatedDisplayId() {
return mDisplayId;
}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.h b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
index 9f272cf..f27895f 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -66,16 +66,12 @@
using MetricsIdentifier = std::tuple<uint16_t /*busId*/, uint16_t /*vendorId*/,
uint16_t /*productId*/, uint16_t /*version*/>;
- std::optional<int32_t> getAssociatedDisplayId() override;
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId() override;
private:
void resetGestureInterpreter(nsecs_t when);
explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig);
- // Constructor for testing.
- explicit TouchpadInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig,
- bool enablePointerChoreographer);
void updatePalmDetectionMetrics();
[[nodiscard]] std::list<NotifyArgs> sendHardwareState(nsecs_t when, nsecs_t readTime,
SelfContainedHardwareState schs);
@@ -83,7 +79,6 @@
std::unique_ptr<gestures::GestureInterpreter, void (*)(gestures::GestureInterpreter*)>
mGestureInterpreter;
- std::shared_ptr<PointerControllerInterface> mPointerController;
PropertyProvider mPropertyProvider;
TimerProvider mTimerProvider;
@@ -111,12 +106,10 @@
// Tracking IDs for touches that have at some point been reported as palms by the touchpad.
std::set<int32_t> mPalmTrackingIds;
- const bool mEnablePointerChoreographer;
-
// The display that events generated by this mapper should target. This can be set to
// ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e.
// std::nullopt), all events will be ignored.
- std::optional<int32_t> mDisplayId;
+ std::optional<ui::LogicalDisplayId> mDisplayId;
nsecs_t mGestureStartTime{0};
};
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 764bb56..ff95857 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -66,7 +66,6 @@
const InputDeviceContext& deviceContext, int32_t deviceId)
: mDeviceId(deviceId),
mReaderContext(readerContext),
- mPointerController(readerContext.getPointerController(deviceId)),
mEnableFlingStop(input_flags::enable_touchpad_fling_stop()) {
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mXAxisInfo);
deviceContext.getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mYAxisInfo);
@@ -174,7 +173,6 @@
const Gesture& gesture) {
float deltaX = gesture.details.move.dx;
float deltaY = gesture.details.move.dy;
- const auto [oldXCursorPosition, oldYCursorPosition] = mPointerController->getPosition();
if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) {
bool wasHoverCancelled = mIsHoverCancelled;
// Gesture will be cancelled if it started before the user started typing and
@@ -185,8 +183,7 @@
if (!wasHoverCancelled && mIsHoverCancelled) {
// This is the first event of the cancelled gesture, we won't return because we need to
// generate a HOVER_EXIT event
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- return exitHover(when, readTime, oldXCursorPosition, oldYCursorPosition);
+ return exitHover(when, readTime);
} else if (mIsHoverCancelled) {
return {};
}
@@ -202,30 +199,23 @@
(std::abs(deltaX) > 0 || std::abs(deltaY) > 0)) {
enableTapToClick(when);
}
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- mPointerController->move(deltaX, deltaY);
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
std::list<NotifyArgs> out;
const bool down = isPointerDown(mButtonState);
if (!down) {
- out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition);
+ out += enterHover(when, readTime);
}
- const auto [newXCursorPosition, newYCursorPosition] = mPointerController->getPosition();
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, newXCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, newYCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE;
out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
- /*pointerCount=*/1, &coords, newXCursorPosition,
- newYCursorPosition));
+ /*pointerCount=*/1, &coords));
return out;
}
@@ -233,15 +223,8 @@
const Gesture& gesture) {
std::list<NotifyArgs> out = {};
- mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
- mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
-
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
-
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
@@ -274,16 +257,15 @@
newButtonState |= actionButton;
pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS,
actionButton, newButtonState,
- /*pointerCount=*/1, &coords, xCursorPosition,
- yCursorPosition));
+ /*pointerCount=*/1, &coords));
}
}
if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) {
mDownTime = when;
- out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += exitHover(when, readTime);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, newButtonState, /* pointerCount= */ 1,
- &coords, xCursorPosition, yCursorPosition));
+ &coords));
}
out.splice(out.end(), pressEvents);
@@ -299,16 +281,15 @@
newButtonState &= ~actionButton;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
actionButton, newButtonState, /* pointerCount= */ 1,
- &coords, xCursorPosition, yCursorPosition));
+ &coords));
}
}
if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) {
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- newButtonState, /* pointerCount= */ 1, &coords,
- xCursorPosition, yCursorPosition));
+ newButtonState, /* pointerCount= */ 1, &coords));
mButtonState = newButtonState;
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += enterHover(when, readTime);
}
mButtonState = newButtonState;
return out;
@@ -316,12 +297,9 @@
std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
const bool pointerDown = isPointerDown(mButtonState);
@@ -332,17 +310,15 @@
if (mButtonState & button) {
newButtonState &= ~button;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
- button, newButtonState, /*pointerCount=*/1, &coords,
- xCursorPosition, yCursorPosition));
+ button, newButtonState, /*pointerCount=*/1, &coords));
}
}
mButtonState = 0;
if (pointerDown) {
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
- mButtonState, /*pointerCount=*/1, &coords, xCursorPosition,
- yCursorPosition));
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ mButtonState, /*pointerCount=*/1, &coords));
+ out += enterHover(when, readTime);
}
return out;
}
@@ -351,19 +327,15 @@
const Gesture& gesture) {
std::list<NotifyArgs> out;
PointerCoords& coords = mFakeFingerCoords[0];
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) {
- out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += exitHover(when, readTime);
mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
mDownTime = when;
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
}
@@ -378,8 +350,7 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy);
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
return out;
@@ -409,28 +380,23 @@
// avoid side effects (e.g. activation of UI elements).
// TODO(b/326056750): add an API for fling stops.
mFlingMayBeInProgress = false;
- const auto [xCursorPosition, yCursorPosition] =
- mPointerController->getPosition();
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
std::list<NotifyArgs> out;
mDownTime = when;
- out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
- // TODO(b/281106755): add a MotionClassification value for fling stops.
+ mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE;
+ out += exitHover(when, readTime);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/*actionButton=*/0, /*buttonState=*/0,
- /*pointerCount=*/1, &coords, xCursorPosition,
- yCursorPosition));
+ /*pointerCount=*/1, &coords));
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_CANCEL,
/*actionButton=*/0, /*buttonState=*/0,
- /*pointerCount=*/1, &coords, xCursorPosition,
- yCursorPosition));
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ /*pointerCount=*/1, &coords));
+ out += enterHover(when, readTime);
+ mCurrentClassification = MotionClassification::NONE;
return out;
} else {
// Use the tap down state of a fling gesture as an indicator that a contact
@@ -454,17 +420,15 @@
std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
NotifyMotionArgs args =
makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition);
+ mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data());
args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE;
out.push_back(args);
mCurrentClassification = MotionClassification::NONE;
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += enterHover(when, readTime);
return out;
}
@@ -474,26 +438,26 @@
float dx, float dy) {
std::list<NotifyArgs> out = {};
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
// If the user changes the number of fingers mid-way through a swipe (e.g. they start with
// three and then put a fourth finger down), the gesture library will treat it as two
// separate swipes with an appropriate lift event between them, so we don't have to worry
// about the finger count changing mid-swipe.
- out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += exitHover(when, readTime);
mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE;
mSwipeFingerCount = fingerCount;
constexpr float FAKE_FINGER_SPACING = 100;
- float xCoord = xCursorPosition - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
+ float xCoord = 0.f - FAKE_FINGER_SPACING * (mSwipeFingerCount - 1) / 2;
for (size_t i = 0; i < mSwipeFingerCount; i++) {
PointerCoords& coords = mFakeFingerCoords[i];
coords.clear();
+ // PointerChoreographer will add the cursor position to these pointers.
coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCoord);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
xCoord += FAKE_FINGER_SPACING;
}
@@ -503,14 +467,13 @@
fingerCount);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
for (size_t i = 1; i < mSwipeFingerCount; i++) {
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_DOWN |
(i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
/* actionButton= */ 0, mButtonState,
- /* pointerCount= */ i + 1, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition));
+ /* pointerCount= */ i + 1, mFakeFingerCoords.data()));
}
}
float rotatedDeltaX = dx, rotatedDeltaY = -dy;
@@ -528,7 +491,7 @@
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset);
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
mButtonState, /* pointerCount= */ mSwipeFingerCount,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
return out;
}
@@ -538,7 +501,6 @@
if (mCurrentClassification != MotionClassification::MULTI_FINGER_SWIPE) {
return out;
}
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, 0);
@@ -547,22 +509,20 @@
AMOTION_EVENT_ACTION_POINTER_UP |
((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
/* actionButton= */ 0, mButtonState, /* pointerCount= */ i,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
}
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0);
mCurrentClassification = MotionClassification::NONE;
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += enterHover(when, readTime);
mSwipeFingerCount = 0;
return out;
}
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
-
// Pinch gesture phases are reported a little differently from others, in that the same details
// struct is used for all phases of the gesture, just with different zoom_state values. When
// zoom_state is START or END, dz will always be 1, so we don't need to move the pointers in
@@ -574,28 +534,27 @@
gesture.details.pinch.zoom_state);
std::list<NotifyArgs> out;
- out += exitHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += exitHover(when, readTime);
mCurrentClassification = MotionClassification::PINCH;
mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX;
+ // PointerChoreographer will add the cursor position to these pointers.
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- xCursorPosition - mPinchFingerSeparation / 2);
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
- mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
- xCursorPosition + mPinchFingerSeparation / 2);
- mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
+ mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
mDownTime = when;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
return out;
}
@@ -604,77 +563,65 @@
}
mPinchFingerSeparation *= gesture.details.pinch.dz;
+ // PointerChoreographer will add the cursor position to these pointers.
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR,
gesture.details.pinch.dz);
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
- xCursorPosition - mPinchFingerSeparation / 2);
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
- mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
- xCursorPosition + mPinchFingerSeparation / 2);
- mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f - mPinchFingerSeparation / 2);
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
+ mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X, 0.f + mPinchFingerSeparation / 2);
+ mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, 0.f);
return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
- mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition)};
+ mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data())};
}
std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
std::list<NotifyArgs> out;
- const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
out.push_back(makeMotionArgs(when, readTime,
AMOTION_EVENT_ACTION_POINTER_UP |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
/*actionButton=*/0, mButtonState, /*pointerCount=*/2,
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mFakeFingerCoords.data()));
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
- mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data(),
- xCursorPosition, yCursorPosition));
+ mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data()));
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
mCurrentClassification = MotionClassification::NONE;
- out += enterHover(when, readTime, xCursorPosition, yCursorPosition);
+ out += enterHover(when, readTime);
return out;
}
-std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime,
- float xCursorPosition, float yCursorPosition) {
+std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime) {
if (!mIsHovering) {
mIsHovering = true;
- return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER, xCursorPosition,
- yCursorPosition)};
+ return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER)};
} else {
return {};
}
}
-std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime,
- float xCursorPosition, float yCursorPosition) {
+std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime) {
if (mIsHovering) {
mIsHovering = false;
- return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT, xCursorPosition,
- yCursorPosition)};
+ return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT)};
} else {
return {};
}
}
-NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action,
- float xCursorPosition, float yCursorPosition) {
+NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action) {
PointerCoords coords;
coords.clear();
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState,
- /*pointerCount=*/1, &coords, xCursorPosition, yCursorPosition);
+ /*pointerCount=*/1, &coords);
}
NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
uint32_t pointerCount,
- const PointerCoords* pointerCoords,
- float xCursorPosition, float yCursorPosition) {
+ const PointerCoords* pointerCoords) {
return {mReaderContext.getNextId(),
when,
readTime,
@@ -694,8 +641,8 @@
pointerCoords,
/* xPrecision= */ 1.0f,
/* yPrecision= */ 1.0f,
- xCursorPosition,
- yCursorPosition,
+ /* xCursorPosition= */ 0.f,
+ /* yCursorPosition= */ 0.f,
/* downTime= */ mDownTime,
/* videoFrames= */ {}};
}
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index c8f437e..829fb92 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -20,7 +20,6 @@
#include <list>
#include <memory>
-#include <PointerControllerInterface.h>
#include <android/input.h>
#include <utils/Timers.h>
@@ -41,8 +40,7 @@
*/
constexpr std::chrono::nanoseconds TAP_ENABLE_DELAY_NANOS = 400ms;
-// Converts Gesture structs from the gestures library into NotifyArgs and the appropriate
-// PointerController calls.
+// Converts Gesture structs from the gestures library into NotifyArgs.
class GestureConverter {
public:
GestureConverter(InputReaderContext& readerContext, const InputDeviceContext& deviceContext,
@@ -53,7 +51,7 @@
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
- void setDisplayId(std::optional<int32_t> displayId) { mDisplayId = displayId; }
+ void setDisplayId(std::optional<ui::LogicalDisplayId> displayId) { mDisplayId = displayId; }
void setBoundsInLogicalDisplay(FloatRect bounds) { mBoundsInLogicalDisplay = bounds; }
@@ -85,18 +83,14 @@
const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime);
- [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime,
- float xCursorPosition, float yCursorPosition);
- [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime,
- float xCursorPosition, float yCursorPosition);
+ [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime);
+ [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime);
- NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action,
- float xCursorPosition, float yCursorPosition);
+ NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action);
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
- uint32_t pointerCount, const PointerCoords* pointerCoords,
- float xCursorPosition, float yCursorPosition);
+ uint32_t pointerCount, const PointerCoords* pointerCoords);
void enableTapToClick(nsecs_t when);
bool mIsHoverCancelled{false};
@@ -104,10 +98,9 @@
const int32_t mDeviceId;
InputReaderContext& mReaderContext;
- std::shared_ptr<PointerControllerInterface> mPointerController;
const bool mEnableFlingStop;
- std::optional<int32_t> mDisplayId;
+ std::optional<ui::LogicalDisplayId> mDisplayId;
FloatRect mBoundsInLogicalDisplay{};
ui::Rotation mOrientation = ui::ROTATION_0;
RawAbsoluteAxisInfo mXAxisInfo;
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index a544fa3..6df339e 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -396,14 +396,16 @@
IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback},
KeyEvent::KeyEvent,
};
- use std::sync::{Arc, RwLock, RwLockWriteGuard};
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{atomic::AtomicBool, atomic::Ordering, Arc, RwLock, RwLockWriteGuard};
+ use std::time::Duration;
#[derive(Default)]
struct TestCallbacksInner {
last_modifier_state: u32,
last_locked_modifier_state: u32,
last_event: Option<KeyEvent>,
- test_thread: Option<TestThread>,
+ test_thread: Option<FakeCppThread>,
}
#[derive(Default, Clone)]
@@ -438,13 +440,9 @@
self.0.read().unwrap().last_locked_modifier_state
}
- pub fn is_thread_created(&self) -> bool {
- self.0.read().unwrap().test_thread.is_some()
- }
-
- pub fn is_thread_finished(&self) -> bool {
+ pub fn is_thread_running(&self) -> bool {
if let Some(test_thread) = &self.0.read().unwrap().test_thread {
- return test_thread.is_finish_called();
+ return test_thread.is_running();
}
false
}
@@ -468,41 +466,101 @@
fn createInputFilterThread(
&self,
- _callback: &Strong<dyn IInputThreadCallback>,
+ callback: &Strong<dyn IInputThreadCallback>,
) -> std::result::Result<Strong<dyn IInputThread>, binder::Status> {
- let test_thread = TestThread::new();
+ let test_thread = FakeCppThread::new(callback.clone());
+ test_thread.start_looper();
self.inner().test_thread = Some(test_thread.clone());
Result::Ok(BnInputThread::new_binder(test_thread, BinderFeatures::default()))
}
}
#[derive(Default)]
- struct TestThreadInner {
- is_finish_called: bool,
+ struct FakeCppThreadInner {
+ join_handle: Option<std::thread::JoinHandle<()>>,
}
- #[derive(Default, Clone)]
- struct TestThread(Arc<RwLock<TestThreadInner>>);
+ #[derive(Clone)]
+ struct FakeCppThread {
+ callback: Arc<RwLock<Strong<dyn IInputThreadCallback>>>,
+ inner: Arc<RwLock<FakeCppThreadInner>>,
+ exit_flag: Arc<AtomicBool>,
+ }
- impl Interface for TestThread {}
+ impl Interface for FakeCppThread {}
- impl TestThread {
- pub fn new() -> Self {
- Default::default()
+ impl FakeCppThread {
+ pub fn new(callback: Strong<dyn IInputThreadCallback>) -> Self {
+ let thread = Self {
+ callback: Arc::new(RwLock::new(callback)),
+ inner: Arc::new(RwLock::new(FakeCppThreadInner { join_handle: None })),
+ exit_flag: Arc::new(AtomicBool::new(true)),
+ };
+ thread.create_looper();
+ thread
}
- fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> {
- self.0.write().unwrap()
+ fn inner(&self) -> RwLockWriteGuard<'_, FakeCppThreadInner> {
+ self.inner.write().unwrap()
}
- pub fn is_finish_called(&self) -> bool {
- self.0.read().unwrap().is_finish_called
+ fn create_looper(&self) {
+ let clone = self.clone();
+ let join_handle = std::thread::Builder::new()
+ .name("fake_cpp_thread".to_string())
+ .spawn(move || loop {
+ if !clone.exit_flag.load(Ordering::Relaxed) {
+ clone.loop_once();
+ }
+ })
+ .unwrap();
+ self.inner().join_handle = Some(join_handle);
+ // Sleep until the looper thread starts
+ std::thread::sleep(Duration::from_millis(10));
+ }
+
+ pub fn start_looper(&self) {
+ self.exit_flag.store(false, Ordering::Relaxed);
+ }
+
+ pub fn stop_looper(&self) {
+ self.exit_flag.store(true, Ordering::Relaxed);
+ if let Some(join_handle) = &self.inner.read().unwrap().join_handle {
+ join_handle.thread().unpark();
+ }
+ }
+
+ pub fn is_running(&self) -> bool {
+ !self.exit_flag.load(Ordering::Relaxed)
+ }
+
+ fn loop_once(&self) {
+ let _ = self.callback.read().unwrap().loopOnce();
}
}
- impl IInputThread for TestThread {
+ impl IInputThread for FakeCppThread {
fn finish(&self) -> binder::Result<()> {
- self.inner().is_finish_called = true;
+ self.stop_looper();
+ Result::Ok(())
+ }
+
+ fn wake(&self) -> binder::Result<()> {
+ if let Some(join_handle) = &self.inner.read().unwrap().join_handle {
+ join_handle.thread().unpark();
+ }
+ Result::Ok(())
+ }
+
+ fn sleepUntil(&self, wake_up_time: i64) -> binder::Result<()> {
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ if wake_up_time == i64::MAX {
+ std::thread::park();
+ } else {
+ let duration_now = Duration::from_nanos(now as u64);
+ let duration_wake_up = Duration::from_nanos(wake_up_time as u64);
+ std::thread::park_timeout(duration_wake_up - duration_now);
+ }
Result::Ok(())
}
}
diff --git a/services/inputflinger/rust/input_filter_thread.rs b/services/inputflinger/rust/input_filter_thread.rs
index 2d503ae..34f9b25 100644
--- a/services/inputflinger/rust/input_filter_thread.rs
+++ b/services/inputflinger/rust/input_filter_thread.rs
@@ -33,8 +33,6 @@
use log::{debug, error};
use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
use std::sync::{Arc, RwLock, RwLockWriteGuard};
-use std::time::Duration;
-use std::{thread, thread::Thread};
/// Interface to receive callback from Input filter thread
pub trait ThreadCallback {
@@ -54,15 +52,18 @@
thread_creator: InputFilterThreadCreator,
thread_callback_handler: ThreadCallbackHandler,
inner: Arc<RwLock<InputFilterThreadInner>>,
+ looper: Arc<RwLock<Looper>>,
}
struct InputFilterThreadInner {
- cpp_thread: Option<Strong<dyn IInputThread>>,
- looper: Option<Thread>,
next_timeout: i64,
is_finishing: bool,
}
+struct Looper {
+ cpp_thread: Option<Strong<dyn IInputThread>>,
+}
+
impl InputFilterThread {
/// Create a new InputFilterThread instance.
/// NOTE: This will create a new thread. Clone the existing instance to reuse the same thread.
@@ -71,11 +72,10 @@
thread_creator,
thread_callback_handler: ThreadCallbackHandler::new(),
inner: Arc::new(RwLock::new(InputFilterThreadInner {
- cpp_thread: None,
- looper: None,
next_timeout: i64::MAX,
is_finishing: false,
})),
+ looper: Arc::new(RwLock::new(Looper { cpp_thread: None })),
}
}
@@ -83,12 +83,17 @@
/// time on the input filter thread.
/// {@see ThreadCallback.notify_timeout_expired(...)}
pub fn request_timeout_at_time(&self, when_nanos: i64) {
- let filter_thread = &mut self.filter_thread();
- if when_nanos < filter_thread.next_timeout {
- filter_thread.next_timeout = when_nanos;
- if let Some(looper) = &filter_thread.looper {
- looper.unpark();
+ let mut need_wake = false;
+ {
+ // acquire filter lock
+ let filter_thread = &mut self.filter_thread();
+ if when_nanos < filter_thread.next_timeout {
+ filter_thread.next_timeout = when_nanos;
+ need_wake = true;
}
+ } // release filter lock
+ if need_wake {
+ self.wake();
}
}
@@ -120,29 +125,36 @@
fn start(&self) {
debug!("InputFilterThread: start thread");
- let filter_thread = &mut self.filter_thread();
- if filter_thread.cpp_thread.is_none() {
- filter_thread.cpp_thread = Some(self.thread_creator.create(
- &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()),
- ));
- filter_thread.looper = None;
- filter_thread.is_finishing = false;
- }
+ {
+ // acquire looper lock
+ let looper = &mut self.looper();
+ if looper.cpp_thread.is_none() {
+ looper.cpp_thread = Some(self.thread_creator.create(
+ &BnInputThreadCallback::new_binder(self.clone(), BinderFeatures::default()),
+ ));
+ }
+ } // release looper lock
+ self.set_finishing(false);
}
fn stop(&self) {
debug!("InputFilterThread: stop thread");
+ self.set_finishing(true);
+ self.wake();
+ {
+ // acquire looper lock
+ let looper = &mut self.looper();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.finish();
+ }
+ // Clear all references
+ looper.cpp_thread = None;
+ } // release looper lock
+ }
+
+ fn set_finishing(&self, is_finishing: bool) {
let filter_thread = &mut self.filter_thread();
- filter_thread.is_finishing = true;
- if let Some(looper) = &filter_thread.looper {
- looper.unpark();
- }
- if let Some(cpp_thread) = &filter_thread.cpp_thread {
- let _ = cpp_thread.finish();
- }
- // Clear all references
- filter_thread.cpp_thread = None;
- filter_thread.looper = None;
+ filter_thread.is_finishing = is_finishing;
}
fn loop_once(&self, now: i64) {
@@ -163,25 +175,34 @@
wake_up_time = filter_thread.next_timeout;
}
}
- if filter_thread.looper.is_none() {
- filter_thread.looper = Some(std::thread::current());
- }
} // release thread lock
if timeout_expired {
self.thread_callback_handler.notify_timeout_expired(now);
}
- if wake_up_time == i64::MAX {
- thread::park();
- } else {
- let duration_now = Duration::from_nanos(now as u64);
- let duration_wake_up = Duration::from_nanos(wake_up_time as u64);
- thread::park_timeout(duration_wake_up - duration_now);
- }
+ self.sleep_until(wake_up_time);
}
fn filter_thread(&self) -> RwLockWriteGuard<'_, InputFilterThreadInner> {
self.inner.write().unwrap()
}
+
+ fn sleep_until(&self, when_nanos: i64) {
+ let looper = self.looper.read().unwrap();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.sleepUntil(when_nanos);
+ }
+ }
+
+ fn wake(&self) {
+ let looper = self.looper.read().unwrap();
+ if let Some(cpp_thread) = &looper.cpp_thread {
+ let _ = cpp_thread.wake();
+ }
+ }
+
+ fn looper(&self) -> RwLockWriteGuard<'_, Looper> {
+ self.looper.write().unwrap()
+ }
}
impl Interface for InputFilterThread {}
@@ -252,165 +273,64 @@
#[cfg(test)]
mod tests {
- use crate::input_filter::test_callbacks::TestCallbacks;
- use crate::input_filter_thread::{
- test_thread::TestThread, test_thread_callback::TestThreadCallback,
- };
+ use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator};
+ use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread};
+ use binder::Strong;
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{Arc, RwLock};
+ use std::time::Duration;
#[test]
fn test_register_callback_creates_cpp_thread() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback);
- assert!(test_callbacks.is_thread_created());
+ test_thread.register_thread_callback(Box::new(test_thread_callback));
+ assert!(test_callbacks.is_thread_running());
}
#[test]
fn test_unregister_callback_finishes_cpp_thread() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback.clone());
- test_thread.unregister_thread_callback(test_thread_callback);
- assert!(test_callbacks.is_thread_finished());
+ test_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
+ test_thread.unregister_thread_callback(Box::new(test_thread_callback));
+ assert!(!test_callbacks.is_thread_running());
}
#[test]
fn test_notify_timeout_called_after_timeout_expired() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback.clone());
- test_thread.start_looper();
+ test_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
- test_thread.request_timeout_at_time(500);
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_milliseconds();
+ test_thread.request_timeout_at_time((now + 10) * 1000000);
- test_thread.move_time_forward(500);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_millis(100));
assert!(test_thread_callback.is_notify_timeout_called());
}
#[test]
fn test_notify_timeout_not_called_before_timeout_expired() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let test_thread_callback = TestThreadCallback::new();
- test_thread.register_thread_callback(test_thread_callback.clone());
- test_thread.start_looper();
+ test_thread.register_thread_callback(Box::new(test_thread_callback.clone()));
- test_thread.request_timeout_at_time(500);
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_milliseconds();
+ test_thread.request_timeout_at_time((now + 100) * 1000000);
- test_thread.move_time_forward(100);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_millis(10));
assert!(!test_thread_callback.is_notify_timeout_called());
}
-}
-#[cfg(test)]
-pub mod test_thread {
-
- use crate::input_filter::{test_callbacks::TestCallbacks, InputFilterThreadCreator};
- use crate::input_filter_thread::{test_thread_callback::TestThreadCallback, InputFilterThread};
- use binder::Strong;
- use std::sync::{
- atomic::AtomicBool, atomic::AtomicI64, atomic::Ordering, Arc, RwLock, RwLockWriteGuard,
- };
- use std::time::Duration;
-
- #[derive(Clone)]
- pub struct TestThread {
- input_thread: InputFilterThread,
- inner: Arc<RwLock<TestThreadInner>>,
- exit_flag: Arc<AtomicBool>,
- now: Arc<AtomicI64>,
- }
-
- struct TestThreadInner {
- join_handle: Option<std::thread::JoinHandle<()>>,
- }
-
- impl TestThread {
- pub fn new(callbacks: TestCallbacks) -> TestThread {
- Self {
- input_thread: InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(
- RwLock::new(Strong::new(Box::new(callbacks))),
- ))),
- inner: Arc::new(RwLock::new(TestThreadInner { join_handle: None })),
- exit_flag: Arc::new(AtomicBool::new(false)),
- now: Arc::new(AtomicI64::new(0)),
- }
- }
-
- fn inner(&self) -> RwLockWriteGuard<'_, TestThreadInner> {
- self.inner.write().unwrap()
- }
-
- pub fn get_input_thread(&self) -> InputFilterThread {
- self.input_thread.clone()
- }
-
- pub fn register_thread_callback(&self, thread_callback: TestThreadCallback) {
- self.input_thread.register_thread_callback(Box::new(thread_callback));
- }
-
- pub fn unregister_thread_callback(&self, thread_callback: TestThreadCallback) {
- self.input_thread.unregister_thread_callback(Box::new(thread_callback));
- }
-
- pub fn start_looper(&self) {
- self.exit_flag.store(false, Ordering::Relaxed);
- let clone = self.clone();
- let join_handle = std::thread::Builder::new()
- .name("test_thread".to_string())
- .spawn(move || {
- while !clone.exit_flag.load(Ordering::Relaxed) {
- clone.loop_once();
- }
- })
- .unwrap();
- self.inner().join_handle = Some(join_handle);
- // Sleep until the looper thread starts
- std::thread::sleep(Duration::from_millis(10));
- }
-
- pub fn stop_looper(&self) {
- self.exit_flag.store(true, Ordering::Relaxed);
- {
- let mut inner = self.inner();
- if let Some(join_handle) = &inner.join_handle {
- join_handle.thread().unpark();
- }
- inner.join_handle.take().map(std::thread::JoinHandle::join);
- inner.join_handle = None;
- }
- self.exit_flag.store(false, Ordering::Relaxed);
- }
-
- pub fn move_time_forward(&self, value: i64) {
- let _ = self.now.fetch_add(value, Ordering::Relaxed);
- self.dispatch_next();
- }
-
- pub fn dispatch_next(&self) {
- if let Some(join_handle) = &self.inner().join_handle {
- join_handle.thread().unpark();
- }
- // Sleep until the looper thread runs a loop
- std::thread::sleep(Duration::from_millis(10));
- }
-
- fn loop_once(&self) {
- self.input_thread.loop_once(self.now.load(Ordering::Relaxed));
- }
-
- pub fn request_timeout_at_time(&self, when_nanos: i64) {
- self.input_thread.request_timeout_at_time(when_nanos);
- }
+ fn get_thread(callbacks: TestCallbacks) -> InputFilterThread {
+ InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(RwLock::new(Strong::new(
+ Box::new(callbacks),
+ )))))
}
}
diff --git a/services/inputflinger/rust/slow_keys_filter.rs b/services/inputflinger/rust/slow_keys_filter.rs
index 09fbf40..0f18a2f 100644
--- a/services/inputflinger/rust/slow_keys_filter.rs
+++ b/services/inputflinger/rust/slow_keys_filter.rs
@@ -207,13 +207,19 @@
#[cfg(test)]
mod tests {
- use crate::input_filter::{test_callbacks::TestCallbacks, test_filter::TestFilter, Filter};
- use crate::input_filter_thread::test_thread::TestThread;
+ use crate::input_filter::{
+ test_callbacks::TestCallbacks, test_filter::TestFilter, Filter, InputFilterThreadCreator,
+ };
+ use crate::input_filter_thread::InputFilterThread;
use crate::slow_keys_filter::{SlowKeysFilter, POLICY_FLAG_DISABLE_KEY_REPEAT};
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
+ use binder::Strong;
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use nix::{sys::time::TimeValLike, time::clock_gettime, time::ClockId};
+ use std::sync::{Arc, RwLock};
+ use std::time::Duration;
static BASE_KEY_EVENT: KeyEvent = KeyEvent {
id: 1,
@@ -231,18 +237,19 @@
metaState: 0,
};
+ static SLOW_KEYS_THRESHOLD_NS: i64 = 100 * 1000000; // 100 ms
+
#[test]
fn test_is_notify_key_for_internal_keyboard_not_blocked() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
let mut filter = setup_filter_with_internal_device(
Box::new(next.clone()),
test_thread.clone(),
- 1, /* device_id */
- 100, /* threshold */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
let event = KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT };
filter.notify_key(&event);
@@ -252,15 +259,14 @@
#[test]
fn test_is_notify_key_for_external_stylus_not_blocked() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
test_thread.clone(),
- 1, /* device_id */
- 100, /* threshold */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
let event =
KeyEvent { action: KeyEventAction::DOWN, source: Source::STYLUS, ..BASE_KEY_EVENT };
@@ -271,89 +277,115 @@
#[test]
fn test_notify_key_for_external_keyboard_when_key_pressed_for_threshold_time() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
test_thread.clone(),
- 1, /* device_id */
- 100, /* threshold */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
-
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
+ let down_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: down_time,
+ eventTime: down_time,
+ ..BASE_KEY_EVENT
+ });
assert!(next.last_event().is_none());
- test_thread.dispatch_next();
- test_thread.move_time_forward(100);
-
- test_thread.stop_looper();
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
assert_eq!(
next.last_event().unwrap(),
KeyEvent {
action: KeyEventAction::DOWN,
- downTime: 100,
- eventTime: 100,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: down_time + SLOW_KEYS_THRESHOLD_NS,
policyFlags: POLICY_FLAG_DISABLE_KEY_REPEAT,
..BASE_KEY_EVENT
}
);
+
+ let up_time = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ });
+
+ assert_eq!(
+ next.last_event().unwrap(),
+ KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: down_time + SLOW_KEYS_THRESHOLD_NS,
+ eventTime: up_time,
+ ..BASE_KEY_EVENT
+ }
+ );
}
#[test]
fn test_notify_key_for_external_keyboard_when_key_not_pressed_for_threshold_time() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
test_thread.clone(),
- 1, /* device_id */
- 100, /* threshold */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
+ let mut now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
- test_thread.dispatch_next();
+ std::thread::sleep(Duration::from_nanos(SLOW_KEYS_THRESHOLD_NS as u64 / 2));
- test_thread.move_time_forward(10);
+ now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::UP,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
- filter.notify_key(&KeyEvent { action: KeyEventAction::UP, ..BASE_KEY_EVENT });
- test_thread.dispatch_next();
-
- test_thread.stop_looper();
assert!(next.last_event().is_none());
}
#[test]
fn test_notify_key_for_external_keyboard_when_device_removed_before_threshold_time() {
let test_callbacks = TestCallbacks::new();
- let test_thread = TestThread::new(test_callbacks.clone());
+ let test_thread = get_thread(test_callbacks.clone());
let next = TestFilter::new();
let mut filter = setup_filter_with_external_device(
Box::new(next.clone()),
test_thread.clone(),
- 1, /* device_id */
- 100, /* threshold */
+ 1, /* device_id */
+ SLOW_KEYS_THRESHOLD_NS,
);
- test_thread.start_looper();
- filter.notify_key(&KeyEvent { action: KeyEventAction::DOWN, ..BASE_KEY_EVENT });
- assert!(next.last_event().is_none());
- test_thread.dispatch_next();
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap().num_nanoseconds();
+ filter.notify_key(&KeyEvent {
+ action: KeyEventAction::DOWN,
+ downTime: now,
+ eventTime: now,
+ ..BASE_KEY_EVENT
+ });
filter.notify_devices_changed(&[]);
- test_thread.dispatch_next();
+ std::thread::sleep(Duration::from_nanos(2 * SLOW_KEYS_THRESHOLD_NS as u64));
- test_thread.move_time_forward(100);
-
- test_thread.stop_looper();
assert!(next.last_event().is_none());
}
fn setup_filter_with_external_device(
next: Box<dyn Filter + Send + Sync>,
- test_thread: TestThread,
+ test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
) -> SlowKeysFilter {
@@ -367,7 +399,7 @@
fn setup_filter_with_internal_device(
next: Box<dyn Filter + Send + Sync>,
- test_thread: TestThread,
+ test_thread: InputFilterThread,
device_id: i32,
threshold: i64,
) -> SlowKeysFilter {
@@ -381,12 +413,18 @@
fn setup_filter_with_devices(
next: Box<dyn Filter + Send + Sync>,
- test_thread: TestThread,
+ test_thread: InputFilterThread,
devices: &[DeviceInfo],
threshold: i64,
) -> SlowKeysFilter {
- let mut filter = SlowKeysFilter::new(next, threshold, test_thread.get_input_thread());
+ let mut filter = SlowKeysFilter::new(next, threshold, test_thread);
filter.notify_devices_changed(devices);
filter
}
+
+ fn get_thread(callbacks: TestCallbacks) -> InputFilterThread {
+ InputFilterThread::new(InputFilterThreadCreator::new(Arc::new(RwLock::new(Strong::new(
+ Box::new(callbacks),
+ )))))
+ }
}
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 09ae6dd..9b5db23 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -22,6 +22,15 @@
default_applicable_licenses: ["frameworks_native_license"],
}
+// Source files shared with InputDispatcher's benchmarks and fuzzers
+filegroup {
+ name: "inputdispatcher_common_test_sources",
+ srcs: [
+ "FakeInputDispatcherPolicy.cpp",
+ "FakeWindows.cpp",
+ ],
+}
+
cc_test {
name: "inputflinger_tests",
host_supported: true,
@@ -38,6 +47,7 @@
"libinputflinger_defaults",
],
srcs: [
+ ":inputdispatcher_common_test_sources",
"AnrTracker_test.cpp",
"CapturedTouchpadEventConverter_test.cpp",
"CursorInputMapper_test.cpp",
@@ -57,6 +67,8 @@
"InputProcessorConverter_test.cpp",
"InputDispatcher_test.cpp",
"InputReader_test.cpp",
+ "InputTraceSession.cpp",
+ "InputTracingTest.cpp",
"InstrumentedInputReader.cpp",
"LatencyTracker_test.cpp",
"MultiTouchMotionAccumulator_test.cpp",
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index c44f880..72b5573 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -29,7 +29,6 @@
#include <linux/input.h>
#include <utils/Timers.h>
-#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InputReaderBase.h"
#include "InterfaceMocks.h"
@@ -51,8 +50,8 @@
constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE;
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
-constexpr int32_t DISPLAY_ID = 0;
-constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
+constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID = ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
constexpr int32_t DISPLAY_WIDTH = 480;
constexpr int32_t DISPLAY_HEIGHT = 800;
@@ -157,12 +156,9 @@
mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
}
- virtual bool isPointerChoreographerEnabled() { return false; }
-
void createMapper() {
createDevice();
- mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration,
- isPointerChoreographerEnabled());
+ mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
void setPointerCapture(bool enabled) {
@@ -200,8 +196,6 @@
input_flags::enable_new_mouse_pointer_ballistics(false);
CursorInputMapperUnitTestBase::SetUp();
}
-
- bool isPointerChoreographerEnabled() override { return false; }
};
TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) {
@@ -325,12 +319,9 @@
// Disable pointer capture. Afterwards, events should be generated the usual way.
setPointerCapture(false);
- const auto expectedCoords = CursorInputMapperUnitTest::isPointerChoreographerEnabled()
- ? WithCoords(0, 0)
- : WithCoords(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
- const auto expectedCursorPosition = CursorInputMapperUnitTest::isPointerChoreographerEnabled()
- ? WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION)
- : WithCursorPosition(INITIAL_CURSOR_X + 10.0f, INITIAL_CURSOR_Y + 20.0f);
+ const auto expectedCoords = WithCoords(0, 0);
+ const auto expectedCursorPosition =
+ WithCursorPosition(INVALID_CURSOR_POSITION, INVALID_CURSOR_POSITION);
args.clear();
args += process(EV_REL, REL_X, 10);
args += process(EV_REL, REL_Y, 20);
@@ -342,42 +333,6 @@
WithRelativeMotion(10.0f, 20.0f)))));
}
-TEST_F(CursorInputMapperUnitTest,
- PopulateDeviceInfoReturnsRangeFromPointerControllerInPointerMode) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- mFakePolicy->clearViewports();
- mFakePointerController->clearBounds();
- createMapper();
-
- InputDeviceInfo info;
- mMapper->populateDeviceInfo(info);
-
- // Initially there should not be a valid motion range because there's no viewport or pointer
- // bounds.
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
- AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-
- // When the bounds are set, then there should be a valid motion range.
- mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
- mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
- std::list<NotifyArgs> args =
- mMapper->reconfigure(systemTime(), mReaderConfiguration,
- InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_THAT(args, testing::IsEmpty());
-
- InputDeviceInfo info2;
- mMapper->populateDeviceInfo(info2);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 1,
- 800 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 2,
- 480 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
- AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) {
mPropertyMap.addProperty("cursor.mode", "navigation");
createMapper();
@@ -649,334 +604,9 @@
ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1));
}
-TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesOrientationChanges) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- DisplayViewport viewport = createPrimaryViewport(ui::Rotation::Rotation90);
- mFakePointerController->setDisplayViewport(viewport);
- mReaderConfiguration.setDisplayViewports({viewport});
- createMapper();
-
- // Verify that the coordinates are rotated.
- std::list<NotifyArgs> args;
- 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), WithSource(AINPUT_SOURCE_MOUSE),
- WithRelativeMotion(-20.0f, 10.0f)))));
-
- // Enable Pointer Capture.
- setPointerCapture(true);
-
- // Move and verify rotation is not applied.
- 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(ACTION_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithCoords(10.0f, 20.0f)))));
-}
-
-TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) {
- DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
- DisplayViewport secondaryViewport = createSecondaryViewport();
- mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
- // Set up the secondary display as the display on which the pointer should be shown. The
- // InputDevice is not associated with any display.
- mFakePointerController->setDisplayViewport(secondaryViewport);
- mFakePointerController->setPosition(100, 200);
- createMapper();
-
- // Ensure input events are generated for the secondary display.
- std::list<NotifyArgs> args;
- 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), WithSource(AINPUT_SOURCE_MOUSE),
- WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) {
- DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
- DisplayViewport secondaryViewport = createSecondaryViewport();
- mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
- // Set up the secondary display as the display on which the pointer should be shown.
- mFakePointerController->setDisplayViewport(secondaryViewport);
- mFakePointerController->setPosition(100, 200);
- createDevice();
- // Associate the InputDevice with the secondary display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<
- CursorInputMapper>(deviceContext, mReaderConfiguration,
- CursorInputMapperUnitTest::isPointerChoreographerEnabled());
-
- // Ensure input events are generated for the secondary display.
- std::list<NotifyArgs> args;
- 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), WithSource(AINPUT_SOURCE_MOUSE),
- WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdIgnoresEventsForMismatchedPointerDisplay) {
- DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
- DisplayViewport secondaryViewport = createSecondaryViewport();
- mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
- // Set up the primary display as the display on which the pointer should be shown.
- mFakePointerController->setDisplayViewport(primaryViewport);
- createDevice();
- // Associate the InputDevice with the secondary display.
- ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
- mMapper = createInputMapper<
- CursorInputMapper>(deviceContext, mReaderConfiguration,
- CursorInputMapperUnitTest::isPointerChoreographerEnabled());
-
- // The mapper should not generate any events because it is associated with a display that is
- // different from the pointer display.
- std::list<NotifyArgs> args;
- 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, testing::IsEmpty());
-}
-
-TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- std::list<NotifyArgs> args;
-
- // press BTN_LEFT, release BTN_LEFT
- args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
- args.clear();
-
- args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f)))));
- args.clear();
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
- args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
- AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
- args.clear();
-
- args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
- WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
- args.clear();
-
- args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithButtonState(0),
- WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithButtonState(0),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
-}
-
-class CursorInputMapperButtonKeyTest
- : public CursorInputMapperUnitTest,
- public testing::WithParamInterface<
- std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
- int32_t /*expectedKeyCode*/>> {
- virtual bool isPointerChoreographerEnabled() override { return false; }
-};
-
-TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) {
- auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- std::list<NotifyArgs> args;
-
- args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
- WithKeyCode(expectedKeyCode))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithButtonState(expectedButtonState),
- WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithButtonState(expectedButtonState),
- WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
- args.clear();
-
- args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0);
- args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithButtonState(0), WithCoords(100.0f, 200.0f),
- WithPressure(0.0f))),
- VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
- WithKeyCode(expectedKeyCode)))));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- SideExtraBackAndForward, CursorInputMapperButtonKeyTest,
- testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
- std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
- std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
- std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
- AKEYCODE_FORWARD)));
-
-TEST_F(CursorInputMapperUnitTest, ProcessShouldMoveThePointerAroundInPointerMode) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- std::list<NotifyArgs> args;
-
- 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);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(110.0f, 220.0f), WithPressure(0.0f), WithSize(0.0f),
- WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
- WithOrientation(0.0f), WithDistance(0.0f)))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-/**
- * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
- * pointer acceleration or speed processing should not be applied.
- */
-TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
- /*highThreshold=*/100.f, /*acceleration=*/10.f);
- mReaderConfiguration.pointerVelocityControlParameters = testParams;
- mFakePolicy->setVelocityControlParams(testParams);
- createMapper();
-
- std::list<NotifyArgs> args;
-
- // Move and verify scale is applied.
- 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);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE),
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
- NotifyMotionArgs motionArgs = std::get<NotifyMotionArgs>(args.front());
- const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_GT(relX, 10);
- ASSERT_GT(relY, 20);
- args.clear();
-
- // Enable Pointer Capture
- setPointerCapture(true);
-
- // Move and verify scale is not applied.
- 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);
- EXPECT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
- WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(10, 20)))));
-}
-
-// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
-// logic can be removed.
-class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_new_mouse_pointer_ballistics(false);
- CursorInputMapperUnitTestBase::SetUp();
- }
-
- bool isPointerChoreographerEnabled() override { return true; }
-};
-
-TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
+TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsRangeFromPolicy) {
mPropertyMap.addProperty("cursor.mode", "pointer");
mFakePolicy->clearViewports();
- mFakePointerController->clearBounds();
createMapper();
InputDeviceInfo info;
@@ -1009,14 +639,12 @@
AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
}
-TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
+TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) {
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
DisplayViewport secondaryViewport = createSecondaryViewport();
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
- mFakePointerController->setDisplayViewport(secondaryViewport);
- mFakePointerController->setPosition(100, 200);
createDevice();
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
@@ -1032,13 +660,12 @@
WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f)))));
}
-TEST_F(CursorInputMapperUnitTestWithChoreographer,
+TEST_F(CursorInputMapperUnitTest,
ConfigureDisplayIdShouldGenerateEventForMismatchedPointerDisplay) {
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
DisplayViewport secondaryViewport = createSecondaryViewport();
mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
// Set up the primary display as the display on which the pointer should be shown.
- mFakePointerController->setDisplayViewport(primaryViewport);
createDevice();
// Associate the InputDevice with the secondary display.
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
@@ -1057,13 +684,10 @@
WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f)))));
}
-TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtonsWithZeroCoords) {
mPropertyMap.addProperty("cursor.mode", "pointer");
createMapper();
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
std::list<NotifyArgs> args;
// press BTN_LEFT, release BTN_LEFT
@@ -1147,21 +771,17 @@
WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
}
-class CursorInputMapperButtonKeyTestWithChoreographer
- : public CursorInputMapperUnitTestWithChoreographer,
+class CursorInputMapperButtonKeyTest
+ : public CursorInputMapperUnitTest,
public testing::WithParamInterface<
std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
int32_t /*expectedKeyCode*/>> {};
-TEST_P(CursorInputMapperButtonKeyTestWithChoreographer,
- ProcessShouldHandleButtonKeyWithZeroCoords) {
+TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKeyWithZeroCoords) {
auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
mPropertyMap.addProperty("cursor.mode", "pointer");
createMapper();
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
std::list<NotifyArgs> args;
args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
@@ -1195,20 +815,17 @@
}
INSTANTIATE_TEST_SUITE_P(
- SideExtraBackAndForward, CursorInputMapperButtonKeyTestWithChoreographer,
+ SideExtraBackAndForward, CursorInputMapperButtonKeyTest,
testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
AKEYCODE_FORWARD)));
-TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
+TEST_F(CursorInputMapperUnitTest, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
mPropertyMap.addProperty("cursor.mode", "pointer");
createMapper();
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
std::list<NotifyArgs> args;
args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
@@ -1223,7 +840,11 @@
WithOrientation(0.0f), WithDistance(0.0f)))));
}
-TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
+/**
+ * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
+ * pointer acceleration or speed processing should not be applied.
+ */
+TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
mPropertyMap.addProperty("cursor.mode", "pointer");
const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
/*highThreshold=*/100.f, /*acceleration=*/10.f);
@@ -1267,7 +888,7 @@
ASSERT_EQ(20, relY2);
}
-TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
+TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) {
// Set up the default display.
mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
@@ -1279,9 +900,6 @@
createMapper();
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
// Ensure input events are generated without display ID or coords, because they will be decided
// later by PointerChoreographer.
std::list<NotifyArgs> args;
@@ -1291,7 +909,7 @@
EXPECT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ui::ADISPLAY_ID_NONE),
WithCoords(0.0f, 0.0f)))));
}
@@ -1302,8 +920,6 @@
input_flags::enable_new_mouse_pointer_ballistics(true);
CursorInputMapperUnitTestBase::SetUp();
}
-
- bool isPointerChoreographerEnabled() override { return true; }
};
TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) {
@@ -1422,14 +1038,11 @@
} // namespace
+// --- BluetoothCursorInputMapperUnitTest ---
+
class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
protected:
- void SetUp() override {
- SetUpWithBus(BUS_BLUETOOTH);
-
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
- }
+ void SetUp() override { SetUpWithBus(BUS_BLUETOOTH); }
};
TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) {
@@ -1537,123 +1150,4 @@
argsList.clear();
}
-// --- BluetoothCursorInputMapperUnitTestWithChoreographer ---
-
-class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
-protected:
- void SetUp() override {
- SetUpWithBus(BUS_BLUETOOTH);
-
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
- }
-
- bool isPointerChoreographerEnabled() override { return true; }
-};
-
-TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
- std::list<NotifyArgs> argsList;
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- argsList += process(kernelEventTime, EV_REL, REL_X, 1);
- argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
-
- // Process several events that come in quick succession, according to their timestamps.
- for (int i = 0; i < 3; i++) {
- constexpr static nsecs_t delta = ms2ns(1);
- static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
- kernelEventTime += delta;
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- argsList += process(kernelEventTime, EV_REL, REL_X, 1);
- argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
- }
-}
-
-TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningIsCapped) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
- std::list<NotifyArgs> argsList;
-
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
- argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
-
- // Process several events with the same timestamp from the kernel.
- // Ensure that we do not generate events too far into the future.
- constexpr static int32_t numEvents =
- MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
- for (int i = 0; i < numEvents; i++) {
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
- argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
- }
-
- // By processing more events with the same timestamp, we should not generate events with a
- // timestamp that is more than the specified max time delta from the timestamp at its injection.
- const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
- for (int i = 0; i < 3; i++) {
- argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
- argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(cappedEventTime)))));
- argsList.clear();
- }
-}
-
-TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningNotUsed) {
- mPropertyMap.addProperty("cursor.mode", "pointer");
- createMapper();
- std::list<NotifyArgs> argsList;
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- argsList += process(kernelEventTime, EV_REL, REL_X, 1);
- argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
-
- // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
- // smoothening is not needed, its timestamp is not affected.
- kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
- expectedEventTime = kernelEventTime;
-
- argsList += process(kernelEventTime, EV_REL, REL_X, 1);
- argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
- EXPECT_THAT(argsList,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime)))));
- argsList.clear();
-}
-
} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
new file mode 100644
index 0000000..530416c
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -0,0 +1,469 @@
+/*
+ * 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 "FakeInputDispatcherPolicy.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- FakeInputDispatcherPolicy ---
+
+void FakeInputDispatcherPolicy::assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
+ assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), InputEventType::KEY);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& keyEvent = static_cast<const KeyEvent&>(event);
+ EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(keyEvent.getAction(), args.action);
+ });
+}
+
+void FakeInputDispatcherPolicy::assertFilterInputEventWasCalled(const NotifyMotionArgs& args,
+ vec2 point) {
+ assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
+ ASSERT_EQ(event.getType(), InputEventType::MOTION);
+ EXPECT_EQ(event.getDisplayId(), args.displayId);
+
+ const auto& motionEvent = static_cast<const MotionEvent&>(event);
+ EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
+ EXPECT_EQ(motionEvent.getAction(), args.action);
+ EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
+ EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
+ });
+}
+
+void FakeInputDispatcherPolicy::assertFilterInputEventWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_EQ(nullptr, mFilteredEvent);
+}
+
+void FakeInputDispatcherPolicy::assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mConfigurationChangedTime) << "Timed out waiting for configuration changed call";
+ ASSERT_EQ(*mConfigurationChangedTime, when);
+ mConfigurationChangedTime = std::nullopt;
+}
+
+void FakeInputDispatcherPolicy::assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mLastNotifySwitch);
+ // We do not check id because it is not exposed to the policy
+ EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+ EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+ EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+ EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+ mLastNotifySwitch = std::nullopt;
+}
+
+void FakeInputDispatcherPolicy::assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
+ std::scoped_lock lock(mLock);
+ ASSERT_EQ(touchedToken, mOnPointerDownToken);
+ mOnPointerDownToken.clear();
+}
+
+void FakeInputDispatcherPolicy::assertOnPointerDownWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mOnPointerDownToken == nullptr)
+ << "Expected onPointerDownOutsideFocus to not have been called";
+}
+
+void FakeInputDispatcherPolicy::assertNotifyNoFocusedWindowAnrWasCalled(
+ std::chrono::nanoseconds timeout,
+ const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ std::shared_ptr<InputApplicationHandle> application;
+ ASSERT_NO_FATAL_FAILURE(
+ application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
+ ASSERT_EQ(expectedApplication, application);
+}
+
+void FakeInputDispatcherPolicy::assertNotifyWindowUnresponsiveWasCalled(
+ std::chrono::nanoseconds timeout, const sp<gui::WindowInfoHandle>& window) {
+ LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null");
+ assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(),
+ window->getInfo()->ownerPid);
+}
+
+void FakeInputDispatcherPolicy::assertNotifyWindowUnresponsiveWasCalled(
+ std::chrono::nanoseconds timeout, const sp<IBinder>& expectedToken,
+ std::optional<gui::Pid> expectedPid) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result;
+ ASSERT_NO_FATAL_FAILURE(result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
+}
+
+sp<IBinder> FakeInputDispatcherPolicy::getUnresponsiveWindowToken(
+ std::chrono::nanoseconds timeout) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock);
+ const auto& [token, _] = result;
+ return token;
+}
+
+void FakeInputDispatcherPolicy::assertNotifyWindowResponsiveWasCalled(
+ const sp<IBinder>& expectedToken, std::optional<gui::Pid> expectedPid) {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result;
+ ASSERT_NO_FATAL_FAILURE(result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
+ ASSERT_EQ(expectedToken, result.token);
+ ASSERT_EQ(expectedPid, result.pid);
+}
+
+sp<IBinder> FakeInputDispatcherPolicy::getResponsiveWindowToken() {
+ std::unique_lock lock(mLock);
+ android::base::ScopedLockAssertion assumeLocked(mLock);
+ AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock);
+ const auto& [token, _] = result;
+ return token;
+}
+
+void FakeInputDispatcherPolicy::assertNotifyAnrWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mAnrApplications.empty());
+ ASSERT_TRUE(mAnrWindows.empty());
+ ASSERT_TRUE(mResponsiveWindows.empty())
+ << "ANR was not called, but please also consume the 'connection is responsive' "
+ "signal";
+}
+
+PointerCaptureRequest FakeInputDispatcherPolicy::assertSetPointerCaptureCalled(
+ const sp<gui::WindowInfoHandle>& window, bool enabled) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (!mPointerCaptureChangedCondition
+ .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) {
+ if (enabled) {
+ return mPointerCaptureRequest->isEnable() &&
+ mPointerCaptureRequest->window == window->getToken();
+ } else {
+ return !mPointerCaptureRequest->isEnable();
+ }
+ })) {
+ ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", "
+ << enabled << ") to be called.";
+ return {};
+ }
+ auto request = *mPointerCaptureRequest;
+ mPointerCaptureRequest.reset();
+ return request;
+}
+
+void FakeInputDispatcherPolicy::assertSetPointerCaptureNotCalled() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
+ FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
+ "enabled = "
+ << std::to_string(mPointerCaptureRequest->isEnable());
+ }
+ mPointerCaptureRequest.reset();
+}
+
+void FakeInputDispatcherPolicy::assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
+ const sp<IBinder>& targetToken) {
+ dispatcher.waitForIdle();
+ std::scoped_lock lock(mLock);
+ ASSERT_TRUE(mNotifyDropWindowWasCalled);
+ ASSERT_EQ(targetToken, mDropTargetWindowToken);
+ mNotifyDropWindowWasCalled = false;
+}
+
+void FakeInputDispatcherPolicy::assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<sp<IBinder>> receivedToken =
+ getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
+ mNotifyInputChannelBroken);
+ ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token";
+ ASSERT_EQ(token, *receivedToken);
+}
+
+void FakeInputDispatcherPolicy::setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
+ mInterceptKeyTimeout = timeout;
+}
+
+std::chrono::nanoseconds FakeInputDispatcherPolicy::getKeyWaitingForEventsTimeout() {
+ return 500ms;
+}
+
+void FakeInputDispatcherPolicy::setStaleEventTimeout(std::chrono::nanoseconds timeout) {
+ mStaleEventTimeout = timeout;
+}
+
+void FakeInputDispatcherPolicy::assertUserActivityNotPoked() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::optional<UserActivityPokeEvent> pokeEvent =
+ getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
+ mNotifyUserActivity);
+
+ ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked";
+}
+
+void FakeInputDispatcherPolicy::assertUserActivityPoked(
+ std::optional<UserActivityPokeEvent> expectedPokeEvent) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ std::optional<UserActivityPokeEvent> pokeEvent =
+ getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
+ mNotifyUserActivity);
+ ASSERT_TRUE(pokeEvent) << "Expected a user poke event";
+
+ if (expectedPokeEvent) {
+ ASSERT_EQ(expectedPokeEvent, *pokeEvent);
+ }
+}
+
+void FakeInputDispatcherPolicy::assertNotifyDeviceInteractionWasCalled(int32_t deviceId,
+ std::set<gui::Uid> uids) {
+ ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms));
+}
+
+void FakeInputDispatcherPolicy::assertNotifyDeviceInteractionWasNotCalled() {
+ ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
+}
+
+void FakeInputDispatcherPolicy::setUnhandledKeyHandler(
+ std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) {
+ std::scoped_lock lock(mLock);
+ mUnhandledKeyHandler = handler;
+}
+
+void FakeInputDispatcherPolicy::assertUnhandledKeyReported(int32_t keycode) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported";
+ ASSERT_EQ(unhandledKeycode, keycode);
+}
+
+void FakeInputDispatcherPolicy::assertUnhandledKeyNotReported() {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<int32_t> unhandledKeycode =
+ getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock,
+ mNotifyUnhandledKey);
+ ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported";
+}
+
+template <class T>
+T FakeInputDispatcherPolicy::getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout,
+ std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock)
+ REQUIRES(mLock) {
+ // If there is an ANR, Dispatcher won't be idle because there are still events
+ // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
+ // before checking if ANR was called.
+ // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
+ // to provide it some time to act. 100ms seems reasonable.
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::optional<T> token =
+ getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+ if (!token.has_value()) {
+ ADD_FAILURE() << "Did not receive the ANR callback";
+ return {};
+ }
+
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+ // Ensure that the ANR didn't get raised too early. We can't be too strict here because
+ // the dispatcher started counting before this function was called
+ if (std::chrono::abs(timeout - waited) > 100ms) {
+ ADD_FAILURE() << "ANR was raised too early or too late. Expected "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
+ << "ms, but waited "
+ << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
+ << "ms instead";
+ }
+ return *token;
+}
+
+template <class T>
+std::optional<T> FakeInputDispatcherPolicy::getItemFromStorageLockedInterruptible(
+ std::chrono::nanoseconds timeout, std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock, std::condition_variable& condition) REQUIRES(mLock) {
+ condition.wait_for(lock, timeout, [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+ if (storage.empty()) {
+ return std::nullopt;
+ }
+ T item = storage.front();
+ storage.pop();
+ return std::make_optional(item);
+}
+
+void FakeInputDispatcherPolicy::notifyConfigurationChanged(nsecs_t when) {
+ std::scoped_lock lock(mLock);
+ mConfigurationChangedTime = when;
+}
+
+void FakeInputDispatcherPolicy::notifyWindowUnresponsive(const sp<IBinder>& connectionToken,
+ std::optional<gui::Pid> pid,
+ const std::string&) {
+ std::scoped_lock lock(mLock);
+ mAnrWindows.push({connectionToken, pid});
+ mNotifyAnr.notify_all();
+}
+
+void FakeInputDispatcherPolicy::notifyWindowResponsive(const sp<IBinder>& connectionToken,
+ std::optional<gui::Pid> pid) {
+ std::scoped_lock lock(mLock);
+ mResponsiveWindows.push({connectionToken, pid});
+ mNotifyAnr.notify_all();
+}
+
+void FakeInputDispatcherPolicy::notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) {
+ std::scoped_lock lock(mLock);
+ mAnrApplications.push(applicationHandle);
+ mNotifyAnr.notify_all();
+}
+
+void FakeInputDispatcherPolicy::notifyInputChannelBroken(const sp<IBinder>& connectionToken) {
+ std::scoped_lock lock(mLock);
+ mBrokenInputChannels.push(connectionToken);
+ mNotifyInputChannelBroken.notify_all();
+}
+
+void FakeInputDispatcherPolicy::notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) {}
+
+void FakeInputDispatcherPolicy::notifySensorEvent(int32_t deviceId,
+ InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy,
+ nsecs_t timestamp,
+ const std::vector<float>& values) {}
+
+void FakeInputDispatcherPolicy::notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) {}
+
+void FakeInputDispatcherPolicy::notifyVibratorState(int32_t deviceId, bool isOn) {}
+
+bool FakeInputDispatcherPolicy::filterInputEvent(const InputEvent& inputEvent,
+ uint32_t policyFlags) {
+ std::scoped_lock lock(mLock);
+ switch (inputEvent.getType()) {
+ case InputEventType::KEY: {
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<KeyEvent>(keyEvent);
+ break;
+ }
+
+ case InputEventType::MOTION: {
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent);
+ mFilteredEvent = std::make_unique<MotionEvent>(motionEvent);
+ break;
+ }
+ default: {
+ ADD_FAILURE() << "Should only filter keys or motions";
+ break;
+ }
+ }
+ return true;
+}
+
+void FakeInputDispatcherPolicy::interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) {
+ if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
+ // Clear intercept state when we handled the event.
+ mInterceptKeyTimeout = 0ms;
+ }
+}
+
+void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t,
+ int32_t, nsecs_t, uint32_t&) {}
+
+nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&,
+ const KeyEvent&, uint32_t) {
+ nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
+ // Clear intercept state so we could dispatch the event in next wake.
+ mInterceptKeyTimeout = 0ms;
+ return delay;
+}
+
+std::optional<KeyEvent> FakeInputDispatcherPolicy::dispatchUnhandledKey(const sp<IBinder>&,
+ const KeyEvent& event,
+ uint32_t) {
+ std::scoped_lock lock(mLock);
+ mReportedUnhandledKeycodes.emplace(event.getKeyCode());
+ mNotifyUnhandledKey.notify_all();
+ return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt;
+}
+
+void FakeInputDispatcherPolicy::notifySwitch(nsecs_t when, uint32_t switchValues,
+ uint32_t switchMask, uint32_t policyFlags) {
+ std::scoped_lock lock(mLock);
+ // We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+ // essentially a passthrough for notifySwitch.
+ mLastNotifySwitch =
+ NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask);
+}
+
+void FakeInputDispatcherPolicy::pokeUserActivity(nsecs_t eventTime, int32_t eventType,
+ ui::LogicalDisplayId displayId) {
+ std::scoped_lock lock(mLock);
+ mNotifyUserActivity.notify_all();
+ mUserActivityPokeEvents.push({eventTime, eventType, displayId});
+}
+
+bool FakeInputDispatcherPolicy::isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) {
+ return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout;
+}
+
+void FakeInputDispatcherPolicy::onPointerDownOutsideFocus(const sp<IBinder>& newToken) {
+ std::scoped_lock lock(mLock);
+ mOnPointerDownToken = newToken;
+}
+
+void FakeInputDispatcherPolicy::setPointerCapture(const PointerCaptureRequest& request) {
+ std::scoped_lock lock(mLock);
+ mPointerCaptureRequest = {request};
+ mPointerCaptureChangedCondition.notify_all();
+}
+
+void FakeInputDispatcherPolicy::notifyDropWindow(const sp<IBinder>& token, float x, float y) {
+ std::scoped_lock lock(mLock);
+ mNotifyDropWindowWasCalled = true;
+ mDropTargetWindowToken = token;
+}
+
+void FakeInputDispatcherPolicy::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) {
+ ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids));
+}
+
+void FakeInputDispatcherPolicy::assertFilterInputEventWasCalledInternal(
+ const std::function<void(const InputEvent&)>& verify) {
+ std::scoped_lock lock(mLock);
+ ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+ verify(*mFilteredEvent);
+ mFilteredEvent = nullptr;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index e0a7324..2c86146 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -16,78 +16,191 @@
#pragma once
-#include <android-base/logging.h>
#include "InputDispatcherPolicyInterface.h"
-namespace android {
+#include "InputDispatcherInterface.h"
+#include "NotifyArgs.h"
-// --- FakeInputDispatcherPolicy ---
+#include <condition_variable>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <binder/IBinder.h>
+#include <gui/PidUid.h>
+#include <gui/WindowInfo.h>
+#include <input/BlockingQueue.h>
+#include <input/Input.h>
+
+namespace android {
class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
public:
FakeInputDispatcherPolicy() = default;
virtual ~FakeInputDispatcherPolicy() = default;
+ struct AnrResult {
+ sp<IBinder> token{};
+ std::optional<gui::Pid> pid{};
+ };
+
+ struct UserActivityPokeEvent {
+ nsecs_t eventTime;
+ int32_t eventType;
+ ui::LogicalDisplayId displayId;
+
+ bool operator==(const UserActivityPokeEvent& rhs) const = default;
+ inline friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) {
+ os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType
+ << ", displayId=" << ev.displayId << "]";
+ return os;
+ }
+ };
+
+ void assertFilterInputEventWasCalled(const NotifyKeyArgs& args);
+ void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point);
+ void assertFilterInputEventWasNotCalled();
+ void assertNotifyConfigurationChangedWasCalled(nsecs_t when);
+ void assertNotifySwitchWasCalled(const NotifySwitchArgs& args);
+ void assertOnPointerDownEquals(const sp<IBinder>& touchedToken);
+ void assertOnPointerDownWasNotCalled();
+ /**
+ * This function must be called soon after the expected ANR timer starts,
+ * because we are also checking how much time has passed.
+ */
+ void assertNotifyNoFocusedWindowAnrWasCalled(
+ std::chrono::nanoseconds timeout,
+ const std::shared_ptr<InputApplicationHandle>& expectedApplication);
+ void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<gui::WindowInfoHandle>& window);
+ void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
+ const sp<IBinder>& expectedToken,
+ std::optional<gui::Pid> expectedPid);
+ /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
+ sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout);
+ void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
+ std::optional<gui::Pid> expectedPid);
+ /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
+ sp<IBinder> getResponsiveWindowToken();
+ void assertNotifyAnrWasNotCalled();
+ PointerCaptureRequest assertSetPointerCaptureCalled(const sp<gui::WindowInfoHandle>& window,
+ bool enabled);
+ void assertSetPointerCaptureNotCalled();
+ void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
+ const sp<IBinder>& targetToken);
+ void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token);
+ /**
+ * Set policy timeout. A value of zero means next key will not be intercepted.
+ */
+ void setInterceptKeyTimeout(std::chrono::milliseconds timeout);
+ std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override;
+ void setStaleEventTimeout(std::chrono::nanoseconds timeout);
+ void assertUserActivityNotPoked();
+ /**
+ * Asserts that a user activity poke has happened. The earliest recorded poke event will be
+ * cleared after this call.
+ *
+ * If an expected UserActivityPokeEvent is provided, asserts that the given event is the
+ * earliest recorded poke event.
+ */
+ void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {});
+ void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids);
+ void assertNotifyDeviceInteractionWasNotCalled();
+ void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler);
+ void assertUnhandledKeyReported(int32_t keycode);
+ void assertUnhandledKeyNotReported();
+
private:
- void notifyConfigurationChanged(nsecs_t) override {}
+ std::mutex mLock;
+ std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
+ std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
+ sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
+ std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
- void notifyNoFocusedWindowAnr(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
- LOG(ERROR) << "There is no focused window for " << applicationHandle->getName();
- }
+ std::condition_variable mPointerCaptureChangedCondition;
+ std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
+ // ANR handling
+ std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
+ std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock);
+ std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock);
+ std::condition_variable mNotifyAnr;
+ std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
+ std::condition_variable mNotifyInputChannelBroken;
+
+ sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
+ bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
+
+ std::condition_variable mNotifyUserActivity;
+ std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
+
+ std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
+
+ std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
+
+ BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
+
+ std::condition_variable mNotifyUnhandledKey;
+ std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock);
+ std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock);
+
+ /**
+ * All three ANR-related callbacks behave the same way, so we use this generic function to wait
+ * for a specific container to become non-empty. When the container is non-empty, return the
+ * first entry from the container and erase it.
+ */
+ template <class T>
+ T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock) REQUIRES(mLock);
+
+ template <class T>
+ std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+ std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock,
+ std::condition_variable& condition)
+ REQUIRES(mLock);
+
+ void notifyConfigurationChanged(nsecs_t when) override;
void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
- const std::string& reason) override {
- LOG(ERROR) << "Window is not responding: " << reason;
- }
-
+ const std::string&) override;
void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<gui::Pid> pid) override {}
-
- void notifyInputChannelBroken(const sp<IBinder>&) override {}
-
- void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
-
+ std::optional<gui::Pid> pid) override;
+ void notifyNoFocusedWindowAnr(
+ const std::shared_ptr<InputApplicationHandle>& applicationHandle) override;
+ void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override;
+ void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override;
void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
- const std::vector<float>& values) override {}
+ const std::vector<float>& values) override;
+ void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType,
+ InputDeviceSensorAccuracy accuracy) override;
+ void notifyVibratorState(int32_t deviceId, bool isOn) override;
+ bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override;
+ void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override;
+ void interceptMotionBeforeQueueing(ui::LogicalDisplayId, uint32_t, int32_t, nsecs_t,
+ uint32_t&) override;
+ nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override;
+ std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
+ uint32_t) override;
+ void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override;
+ void pokeUserActivity(nsecs_t eventTime, int32_t eventType,
+ ui::LogicalDisplayId displayId) override;
+ bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override;
+ void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override;
+ void setPointerCapture(const PointerCaptureRequest& request) override;
+ void notifyDropWindow(const sp<IBinder>& token, float x, float y) override;
+ void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
+ const std::set<gui::Uid>& uids) override;
- void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy) override {}
-
- void notifyVibratorState(int32_t deviceId, bool isOn) override {}
-
- bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
- return true; // dispatch event normally
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}
-
- void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {}
-
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
- return 0;
- }
-
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
- uint32_t) override {
- return {};
- }
-
- void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
-
- void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}
-
- void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
-
- void setPointerCapture(const PointerCaptureRequest&) override {}
-
- void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}
-
- void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
- const std::set<gui::Uid>& uids) override {}
-
- gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; }
+ void assertFilterInputEventWasCalledInternal(
+ const std::function<void(const InputEvent&)>& verify);
};
} // namespace android
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
index 8f593b5..088c7df 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp
@@ -82,9 +82,9 @@
mConfig.setDisplayViewports(mViewports);
}
-void FakeInputReaderPolicy::addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
- ui::Rotation orientation, bool isActive,
- const std::string& uniqueId,
+void FakeInputReaderPolicy::addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width,
+ int32_t height, ui::Rotation orientation,
+ bool isActive, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType type) {
const bool isRotated = orientation == ui::ROTATION_90 || orientation == ui::ROTATION_270;
@@ -139,7 +139,7 @@
void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId) {
- mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+ mConfig.uniqueIdAssociationsByPort.insert({inputUniqueId, displayUniqueId});
}
void FakeInputReaderPolicy::addKeyboardLayoutAssociation(const std::string& inputUniqueId,
@@ -155,11 +155,6 @@
mConfig.disabledDevices.erase(deviceId);
}
-void FakeInputReaderPolicy::setPointerController(
- std::shared_ptr<FakePointerController> controller) {
- mPointerController = std::move(controller);
-}
-
const InputReaderConfiguration& FakeInputReaderPolicy::getReaderConfiguration() const {
return mConfig;
}
@@ -183,11 +178,7 @@
return mConfig.pointerCaptureRequest;
}
-void FakeInputReaderPolicy::setShowTouches(bool enabled) {
- mConfig.showTouches = enabled;
-}
-
-void FakeInputReaderPolicy::setDefaultPointerDisplayId(int32_t pointerDisplayId) {
+void FakeInputReaderPolicy::setDefaultPointerDisplayId(ui::LogicalDisplayId pointerDisplayId) {
mConfig.defaultPointerDisplayId = pointerDisplayId;
}
@@ -228,11 +219,6 @@
*outConfig = mConfig;
}
-std::shared_ptr<PointerControllerInterface> FakeInputReaderPolicy::obtainPointerController(
- int32_t /*deviceId*/) {
- return mPointerController;
-}
-
void FakeInputReaderPolicy::notifyInputDevicesChanged(
const std::vector<InputDeviceInfo>& inputDevices) {
std::scoped_lock lock(mLock);
@@ -269,8 +255,8 @@
}
std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay(
- int32_t associatedDisplayId) {
- if (associatedDisplayId == ADISPLAY_ID_NONE) {
+ ui::LogicalDisplayId associatedDisplayId) {
+ if (!associatedDisplayId.isValid()) {
associatedDisplayId = mConfig.defaultPointerDisplayId;
}
for (auto& viewport : mViewports) {
diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h
index 710bb54..94f1311 100644
--- a/services/inputflinger/tests/FakeInputReaderPolicy.h
+++ b/services/inputflinger/tests/FakeInputReaderPolicy.h
@@ -26,7 +26,6 @@
#include <InputDevice.h>
#include <InputReaderBase.h>
-#include "FakePointerController.h"
#include "input/DisplayViewport.h"
#include "input/InputDevice.h"
@@ -49,7 +48,7 @@
std::optional<DisplayViewport> getDisplayViewportByType(ViewportType type) const;
std::optional<DisplayViewport> getDisplayViewportByPort(uint8_t displayPort) const;
void addDisplayViewport(DisplayViewport viewport);
- void addDisplayViewport(int32_t displayId, int32_t width, int32_t height,
+ void addDisplayViewport(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
ui::Rotation orientation, bool isActive, const std::string& uniqueId,
std::optional<uint8_t> physicalPort, ViewportType type);
bool updateViewport(const DisplayViewport& viewport);
@@ -62,15 +61,13 @@
const KeyboardLayoutInfo& layoutInfo);
void addDisabledDevice(int32_t deviceId);
void removeDisabledDevice(int32_t deviceId);
- void setPointerController(std::shared_ptr<FakePointerController> controller);
const InputReaderConfiguration& getReaderConfiguration() const;
const std::vector<InputDeviceInfo> getInputDevices() const;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
ui::Rotation surfaceRotation);
void setTouchAffineTransformation(const TouchAffineTransformation t);
PointerCaptureRequest setPointerCapture(const sp<IBinder>& window);
- void setShowTouches(bool enabled);
- void setDefaultPointerDisplayId(int32_t pointerDisplayId);
+ void setDefaultPointerDisplayId(ui::LogicalDisplayId pointerDisplayId);
void setPointerGestureEnabled(bool enabled);
float getPointerGestureMovementSpeedRatio();
float getPointerGestureZoomSpeedRatio();
@@ -80,12 +77,10 @@
void setIsInputMethodConnectionActive(bool active);
bool isInputMethodConnectionActive() override;
std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
- int32_t associatedDisplayId) override;
+ ui::LogicalDisplayId associatedDisplayId) override;
private:
void getReaderConfiguration(InputReaderConfiguration* outConfig) override;
- std::shared_ptr<PointerControllerInterface> obtainPointerController(
- int32_t /*deviceId*/) override;
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override;
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier&, const std::optional<KeyboardLayoutInfo>) override;
@@ -97,7 +92,6 @@
std::condition_variable mDevicesChangedCondition;
InputReaderConfiguration mConfig;
- std::shared_ptr<FakePointerController> mPointerController;
std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock);
bool mInputDevicesChanged GUARDED_BY(mLock){false};
std::vector<DisplayViewport> mViewports;
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index 069b50d..b46055e 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -162,7 +162,7 @@
// --- FakeInputTracingBackend ---
void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event,
- const trace::TracedEventArgs&) {
+ const trace::TracedEventMetadata&) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -171,7 +171,7 @@
}
void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event,
- const trace::TracedEventArgs&) {
+ const trace::TracedEventMetadata&) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -180,7 +180,7 @@
}
void FakeInputTracingBackend::traceWindowDispatch(const trace::WindowDispatchArgs& args,
- const trace::TracedEventArgs&) {
+ const trace::TracedEventMetadata&) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedWindowDispatches.push_back(args);
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index ab05d6b..cd4b507 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -82,11 +82,12 @@
private:
std::shared_ptr<VerifyingTrace> mTrace;
- void traceKeyEvent(const trace::TracedKeyEvent& entry, const trace::TracedEventArgs&) override;
+ void traceKeyEvent(const trace::TracedKeyEvent& entry,
+ const trace::TracedEventMetadata&) override;
void traceMotionEvent(const trace::TracedMotionEvent& entry,
- const trace::TracedEventArgs&) override;
+ const trace::TracedEventMetadata&) override;
void traceWindowDispatch(const trace::WindowDispatchArgs& entry,
- const trace::TracedEventArgs&) override;
+ const trace::TracedEventMetadata&) override;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index dc199e2..731a286 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -32,7 +32,7 @@
mHaveBounds = false;
}
-const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
+const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& FakePointerController::getSpots() {
return mSpotsByDisplay;
}
@@ -51,9 +51,9 @@
return {mX, mY};
}
-int32_t FakePointerController::getDisplayId() const {
+ui::LogicalDisplayId FakePointerController::getDisplayId() const {
if (!mEnabled || !mDisplayId) {
- return ADISPLAY_ID_NONE;
+ return ui::ADISPLAY_ID_NONE;
}
return *mDisplayId;
}
@@ -76,7 +76,15 @@
mCustomIconStyle = icon.style;
}
-void FakePointerController::assertViewportSet(int32_t displayId) {
+void FakePointerController::setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) {
+ if (skip) {
+ mDisplaysToSkipScreenshot.insert(displayId);
+ } else {
+ mDisplaysToSkipScreenshot.erase(displayId);
+ }
+};
+
+void FakePointerController::assertViewportSet(ui::LogicalDisplayId displayId) {
ASSERT_TRUE(mDisplayId);
ASSERT_EQ(displayId, mDisplayId);
}
@@ -91,7 +99,7 @@
ASSERT_NEAR(y, actualY, 1);
}
-void FakePointerController::assertSpotCount(int32_t displayId, int32_t count) {
+void FakePointerController::assertSpotCount(ui::LogicalDisplayId displayId, int32_t count) {
auto it = mSpotsByDisplay.find(displayId);
ASSERT_TRUE(it != mSpotsByDisplay.end()) << "Spots not found for display " << displayId;
ASSERT_EQ(static_cast<size_t>(count), it->second.size());
@@ -117,6 +125,15 @@
ASSERT_EQ(std::nullopt, mCustomIconStyle);
}
+void FakePointerController::assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId,
+ bool isHidden) {
+ if (isHidden) {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end());
+ } else {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end());
+ }
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
@@ -150,7 +167,7 @@
}
void FakePointerController::setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) {
+ ui::LogicalDisplayId displayId) {
if (!mEnabled) return;
std::vector<int32_t> newSpots;
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 536b447..5bc713f 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -17,10 +17,10 @@
#pragma once
#include <PointerControllerInterface.h>
-#include <gui/constants.h>
#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <utils/BitSet.h>
+#include <unordered_set>
namespace android {
@@ -37,24 +37,26 @@
void setBounds(float minX, float minY, float maxX, float maxY);
void clearBounds();
- const std::map<int32_t, std::vector<int32_t>>& getSpots();
+ const std::map<ui::LogicalDisplayId, std::vector<int32_t>>& getSpots();
void setPosition(float x, float y) override;
FloatPoint getPosition() const override;
- int32_t getDisplayId() const override;
+ ui::LogicalDisplayId getDisplayId() const override;
void setDisplayViewport(const DisplayViewport& viewport) override;
void updatePointerIcon(PointerIconStyle iconId) override;
void setCustomPointerIcon(const SpriteIcon& icon) override;
+ void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) override;
void fade(Transition) override;
- void assertViewportSet(int32_t displayId);
+ void assertViewportSet(ui::LogicalDisplayId displayId);
void assertViewportNotSet();
void assertPosition(float x, float y);
- void assertSpotCount(int32_t displayId, int32_t count);
+ void assertSpotCount(ui::LogicalDisplayId displayId, int32_t count);
void assertPointerIconSet(PointerIconStyle iconId);
void assertPointerIconNotSet();
void assertCustomPointerIconSet(PointerIconStyle iconId);
void assertCustomPointerIconNotSet();
+ void assertIsHiddenOnMirroredDisplays(ui::LogicalDisplayId displayId, bool isHidden);
bool isPointerShown();
private:
@@ -64,19 +66,20 @@
void unfade(Transition) override;
void setPresentation(Presentation) override {}
void setSpots(const PointerCoords*, const uint32_t*, BitSet32 spotIdBits,
- int32_t displayId) override;
+ ui::LogicalDisplayId displayId) override;
void clearSpots() override;
const bool mEnabled;
bool mHaveBounds{false};
float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0};
float mX{0}, mY{0};
- std::optional<int32_t> mDisplayId;
+ std::optional<ui::LogicalDisplayId> mDisplayId;
bool mIsPointerShown{false};
std::optional<PointerIconStyle> mIconStyle;
std::optional<PointerIconStyle> mCustomIconStyle;
- std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay;
+ std::map<ui::LogicalDisplayId, std::vector<int32_t>> mSpotsByDisplay;
+ std::unordered_set<ui::LogicalDisplayId> mDisplaysToSkipScreenshot;
};
} // namespace android
diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h
deleted file mode 100644
index 8ce61e7..0000000
--- a/services/inputflinger/tests/FakeWindowHandle.h
+++ /dev/null
@@ -1,275 +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 <android-base/logging.h>
-#include <input/InputConsumer.h>
-#include "../dispatcher/InputDispatcher.h"
-
-using android::base::Result;
-using android::gui::Pid;
-using android::gui::TouchOcclusionMode;
-using android::gui::Uid;
-using android::gui::WindowInfo;
-using android::gui::WindowInfoHandle;
-
-namespace android {
-namespace inputdispatcher {
-
-namespace {
-
-// The default pid and uid for windows created by the test.
-constexpr gui::Pid WINDOW_PID{999};
-constexpr gui::Uid WINDOW_UID{1001};
-
-static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;
-
-} // namespace
-
-class FakeInputReceiver {
-public:
- std::unique_ptr<InputEvent> consumeEvent(std::chrono::milliseconds timeout) {
- uint32_t consumeSeq = 0;
- std::unique_ptr<InputEvent> event;
-
- std::chrono::time_point start = std::chrono::steady_clock::now();
- status_t result = WOULD_BLOCK;
- while (result == WOULD_BLOCK) {
- InputEvent* rawEventPtr = nullptr;
- result = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &rawEventPtr);
- event = std::unique_ptr<InputEvent>(rawEventPtr);
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > timeout) {
- if (timeout != 0ms) {
- LOG(ERROR) << "Waited too long for consumer to produce an event, giving up";
- }
- break;
- }
- }
- // Events produced by this factory are owned pointers.
- if (result != OK) {
- if (timeout == 0ms) {
- // This is likely expected. No need to log.
- } else {
- LOG(ERROR) << "Received result = " << result << " from consume";
- }
- return nullptr;
- }
- result = mConsumer.sendFinishedSignal(consumeSeq, true);
- if (result != OK) {
- LOG(ERROR) << "Received result = " << result << " from sendFinishedSignal";
- }
- return event;
- }
-
- explicit FakeInputReceiver(std::unique_ptr<InputChannel> channel, const std::string name)
- : mConsumer(std::move(channel)) {}
-
- virtual ~FakeInputReceiver() {}
-
-private:
- std::unique_ptr<InputChannel> mClientChannel;
- InputConsumer mConsumer;
- DynamicInputEventFactory mEventFactory;
-};
-
-class FakeWindowHandle : public WindowInfoHandle {
-public:
- static const int32_t WIDTH = 600;
- static const int32_t HEIGHT = 800;
-
- FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- InputDispatcher& dispatcher, const std::string name, int32_t displayId)
- : mName(name) {
- Result<std::unique_ptr<InputChannel>> channel = dispatcher.createInputChannel(name);
- mInfo.token = (*channel)->getConnectionToken();
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
-
- inputApplicationHandle->updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
-
- mInfo.id = sId++;
- mInfo.name = name;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.alpha = 1.0;
- mInfo.frame.left = 0;
- mInfo.frame.top = 0;
- mInfo.frame.right = WIDTH;
- mInfo.frame.bottom = HEIGHT;
- mInfo.transform.set(0, 0);
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
- mInfo.ownerPid = WINDOW_PID;
- mInfo.ownerUid = WINDOW_UID;
- mInfo.displayId = displayId;
- mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
- }
-
- sp<FakeWindowHandle> clone(int32_t displayId) {
- sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
- handle->mInfo = mInfo;
- handle->mInfo.displayId = displayId;
- handle->mInfo.id = sId++;
- handle->mInputReceiver = mInputReceiver;
- return handle;
- }
-
- void setTouchable(bool touchable) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
- }
-
- void setFocusable(bool focusable) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
- }
-
- void setVisible(bool visible) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
- }
-
- void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout;
- }
-
- void setPaused(bool paused) {
- mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
- }
-
- void setPreventSplitting(bool preventSplitting) {
- mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
- }
-
- void setSlippery(bool slippery) {
- mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
- }
-
- void setWatchOutsideTouch(bool watchOutside) {
- mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
- }
-
- void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
-
- void setInterceptsStylus(bool interceptsStylus) {
- mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
- }
-
- void setDropInput(bool dropInput) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
- }
-
- void setDropInputIfObscured(bool dropInputIfObscured) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
- }
-
- void setNoInputChannel(bool noInputChannel) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
- }
-
- void setDisableUserActivity(bool disableUserActivity) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
- }
-
- void setAlpha(float alpha) { mInfo.alpha = alpha; }
-
- void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
-
- void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
-
- void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
- mInfo.frame.left = frame.left;
- mInfo.frame.top = frame.top;
- mInfo.frame.right = frame.right;
- mInfo.frame.bottom = frame.bottom;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(frame);
-
- const Rect logicalDisplayFrame = displayTransform.transform(frame);
- ui::Transform translate;
- translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
- mInfo.transform = translate * displayTransform;
- }
-
- void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
-
- void setIsWallpaper(bool isWallpaper) {
- mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
- }
-
- void setDupTouchToWallpaper(bool hasWallpaper) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
- }
-
- void setTrustedOverlay(bool trustedOverlay) {
- mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
- }
-
- void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
- mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
- }
-
- void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
-
- void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
-
- std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout) {
- if (mInputReceiver == nullptr) {
- return nullptr;
- }
- return mInputReceiver->consumeEvent(timeout);
- }
-
- void consumeMotion() {
- std::unique_ptr<InputEvent> event = consume(100ms);
-
- if (event == nullptr) {
- LOG(FATAL) << mName << ": expected a MotionEvent, but didn't get one.";
- return;
- }
-
- if (event->getType() != InputEventType::MOTION) {
- LOG(FATAL) << mName << " expected a MotionEvent, got " << *event;
- return;
- }
- }
-
- sp<IBinder> getToken() { return mInfo.token; }
-
- const std::string& getName() { return mName; }
-
- void setOwnerInfo(Pid ownerPid, Uid ownerUid) {
- mInfo.ownerPid = ownerPid;
- mInfo.ownerUid = ownerUid;
- }
-
- Pid getPid() const { return mInfo.ownerPid; }
-
- void destroyReceiver() { mInputReceiver = nullptr; }
-
-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>;
-};
-
-std::atomic<int32_t> FakeWindowHandle::sId{1};
-
-} // namespace inputdispatcher
-
-} // namespace android
diff --git a/services/inputflinger/tests/FakeWindows.cpp b/services/inputflinger/tests/FakeWindows.cpp
new file mode 100644
index 0000000..c800d6a
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindows.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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 "FakeWindows.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+// --- FakeInputReceiver ---
+
+FakeInputReceiver::FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel,
+ const std::string name)
+ : mConsumer(std::move(clientChannel)), mName(name) {}
+
+std::unique_ptr<InputEvent> FakeInputReceiver::consume(std::chrono::milliseconds timeout,
+ bool handled) {
+ auto [consumeSeq, event] = receiveEvent(timeout);
+ if (!consumeSeq) {
+ return nullptr;
+ }
+ finishEvent(*consumeSeq, handled);
+ return std::move(event);
+}
+
+std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> FakeInputReceiver::receiveEvent(
+ std::chrono::milliseconds timeout) {
+ uint32_t consumeSeq;
+ std::unique_ptr<InputEvent> event;
+
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t status = WOULD_BLOCK;
+ while (status == WOULD_BLOCK) {
+ InputEvent* rawEventPtr = nullptr;
+ status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
+ &rawEventPtr);
+ event = std::unique_ptr<InputEvent>(rawEventPtr);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > timeout) {
+ break;
+ }
+ }
+
+ if (status == WOULD_BLOCK) {
+ // Just means there's no event available.
+ return std::make_pair(std::nullopt, nullptr);
+ }
+
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+ return std::make_pair(std::nullopt, nullptr);
+ }
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+ }
+ return std::make_pair(consumeSeq, std::move(event));
+}
+
+void FakeInputReceiver::finishEvent(uint32_t consumeSeq, bool handled) {
+ const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled);
+ ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+}
+
+void FakeInputReceiver::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ const status_t status = mConsumer.sendTimeline(inputEventId, timeline);
+ ASSERT_EQ(OK, status);
+}
+
+void FakeInputReceiver::consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
+ std::optional<ui::LogicalDisplayId> expectedDisplayId,
+ std::optional<int32_t> expectedFlags) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(expectedEventType, event->getType())
+ << mName.c_str() << " expected " << ftl::enum_string(expectedEventType)
+ << " event, got " << *event;
+
+ if (expectedDisplayId.has_value()) {
+ EXPECT_EQ(expectedDisplayId, event->getDisplayId());
+ }
+
+ switch (expectedEventType) {
+ case InputEventType::KEY: {
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
+ ASSERT_THAT(keyEvent, WithKeyAction(expectedAction));
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
+ }
+ break;
+ }
+ case InputEventType::MOTION: {
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ ASSERT_THAT(motionEvent, WithMotionAction(expectedAction));
+ if (expectedFlags.has_value()) {
+ EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
+ }
+ break;
+ }
+ case InputEventType::FOCUS: {
+ FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
+ }
+ case InputEventType::CAPTURE: {
+ FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
+ }
+ case InputEventType::TOUCH_MODE: {
+ FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
+ }
+ case InputEventType::DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
+ }
+}
+
+std::unique_ptr<MotionEvent> FakeInputReceiver::consumeMotion() {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+
+ if (event == nullptr) {
+ ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
+ return nullptr;
+ }
+
+ if (event->getType() != InputEventType::MOTION) {
+ ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event;
+ return nullptr;
+ }
+ return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
+}
+
+void FakeInputReceiver::consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
+ std::unique_ptr<MotionEvent> motionEvent = consumeMotion();
+ ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
+ ASSERT_THAT(*motionEvent, matcher);
+}
+
+void FakeInputReceiver::consumeFocusEvent(bool hasFocus, bool inTouchMode) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::FOCUS, event->getType()) << "Instead of FocusEvent, got " << *event;
+
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ EXPECT_EQ(hasFocus, focusEvent.getHasFocus());
+}
+
+void FakeInputReceiver::consumeCaptureEvent(bool hasCapture) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::CAPTURE, event->getType())
+ << "Instead of CaptureEvent, got " << *event;
+
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& captureEvent = static_cast<const CaptureEvent&>(*event);
+ EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
+}
+
+void FakeInputReceiver::consumeDragEvent(bool isExiting, float x, float y) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
+
+ EXPECT_EQ(ui::ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+}
+
+void FakeInputReceiver::consumeTouchModeEvent(bool inTouchMode) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ ASSERT_NE(nullptr, event) << mName.c_str() << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
+ << "Instead of TouchModeEvent, got " << *event;
+
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+ const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
+ EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
+}
+
+void FakeInputReceiver::assertNoEvents(std::chrono::milliseconds timeout) {
+ std::unique_ptr<InputEvent> event = consume(timeout);
+ if (event == nullptr) {
+ return;
+ }
+ if (event->getType() == InputEventType::KEY) {
+ KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
+ ADD_FAILURE() << "Received key event " << keyEvent;
+ } else if (event->getType() == InputEventType::MOTION) {
+ MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
+ ADD_FAILURE() << "Received motion event " << motionEvent;
+ } else if (event->getType() == InputEventType::FOCUS) {
+ FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
+ ADD_FAILURE() << "Received focus event, hasFocus = "
+ << (focusEvent.getHasFocus() ? "true" : "false");
+ } else if (event->getType() == InputEventType::CAPTURE) {
+ const auto& captureEvent = static_cast<CaptureEvent&>(*event);
+ ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
+ << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
+ } else if (event->getType() == InputEventType::TOUCH_MODE) {
+ const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
+ ADD_FAILURE() << "Received touch mode event, inTouchMode = "
+ << (touchModeEvent.isInTouchMode() ? "true" : "false");
+ }
+ FAIL() << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
+}
+
+sp<IBinder> FakeInputReceiver::getToken() {
+ return mConsumer.getChannel()->getConnectionToken();
+}
+
+int FakeInputReceiver::getChannelFd() {
+ return mConsumer.getChannel()->getFd();
+}
+
+// --- FakeWindowHandle ---
+
+std::function<void(const std::unique_ptr<InputEvent>&, const gui::WindowInfo&)>
+ FakeWindowHandle::sOnEventReceivedCallback{};
+
+std::atomic<int32_t> FakeWindowHandle::sId{1};
+
+FakeWindowHandle::FakeWindowHandle(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const std::unique_ptr<inputdispatcher::InputDispatcher>& dispatcher, const std::string name,
+ ui::LogicalDisplayId displayId, bool createInputChannel)
+ : mName(name) {
+ sp<IBinder> token;
+ if (createInputChannel) {
+ base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputChannel(name);
+ token = (*channel)->getConnectionToken();
+ mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
+ }
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.token = token;
+ mInfo.id = sId++;
+ mInfo.name = name;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.alpha = 1.0;
+ mInfo.frame = Rect(0, 0, WIDTH, HEIGHT);
+ mInfo.transform.set(0, 0);
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.ownerPid = WINDOW_PID;
+ mInfo.ownerUid = WINDOW_UID;
+ mInfo.displayId = displayId;
+ mInfo.inputConfig = InputConfig::DEFAULT;
+}
+
+sp<FakeWindowHandle> FakeWindowHandle::clone(ui::LogicalDisplayId displayId) {
+ sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
+ handle->mInfo = mInfo;
+ handle->mInfo.displayId = displayId;
+ handle->mInfo.id = sId++;
+ handle->mInputReceiver = mInputReceiver;
+ return handle;
+}
+
+std::unique_ptr<KeyEvent> FakeWindowHandle::consumeKey(bool handled) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
+ if (event == nullptr) {
+ ADD_FAILURE() << "No event";
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::KEY) {
+ ADD_FAILURE() << "Instead of key event, got " << event;
+ return nullptr;
+ }
+ return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release()));
+}
+
+std::unique_ptr<MotionEvent> FakeWindowHandle::consumeMotionEvent(
+ const ::testing::Matcher<MotionEvent>& matcher) {
+ std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
+ if (event == nullptr) {
+ std::ostringstream matcherDescription;
+ matcher.DescribeTo(&matcherDescription);
+ ADD_FAILURE() << "No event (expected " << matcherDescription.str() << ") on " << mName;
+ return nullptr;
+ }
+ if (event->getType() != InputEventType::MOTION) {
+ ADD_FAILURE() << "Instead of motion event, got " << *event << " on " << mName;
+ return nullptr;
+ }
+ std::unique_ptr<MotionEvent> motionEvent =
+ std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
+ if (motionEvent == nullptr) {
+ return nullptr;
+ }
+ EXPECT_THAT(*motionEvent, matcher) << " on " << mName;
+ return motionEvent;
+}
+
+void FakeWindowHandle::assertNoEvents(std::optional<std::chrono::milliseconds> timeout) {
+ if (mInputReceiver == nullptr && mInfo.inputConfig.test(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(timeout.value_or(CONSUME_TIMEOUT_NO_EVENT_EXPECTED));
+}
+
+std::unique_ptr<InputEvent> FakeWindowHandle::consume(std::chrono::milliseconds timeout,
+ bool handled) {
+ if (mInputReceiver == nullptr) {
+ LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
+ }
+ std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled);
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consume failed: no event";
+ }
+
+ if (sOnEventReceivedCallback != nullptr) {
+ sOnEventReceivedCallback(event, mInfo);
+ }
+ return event;
+}
+
+std::pair<std::optional<uint32_t /*seq*/>, std::unique_ptr<InputEvent>>
+FakeWindowHandle::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;
+
+ if (sOnEventReceivedCallback != nullptr) {
+ sOnEventReceivedCallback(event, mInfo);
+ }
+ return out;
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeWindows.h b/services/inputflinger/tests/FakeWindows.h
new file mode 100644
index 0000000..8a40337
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindows.h
@@ -0,0 +1,405 @@
+/*
+ * 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/InputDispatcher.h"
+#include "TestEventMatchers.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputConsumer.h>
+
+namespace android {
+
+/**
+ * If we expect to receive the event, the timeout can be made very long. When the test are running
+ * correctly, we will actually never wait until the end of the timeout because the wait will end
+ * when the event comes in. Still, this value shouldn't be infinite. During development, a local
+ * change may cause the test to fail. This timeout should be short enough to not annoy so that the
+ * developer can see the failure quickly (on human scale).
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms;
+
+/**
+ * When no event is expected, we can have a very short timeout. A large value here would slow down
+ * the tests. In the unlikely event of system being too slow, the event may still be present but the
+ * timeout would complete before it is consumed. This would result in test flakiness. If this
+ * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this
+ * would get noticed and addressed quickly.
+ */
+static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms;
+
+/**
+ * The default pid and uid for windows created on the primary display by the test.
+ */
+static constexpr gui::Pid WINDOW_PID{999};
+static constexpr gui::Uid WINDOW_UID{1001};
+
+/**
+ * Default input dispatching timeout if there is no focused application or paused window
+ * from which to determine an appropriate dispatching timeout.
+ */
+static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
+ android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ android::base::HwTimeoutMultiplier());
+
+// --- FakeInputReceiver ---
+
+class FakeInputReceiver {
+public:
+ explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name);
+
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false);
+ /**
+ * Receive an event without acknowledging it.
+ * Return the sequence number that could later be used to send finished signal.
+ */
+ std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent(
+ std::chrono::milliseconds timeout);
+ /**
+ * To be used together with "receiveEvent" to complete the consumption of an event.
+ */
+ void finishEvent(uint32_t consumeSeq, bool handled = true);
+
+ void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
+ void consumeEvent(android::InputEventType expectedEventType, int32_t expectedAction,
+ std::optional<ui::LogicalDisplayId> expectedDisplayId,
+ std::optional<int32_t> expectedFlags);
+
+ std::unique_ptr<MotionEvent> consumeMotion();
+ void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher);
+
+ void consumeFocusEvent(bool hasFocus, bool inTouchMode);
+ void consumeCaptureEvent(bool hasCapture);
+ void consumeDragEvent(bool isExiting, float x, float y);
+ void consumeTouchModeEvent(bool inTouchMode);
+
+ void assertNoEvents(std::chrono::milliseconds timeout);
+
+ sp<IBinder> getToken();
+ int getChannelFd();
+
+private:
+ InputConsumer mConsumer;
+ DynamicInputEventFactory mEventFactory;
+ std::string mName;
+};
+
+// --- FakeWindowHandle ---
+
+class FakeWindowHandle : public gui::WindowInfoHandle {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+ using InputConfig = gui::WindowInfo::InputConfig;
+
+ // This is a callback that is fired when an event is received by the window.
+ // It is static to avoid having to pass it individually into all of the FakeWindowHandles
+ // created by tests.
+ // TODO(b/210460522): Update the tests to use a factory pattern so that we can avoid
+ // the need to make this static.
+ static std::function<void(const std::unique_ptr<InputEvent>&, const gui::WindowInfo&)>
+ sOnEventReceivedCallback;
+
+ FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const std::unique_ptr<inputdispatcher::InputDispatcher>& dispatcher,
+ const std::string name, ui::LogicalDisplayId displayId,
+ bool createInputChannel = true);
+
+ sp<FakeWindowHandle> clone(ui::LogicalDisplayId displayId);
+
+ inline void setTouchable(bool touchable) {
+ mInfo.setInputConfig(InputConfig::NOT_TOUCHABLE, !touchable);
+ }
+
+ inline void setFocusable(bool focusable) {
+ mInfo.setInputConfig(InputConfig::NOT_FOCUSABLE, !focusable);
+ }
+
+ inline void setVisible(bool visible) {
+ mInfo.setInputConfig(InputConfig::NOT_VISIBLE, !visible);
+ }
+
+ inline void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
+ mInfo.dispatchingTimeout = timeout;
+ }
+
+ inline void setPaused(bool paused) {
+ mInfo.setInputConfig(InputConfig::PAUSE_DISPATCHING, paused);
+ }
+
+ inline void setPreventSplitting(bool preventSplitting) {
+ mInfo.setInputConfig(InputConfig::PREVENT_SPLITTING, preventSplitting);
+ }
+
+ inline void setSlippery(bool slippery) {
+ mInfo.setInputConfig(InputConfig::SLIPPERY, slippery);
+ }
+
+ inline void setWatchOutsideTouch(bool watchOutside) {
+ mInfo.setInputConfig(InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
+ }
+
+ inline void setSpy(bool spy) { mInfo.setInputConfig(InputConfig::SPY, spy); }
+
+ inline void setSecure(bool secure) {
+ if (secure) {
+ mInfo.layoutParamsFlags |= gui::WindowInfo::Flag::SECURE;
+ } else {
+ using namespace ftl::flag_operators;
+ mInfo.layoutParamsFlags &= ~gui::WindowInfo::Flag::SECURE;
+ }
+ mInfo.setInputConfig(InputConfig::SENSITIVE_FOR_PRIVACY, secure);
+ }
+
+ inline void setInterceptsStylus(bool interceptsStylus) {
+ mInfo.setInputConfig(InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
+ }
+
+ inline void setDropInput(bool dropInput) {
+ mInfo.setInputConfig(InputConfig::DROP_INPUT, dropInput);
+ }
+
+ inline void setDropInputIfObscured(bool dropInputIfObscured) {
+ mInfo.setInputConfig(InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
+ }
+
+ inline void setNoInputChannel(bool noInputChannel) {
+ mInfo.setInputConfig(InputConfig::NO_INPUT_CHANNEL, noInputChannel);
+ }
+
+ inline void setDisableUserActivity(bool disableUserActivity) {
+ mInfo.setInputConfig(InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
+ }
+
+ inline void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) {
+ mInfo.setInputConfig(InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH, shouldGlobalStylusBlockTouch);
+ }
+
+ inline void setAlpha(float alpha) { mInfo.alpha = alpha; }
+
+ inline void setTouchOcclusionMode(gui::TouchOcclusionMode mode) {
+ mInfo.touchOcclusionMode = mode;
+ }
+
+ inline void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
+
+ inline void setFrame(const Rect& frame,
+ const ui::Transform& displayTransform = ui::Transform()) {
+ mInfo.frame = frame;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(frame);
+
+ const Rect logicalDisplayFrame = displayTransform.transform(frame);
+ ui::Transform translate;
+ translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
+ mInfo.transform = translate * displayTransform;
+ }
+
+ inline void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
+
+ inline void setIsWallpaper(bool isWallpaper) {
+ mInfo.setInputConfig(InputConfig::IS_WALLPAPER, isWallpaper);
+ }
+
+ inline void setDupTouchToWallpaper(bool hasWallpaper) {
+ mInfo.setInputConfig(InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
+ }
+
+ inline void setTrustedOverlay(bool trustedOverlay) {
+ mInfo.setInputConfig(InputConfig::TRUSTED_OVERLAY, trustedOverlay);
+ }
+
+ inline void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
+ mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
+ }
+
+ inline void setWindowScale(float xScale, float yScale) {
+ setWindowTransform(xScale, 0, 0, yScale);
+ }
+
+ inline void setWindowOffset(float offsetX, float offsetY) {
+ mInfo.transform.set(offsetX, offsetY);
+ }
+
+ std::unique_ptr<KeyEvent> consumeKey(bool handled = true);
+
+ inline std::unique_ptr<KeyEvent> consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
+ std::unique_ptr<KeyEvent> keyEvent = consumeKey();
+ EXPECT_NE(nullptr, keyEvent);
+ if (!keyEvent) {
+ return nullptr;
+ }
+ EXPECT_THAT(*keyEvent, matcher);
+ return keyEvent;
+ }
+
+ inline void consumeKeyDown(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeKeyEvent(testing::AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
+ }
+
+ inline void consumeKeyUp(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeKeyEvent(testing::AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+ WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionCancel(
+ ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
+ }
+
+ inline void consumeMotionMove(ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionDown(ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeAnyMotionDown(expectedDisplayId, expectedFlags);
+ }
+
+ inline void consumeAnyMotionDown(
+ std::optional<ui::LogicalDisplayId> expectedDisplayId = std::nullopt,
+ std::optional<int32_t> expectedFlags = std::nullopt) {
+ consumeMotionEvent(
+ testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ testing::Conditional(expectedDisplayId.has_value(),
+ WithDisplayId(*expectedDisplayId), testing::_),
+ testing::Conditional(expectedFlags.has_value(),
+ WithFlags(*expectedFlags), testing::_)));
+ }
+
+ inline void consumeMotionPointerDown(
+ int32_t pointerIdx, ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeMotionEvent(testing::AllOf(WithMotionAction(action),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionPointerUp(
+ int32_t pointerIdx, ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeMotionEvent(testing::AllOf(WithMotionAction(action),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionUp(ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionOutside(
+ ui::LogicalDisplayId expectedDisplayId = ui::ADISPLAY_ID_DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionOutsideWithZeroedCoords() {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE),
+ WithRawCoords(0, 0)));
+ }
+
+ inline void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
+ }
+
+ inline void consumeCaptureEvent(bool hasCapture) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeCaptureEvent(hasCapture);
+ }
+
+ std::unique_ptr<MotionEvent> consumeMotionEvent(
+ const ::testing::Matcher<MotionEvent>& matcher = testing::_);
+
+ inline void consumeDragEvent(bool isExiting, float x, float y) {
+ mInputReceiver->consumeDragEvent(isExiting, x, y);
+ }
+
+ inline void consumeTouchModeEvent(bool inTouchMode) {
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Cannot consume events from a window with no receiver";
+ mInputReceiver->consumeTouchModeEvent(inTouchMode);
+ }
+
+ inline std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() {
+ return receive();
+ }
+
+ inline void finishEvent(uint32_t sequenceNum) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->finishEvent(sequenceNum);
+ }
+
+ inline void sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
+ mInputReceiver->sendTimeline(inputEventId, timeline);
+ }
+
+ void assertNoEvents(std::optional<std::chrono::milliseconds> timeout = {});
+
+ inline sp<IBinder> getToken() { return mInfo.token; }
+
+ inline const std::string& getName() { return mName; }
+
+ inline void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) {
+ mInfo.ownerPid = ownerPid;
+ mInfo.ownerUid = ownerUid;
+ }
+
+ inline gui::Pid getPid() const { return mInfo.ownerPid; }
+
+ inline void destroyReceiver() { mInputReceiver = nullptr; }
+
+ inline int getChannelFd() { return mInputReceiver->getChannelFd(); }
+
+ // FakeWindowHandle uses this consume method to ensure received events are added to the trace.
+ std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true);
+
+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();
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/FocusResolver_test.cpp b/services/inputflinger/tests/FocusResolver_test.cpp
index cb8c3cb..f794da5 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -74,7 +74,7 @@
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
- ASSERT_EQ(request.displayId, changes->displayId);
+ ASSERT_EQ(ui::LogicalDisplayId{request.displayId}, changes->displayId);
// invisible window cannot get focused
request.token = invisibleWindowToken;
@@ -169,19 +169,20 @@
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
// The mirror window now comes on top, and the focus does not change
- changes = focusResolver.setInputWindows(request.displayId, {mirror, window});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId},
+ {mirror, window});
ASSERT_FALSE(changes.has_value());
// The window now comes on top while the mirror is removed, and the focus does not change
- changes = focusResolver.setInputWindows(request.displayId, {window});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {window});
ASSERT_FALSE(changes.has_value());
// The window is removed but the mirror is on top, and focus does not change
- changes = focusResolver.setInputWindows(request.displayId, {mirror});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {mirror});
ASSERT_FALSE(changes.has_value());
// All windows removed
- changes = focusResolver.setInputWindows(request.displayId, {});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
@@ -203,12 +204,12 @@
ASSERT_EQ(focusableWindowToken, changes->newFocus);
// When there are no changes to the window, focus does not change
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FALSE(changes.has_value());
// Window visibility changes and the window loses focus
window->setVisible(false);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
}
@@ -232,7 +233,7 @@
// Window visibility changes and the window gets focused
invisibleWindow->setVisible(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ invisibleWindowToken);
}
@@ -256,25 +257,25 @@
// Focusability changes and the window gets focused
window->setFocusable(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
// Visibility changes and the window loses focus
window->setVisible(false);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
// Visibility changes and the window gets focused
window->setVisible(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
// Window is gone and the window loses focus
- changes = focusResolver.setInputWindows(request.displayId, {});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
// Window returns and the window gains focus
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
}
@@ -307,27 +308,27 @@
// Embedded is now focusable so will gain focus
embeddedWindow->setFocusable(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
// Embedded is not visible so host will get focus
embeddedWindow->setVisible(false);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
// Embedded is now visible so will get focus
embeddedWindow->setVisible(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ hostWindowToken, /*to*/ embeddedWindowToken);
// Remove focusTransferTarget from host. Host will gain focus.
hostWindow->editInfo()->focusTransferTarget = nullptr;
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ embeddedWindowToken, /*to*/ hostWindowToken);
// Set invalid token for focusTransferTarget. Host will remain focus
hostWindow->editInfo()->focusTransferTarget = sp<BBinder>::make();
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FALSE(changes);
}
@@ -415,16 +416,16 @@
std::optional<FocusResolver::FocusChanges> changes =
focusResolver.setFocusedWindow(request, windows);
ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
- ASSERT_EQ(request.displayId, changes->displayId);
+ ASSERT_EQ(ui::LogicalDisplayId{request.displayId}, changes->displayId);
// When a display is removed, all windows are removed from the display
// and our focused window loses focus
- changes = focusResolver.setInputWindows(request.displayId, {});
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
ASSERT_FOCUS_CHANGE(changes, /*from*/ windowToken, /*to*/ nullptr);
- focusResolver.displayRemoved(request.displayId);
+ focusResolver.displayRemoved(ui::LogicalDisplayId{request.displayId});
// When a display is re-added, the window does not get focus since the request was cleared.
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, windows);
ASSERT_FALSE(changes);
}
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 337b52b..1132e92 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -20,7 +20,6 @@
#include <flag_macros.h>
#include <gestures/GestureConverter.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
@@ -46,18 +45,17 @@
} // namespace
+using android::ui::ADISPLAY_ID_DEFAULT;
using testing::AllOf;
using testing::Each;
using testing::ElementsAre;
using testing::VariantWith;
-class GestureConverterTestBase : public testing::Test {
+class GestureConverterTest : public testing::Test {
protected:
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr int32_t EVENTHUB_ID = 1;
static constexpr stime_t ARBITRARY_GESTURE_TIME = 1.2;
- static constexpr float POINTER_X = 500;
- static constexpr float POINTER_Y = 200;
void SetUp() {
mFakeEventHub = std::make_unique<FakeEventHub>();
@@ -68,12 +66,6 @@
mDevice = newDevice();
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_X, -500, 500, 0, 0, 20);
mFakeEventHub->addAbsoluteAxis(EVENTHUB_ID, ABS_MT_POSITION_Y, -500, 500, 0, 0, 20);
-
- mFakePointerController = std::make_shared<FakePointerController>(
- /*enabled=*/!input_flags::enable_pointer_choreographer());
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(POINTER_X, POINTER_Y);
- mFakePolicy->setPointerController(mFakePointerController);
}
std::shared_ptr<InputDevice> newDevice() {
@@ -96,21 +88,12 @@
std::unique_ptr<TestInputListener> mFakeListener;
std::unique_ptr<InstrumentedInputReader> mReader;
std::shared_ptr<InputDevice> mDevice;
- std::shared_ptr<FakePointerController> mFakePointerController;
-};
-
-class GestureConverterTest : public GestureConverterTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(false);
- GestureConverterTestBase::SetUp();
- }
};
TEST_F(GestureConverterTest, Move) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -118,38 +101,31 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0, 0))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
WithRelativeMotion(-5, 10), WithButtonState(0),
WithPressure(0.0f)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
-
- // The same gesture again should only repeat the HOVER_MOVE and cursor position change, not the
- // HOVER_ENTER.
+ // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER.
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X - 10, POINTER_Y + 20),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
WithButtonState(0), WithPressure(0.0f),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 10, POINTER_Y + 20));
}
TEST_F(GestureConverterTest, Move_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -157,24 +133,21 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
WithRelativeMotion(0, 0))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithCoords(POINTER_X + 10, POINTER_Y + 5),
WithRelativeMotion(10, 5), WithButtonState(0),
WithPressure(0.0f)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
}
TEST_F(GestureConverterTest, ButtonsChange) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -197,9 +170,9 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -210,8 +183,8 @@
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
- WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
// Finally release the right button
@@ -228,16 +201,15 @@
VariantWith<NotifyMotionArgs>(
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ButtonDownAfterMoveExitsHover) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -250,14 +222,14 @@
ASSERT_THAT(args.front(),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
- WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))));
+ WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT))));
}
TEST_F(GestureConverterTest, DragWithButton) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -274,22 +246,19 @@
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
// Move
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10),
- WithToolType(ToolType::FINGER),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0),
+ WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
// Release the button
Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -305,17 +274,16 @@
VariantWith<NotifyMotionArgs>(
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0),
- WithCoords(POINTER_X - 5, POINTER_Y + 10),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll) {
const nsecs_t downTime = 12345;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -323,31 +291,30 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithGestureScrollDistance(0, 0, EPSILON),
WithDownTime(downTime))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X, POINTER_Y - 10),
+ WithCoords(0, -10),
WithGestureScrollDistance(0, 10, EPSILON)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X, POINTER_Y - 15),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -355,18 +322,18 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X, POINTER_Y - 15),
+ WithCoords(0, -15),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_Rotated) {
@@ -374,7 +341,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -382,52 +349,52 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithGestureScrollDistance(0, 0, EPSILON),
WithDownTime(downTime))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 10, POINTER_Y),
+ WithCoords(-10, 0),
WithGestureScrollDistance(0, 10, EPSILON)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(POINTER_X - 15, POINTER_Y),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0),
WithGestureScrollDistance(0, 5, EPSILON),
WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
WithToolType(ToolType::FINGER),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X - 15, POINTER_Y),
+ WithCoords(-15, 0),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -445,13 +412,13 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::NONE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Scroll_ClearsScrollDistanceAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -476,7 +443,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
@@ -497,7 +464,7 @@
TEST_F(GestureConverterTest, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
@@ -524,7 +491,7 @@
// only checks movement in one dimension.
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -535,7 +502,7 @@
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithGestureSwipeFingerCount(3), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
// Three fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -581,7 +548,7 @@
WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
@@ -619,25 +586,25 @@
WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
std::list<NotifyArgs> args =
converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
ASSERT_EQ(4u, args.size());
- ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT))));
+ ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ui::ADISPLAY_ID_DEFAULT))));
// Three fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -681,7 +648,7 @@
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
@@ -706,14 +673,14 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))));
ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT))));
}
TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
@@ -724,7 +691,7 @@
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithGestureSwipeFingerCount(4), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
// Four fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -779,7 +746,7 @@
WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithPointerCount(4u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)));
EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
@@ -828,17 +795,17 @@
WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_Inwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -847,20 +814,18 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(POINTER_X - 100, POINTER_Y),
- WithPointerCount(1u))),
+ WithCoords(-100, 0), WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(
AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCoords(1, POINTER_X + 100, POINTER_Y),
- WithPointerCount(2u)))));
+ WithPointerCoords(1, 100, 0), WithPointerCount(2u)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(1.0f, EPSILON),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -870,10 +835,9 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithMotionClassification(MotionClassification::PINCH),
WithGesturePinchScaleFactor(0.8f, EPSILON),
- WithPointerCoords(0, POINTER_X - 80, POINTER_Y),
- WithPointerCoords(1, POINTER_X + 80, POINTER_Y), WithPointerCount(2u),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0),
+ WithPointerCount(2u), WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -893,7 +857,7 @@
WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
@@ -912,14 +876,12 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(POINTER_X - 100, POINTER_Y),
- WithPointerCount(1u))),
+ WithCoords(-100, 0), WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(
AMOTION_EVENT_ACTION_POINTER_DOWN |
1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCoords(1, POINTER_X + 100, POINTER_Y),
- WithPointerCount(2u)))));
+ WithPointerCoords(1, 100, 0), WithPointerCount(2u)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::PINCH),
@@ -928,15 +890,14 @@
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dz= */ 1.2, GESTURES_ZOOM_UPDATE);
+ /* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.2f, EPSILON),
- WithPointerCoords(0, POINTER_X - 120, POINTER_Y),
- WithPointerCoords(1, POINTER_X + 120, POINTER_Y),
+ WithGesturePinchScaleFactor(1.1f, EPSILON),
+ WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0),
WithPointerCount(2u), WithToolType(ToolType::FINGER),
WithDisplayId(ADISPLAY_ID_DEFAULT)))));
@@ -958,17 +919,17 @@
WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_ClearsClassificationAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -993,7 +954,7 @@
TEST_F(GestureConverterTest, Pinch_ClearsScaleFactorAfterGesture) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -1020,7 +981,7 @@
TEST_F(GestureConverterTest, ResetWithButtonPressed) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
@@ -1044,15 +1005,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithButtonState(0)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringScroll) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
(void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
@@ -1061,24 +1022,24 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(POINTER_X, POINTER_Y - 10),
+ WithCoords(0, -10),
WithGestureScrollDistance(0, 0, EPSILON),
WithMotionClassification(
MotionClassification::TWO_FINGER_SWIPE),
WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
@@ -1113,13 +1074,13 @@
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, ResetDuringPinch) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -1141,17 +1102,17 @@
WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y),
+ WithCoords(0, 0),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, FlingTapDown) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
@@ -1159,20 +1120,16 @@
converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X, POINTER_Y));
- ASSERT_TRUE(mFakePointerController->isPointerShown());
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
+ WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
}
TEST_F(GestureConverterTest, FlingTapDownAfterScrollStopsFling) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
input_flags::enable_touchpad_fling_stop(true);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -1192,19 +1149,19 @@
VariantWith<NotifyMotionArgs>(
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithMotionClassification(MotionClassification::NONE)))));
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE)))));
}
TEST_F(GestureConverterTest, Tap) {
// Tap should produce button press/release events
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1241,17 +1198,17 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithButtonState(0), WithPressure(0.0f)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
}
TEST_F(GestureConverterTest, Click) {
// Click should produce button press/release events
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1278,15 +1235,16 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
WithPressure(1.0f)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
/* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
+
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
@@ -1299,10 +1257,10 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithPressure(0.0f)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithButtonState(0), WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ADISPLAY_ID_DEFAULT)))));
}
TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
@@ -1315,7 +1273,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1344,7 +1302,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1426,1253 +1384,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
- // We don't need to check args here, since it's covered by the FlingTapDown test.
-
- Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
- WithButtonState(0), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_NONE,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
-
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithButtonState(0), WithPressure(0.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(POINTER_X, POINTER_Y),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Future taps should be re-enabled
- ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
- // initially disable tap-to-click
- mReader->getContext()->setPreventingTouchpadTaps(true);
-
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- // We don't need to check args here, since it's covered by the Move test.
-
- // Future taps should be re-enabled
- ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
- const nsecs_t gestureStartTime = 1000;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- // Start a move gesture at gestureStartTime
- Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
-
- // Key presses with IME connection should cancel ongoing move gesture
- nsecs_t currentTime = gestureStartTime + 100;
- mFakePolicy->setIsInputMethodConnectionActive(true);
- mReader->getContext()->setLastKeyDownTimestamp(currentTime);
- moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
- args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT))));
-
- // any updates in existing move gesture should be ignored
- moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
- args = converter.handleGesture(currentTime, READ_TIME, gestureStartTime, moveGesture);
- ASSERT_EQ(0u, args.size());
-
- // New gesture should not be affected
- currentTime += 100;
- moveGesture = Gesture(kGestureMove, currentTime, currentTime, -5, 10);
- args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE))));
-}
-
-// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
-// logic can be removed.
-class GestureConverterTestWithChoreographer : public GestureConverterTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
- GestureConverterTestBase::SetUp();
- }
-};
-
-TEST_F(GestureConverterTestWithChoreographer, Move) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithRelativeMotion(0, 0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithRelativeMotion(-5, 10), WithButtonState(0),
- WithPressure(0.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER.
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0),
- WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithRelativeMotion(0, 0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithRelativeMotion(10, 5), WithButtonState(0),
- WithPressure(0.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- // Press left and right buttons at once
- Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
- /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
- AMOTION_EVENT_BUTTON_SECONDARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
- AMOTION_EVENT_BUTTON_SECONDARY)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Then release the left button
- Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
- /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, leftUpGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Finally release the right button
- Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_RIGHT,
- /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, rightUpGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY))),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_UP)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ButtonDownAfterMoveExitsHover) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
-
- Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE,
- /*is_tap=*/false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
- ASSERT_THAT(args.front(),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0),
- WithCoords(0, 0), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, DragWithButton) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- // Press the button
- Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_LEFT, /* up= */ GESTURES_BUTTON_NONE,
- /* is_tap= */ false);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Move
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, 0),
- WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY), WithPressure(1.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Release the button
- Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_NONE, /* up= */ GESTURES_BUTTON_LEFT,
- /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, upGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY))),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_UP)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Scroll) {
- const nsecs_t downTime = 12345;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args =
- converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(0, 0),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithDownTime(downTime))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(0, -10),
- WithGestureScrollDistance(0, 10, EPSILON)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(0, -15),
- WithGestureScrollDistance(0, 5, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
- GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0, -15),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) {
- const nsecs_t downTime = 12345;
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args =
- converter.handleGesture(downTime, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(0, 0),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithDownTime(downTime))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithCoords(-10, 0),
- WithGestureScrollDistance(0, 10, EPSILON)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(-15, 0),
- WithGestureScrollDistance(0, 5, EPSILON),
- WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
- GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(-15, 0),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::TWO_FINGER_SWIPE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
- GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::NONE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsScrollDistanceAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
- GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
-
- // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
- // need to use another gesture type, like pinch.
- Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
- ASSERT_FALSE(args.empty());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsClassificationAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
- /*dy=*/0);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/-5,
- /*dy=*/10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionClassification(MotionClassification::NONE))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_ClearsGestureAxesAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
- /*dy=*/5);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
-
- // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
- // need to use another gesture type, like pinch.
- Gesture pinchGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, pinchGesture);
- ASSERT_FALSE(args.empty());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithGestureOffset(0, 0, EPSILON), WithGestureSwipeFingerCount(0)));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) {
- // The gestures library will "lock" a swipe into the dimension it starts in. For example, if you
- // start swiping up and then start moving left or right, it'll return gesture events with only Y
- // deltas until you lift your fingers and start swiping again. That's why each of these tests
- // only checks movement in one dimension.
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
- /* dy= */ 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_EQ(4u, args.size());
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithGestureSwipeFingerCount(3), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Three fake fingers should be created. We don't actually care where they are, so long as they
- // move appropriately.
- NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
- PointerCoords finger0Start = arg.pointerCoords[0];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
- PointerCoords finger1Start = arg.pointerCoords[1];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
- PointerCoords finger2Start = arg.pointerCoords[2];
- args.pop_front();
-
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 10);
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 10);
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 10);
-
- Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dx= */ 0, /* dy= */ 5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
- ASSERT_EQ(1u, args.size());
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX());
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX());
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX());
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY() - 15);
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY() - 15);
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY() - 15);
-
- Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(3),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(3),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(3),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setOrientation(ui::ROTATION_90);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
- /* dy= */ 10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_EQ(4u, args.size());
- ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT))));
-
- // Three fake fingers should be created. We don't actually care where they are, so long as they
- // move appropriately.
- NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
- PointerCoords finger0Start = arg.pointerCoords[0];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
- PointerCoords finger1Start = arg.pointerCoords[1];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
- PointerCoords finger2Start = arg.pointerCoords[2];
- args.pop_front();
-
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.01, EPSILON), WithPointerCount(3u)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 10);
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 10);
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 10);
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
-
- Gesture continueGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dx= */ 0, /* dy= */ 5);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
- ASSERT_EQ(1u, args.size());
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() - 15);
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() - 15);
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() - 15);
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
-
- Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))));
- ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dx= */ 10, /* dy= */ 0);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_EQ(5u, args.size());
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithGestureSwipeFingerCount(4), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Four fake fingers should be created. We don't actually care where they are, so long as they
- // move appropriately.
- NotifyMotionArgs arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithGestureOffset(0, 0, EPSILON),
- WithPointerCount(1u)));
- PointerCoords finger0Start = arg.pointerCoords[0];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(2u)));
- PointerCoords finger1Start = arg.pointerCoords[1];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(3u)));
- PointerCoords finger2Start = arg.pointerCoords[2];
- args.pop_front();
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
- 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON), WithPointerCount(4u)));
- PointerCoords finger3Start = arg.pointerCoords[3];
- args.pop_front();
-
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0.01, 0, EPSILON), WithPointerCount(4u)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 10);
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 10);
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 10);
- EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 10);
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
- EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
-
- Gesture continueGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dx= */ 5, /* dy= */ 0);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, continueGesture);
- ASSERT_EQ(1u, args.size());
- arg = std::get<NotifyMotionArgs>(args.front());
- ASSERT_THAT(arg,
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
- WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
- EXPECT_EQ(arg.pointerCoords[0].getX(), finger0Start.getX() + 15);
- EXPECT_EQ(arg.pointerCoords[1].getX(), finger1Start.getX() + 15);
- EXPECT_EQ(arg.pointerCoords[2].getX(), finger2Start.getX() + 15);
- EXPECT_EQ(arg.pointerCoords[3].getX(), finger3Start.getX() + 15);
- EXPECT_EQ(arg.pointerCoords[0].getY(), finger0Start.getY());
- EXPECT_EQ(arg.pointerCoords[1].getY(), finger1Start.getY());
- EXPECT_EQ(arg.pointerCoords[2].getY(), finger2Start.getY());
- EXPECT_EQ(arg.pointerCoords[3].getY(), finger3Start.getY());
-
- Gesture liftGesture(kGestureSwipeLift, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, liftGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 3 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(4),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(4u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(4),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(4),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithGestureOffset(0, 0, EPSILON),
- WithGestureSwipeFingerCount(4),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
- GESTURES_ZOOM_START);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(-100, 0), WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCoords(1, 100, 0), WithPointerCount(2u)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(0.8f, EPSILON),
- WithPointerCoords(0, -80, 0), WithPointerCoords(1, 80, 0),
- WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
- GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
- GESTURES_ZOOM_START);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithCoords(-100, 0), WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_DOWN |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithPointerCoords(1, 100, 0), WithPointerCount(2u)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* dz= */ 1.1, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.1f, EPSILON),
- WithPointerCoords(0, -110, 0), WithPointerCoords(1, 110, 0),
- WithPointerCount(2u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
- GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsClassificationAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_START);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
-
- Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
-
- Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionClassification(MotionClassification::NONE))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Pinch_ClearsScaleFactorAfterGesture) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_START);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*dz=*/1.2, GESTURES_ZOOM_UPDATE);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, updateGesture);
-
- Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_END);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, endGesture);
-
- // Move gestures don't use the fake finger array, so to test that gesture axes are cleared we
- // need to use another gesture type, like scroll.
- Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/1,
- /*dy=*/0);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
- ASSERT_FALSE(args.empty());
- EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGesturePinchScaleFactor(0, EPSILON));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
- /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture);
-
- std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY),
- WithButtonState(0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithButtonState(0)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0, -10),
- WithGestureScrollDistance(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::TWO_FINGER_SWIPE),
- WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
- /*dy=*/10);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(3u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithGestureOffset(0, 0, EPSILON),
- WithMotionClassification(
- MotionClassification::MULTI_FINGER_SWIPE),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
- GESTURES_ZOOM_START);
- (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
-
- std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(2u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithMotionClassification(MotionClassification::PINCH),
- WithGesturePinchScaleFactor(1.0f, EPSILON),
- WithPointerCount(1u))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
-
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0),
- WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER),
- WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT)));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, FlingTapDownAfterScrollStopsFling) {
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- input_flags::enable_touchpad_fling_stop(true);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, scrollGesture);
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
- GESTURES_FLING_START);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
-
- Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
- VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithMotionClassification(MotionClassification::NONE)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Tap) {
- // Tap should produce button press/release events
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
- // We don't need to check args here, since it's covered by the FlingTapDown test.
-
- Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapGesture);
-
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
- WithButtonState(0), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0), WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithButtonState(0), WithPressure(0.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F(GestureConverterTestWithChoreographer, Click) {
- // Click should produce button press/release events
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -2702,183 +1414,7 @@
Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
WithRelativeMotion(0.f, 0.f),
WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_NONE,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonUpGesture);
-
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithPressure(0.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0), WithCoords(0, 0),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION),
- REQUIRES_FLAGS_DISABLED(TOUCHPAD_PALM_REJECTION_V2)) {
- nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
-
- // Tap should be ignored when disabled
- mReader->getContext()->setPreventingTouchpadTaps(true);
-
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
- // We don't need to check args here, since it's covered by the FlingTapDown test.
-
- Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
-
- // no events should be generated
- ASSERT_EQ(0u, args.size());
-
- // Future taps should be re-enabled
- ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabledWithDelay,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
- nsecs_t currentTime = ARBITRARY_GESTURE_TIME;
-
- // Tap should be ignored when disabled
- mReader->getContext()->setPreventingTouchpadTaps(true);
-
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
- // We don't need to check args here, since it's covered by the FlingTapDown test.
-
- Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
-
- // no events should be generated
- ASSERT_EQ(0u, args.size());
-
- // Future taps should be re-enabled
- ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
-
- // taps before the threshold should still be ignored
- currentTime += TAP_ENABLE_DELAY_NANOS.count();
- flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
-
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
-
- tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
-
- // no events should be generated
- ASSERT_EQ(0u, args.size());
-
- // taps after the threshold should be recognised
- currentTime += 1;
- flingGesture = Gesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture);
-
- ASSERT_EQ(1u, args.size());
- ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0)));
-
- tapGesture = Gesture(kGestureButtonsChange, currentTime, currentTime,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true);
- args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
- WithButtonState(0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithButtonState(0)))));
- ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithRelativeMotion(0.f, 0.f))));
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisabled,
- REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
- // Click should still produce button press/release events
- mReader->getContext()->setPreventingTouchpadTaps(true);
-
- InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
- GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
-
- Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
- /* vy= */ 0, GESTURES_FLING_TAP_DOWN);
- std::list<NotifyArgs> args =
- converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture);
- // We don't need to check args here, since it's covered by the FlingTapDown test.
-
- Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
- /* down= */ GESTURES_BUTTON_LEFT,
- /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false);
- args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture);
-
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
- WithButtonState(0), WithPressure(0.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
- WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
- WithPressure(1.0f)))));
- ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithCoords(0, 0),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::ADISPLAY_ID_DEFAULT)))));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
@@ -2909,14 +1445,14 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick,
+TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick,
REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION)) {
// initially disable tap-to-click
mReader->getContext()->setPreventingTouchpadTaps(true);
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -2927,12 +1463,12 @@
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
}
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMove,
+TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove,
REQUIRES_FLAGS_ENABLED(TOUCHPAD_PALM_REJECTION_V2)) {
const nsecs_t gestureStartTime = 1000;
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::ADISPLAY_ID_DEFAULT);
// Start a move gesture at gestureStartTime
Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10);
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 85e055d..029414b 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -18,7 +18,6 @@
#include <NotifyArgsBuilders.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <input/InputEventBuilders.h>
#include <linux/input.h>
@@ -64,7 +63,7 @@
uint32_t sources = TOUCHSCREEN | STYLUS) {
auto info = InputDeviceInfo();
info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, generateTestIdentifier(id),
- "alias", /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+ "alias", /*isExternal=*/false, /*hasMic=*/false, ui::ADISPLAY_ID_NONE);
info.addSource(sources);
return info;
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 9e3a4f1..184659d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,7 +16,9 @@
#include "../dispatcher/InputDispatcher.h"
#include "FakeApplicationHandle.h"
+#include "FakeInputDispatcherPolicy.h"
#include "FakeInputTracingBackend.h"
+#include "FakeWindows.h"
#include "TestEventMatchers.h"
#include <NotifyArgsBuilders.h>
@@ -51,12 +53,15 @@
using android::gui::WindowInfoHandle;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
+using android::ui::ADISPLAY_ID_DEFAULT;
namespace android::inputdispatcher {
using namespace ftl::flag_operators;
using testing::AllOf;
using testing::Not;
+using testing::Pointee;
+using testing::UnorderedElementsAre;
namespace {
@@ -68,8 +73,8 @@
static constexpr int32_t SECOND_DEVICE_ID = 2;
// An arbitrary display id.
-static constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;
-static constexpr int32_t SECOND_DISPLAY_ID = 1;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ADISPLAY_ID_DEFAULT;
+constexpr ui::LogicalDisplayId SECOND_DISPLAY_ID = ui::LogicalDisplayId{1};
// Ensure common actions are interchangeable between keys and motions for convenience.
static_assert(AMOTION_EVENT_ACTION_DOWN == AKEY_EVENT_ACTION_DOWN);
@@ -106,10 +111,6 @@
static constexpr int32_t POINTER_2_UP =
AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
-// The default pid and uid for windows created on the primary display by the test.
-static constexpr gui::Pid WINDOW_PID{999};
-static constexpr gui::Uid WINDOW_UID{1001};
-
// The default pid and uid for the windows created on the secondary display by the test.
static constexpr gui::Pid SECONDARY_WINDOW_PID{1010};
static constexpr gui::Uid SECONDARY_WINDOW_UID{1012};
@@ -117,24 +118,7 @@
// An arbitrary pid of the gesture monitor window
static constexpr gui::Pid MONITOR_PID{2001};
-/**
- * If we expect to receive the event, the timeout can be made very long. When the test are running
- * correctly, we will actually never wait until the end of the timeout because the wait will end
- * when the event comes in. Still, this value shouldn't be infinite. During development, a local
- * change may cause the test to fail. This timeout should be short enough to not annoy so that the
- * developer can see the failure quickly (on human scale).
- */
-static constexpr std::chrono::duration CONSUME_TIMEOUT_EVENT_EXPECTED = 1000ms;
-/**
- * When no event is expected, we can have a very short timeout. A large value here would slow down
- * the tests. In the unlikely event of system being too slow, the event may still be present but the
- * timeout would complete before it is consumed. This would result in test flakiness. If this
- * occurs, the flakiness rate would be high. Since the flakes are treated with high priority, this
- * would get noticed and addressed quickly.
- */
-static constexpr std::chrono::duration CONSUME_TIMEOUT_NO_EVENT_EXPECTED = 10ms;
-
-static constexpr int expectedWallpaperFlags =
+static constexpr int EXPECTED_WALLPAPER_FLAGS =
AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
using ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID;
@@ -145,544 +129,66 @@
static KeyEvent getTestKeyEvent() {
KeyEvent event;
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
return event;
}
-// --- FakeInputDispatcherPolicy ---
-
-class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
- struct AnrResult {
- sp<IBinder> token{};
- std::optional<gui::Pid> pid{};
- };
- /* Stores data about a user-activity-poke event from the dispatcher. */
- struct UserActivityPokeEvent {
- nsecs_t eventTime;
- int32_t eventType;
- int32_t displayId;
-
- bool operator==(const UserActivityPokeEvent& rhs) const = default;
-
- friend std::ostream& operator<<(std::ostream& os, const UserActivityPokeEvent& ev) {
- os << "UserActivityPokeEvent[time=" << ev.eventTime << ", eventType=" << ev.eventType
- << ", displayId=" << ev.displayId << "]";
- return os;
- }
- };
-
+/**
+ * Provide a local override for a flag value. The value is restored when the object of this class
+ * goes out of scope.
+ * This class is not intended to be used directly, because its usage is cumbersome.
+ * Instead, a wrapper macro SCOPED_FLAG_OVERRIDE is provided.
+ */
+class ScopedFlagOverride {
public:
- FakeInputDispatcherPolicy() = default;
- virtual ~FakeInputDispatcherPolicy() = default;
-
- void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- assertFilterInputEventWasCalledInternal([&args](const InputEvent& event) {
- ASSERT_EQ(event.getType(), InputEventType::KEY);
- EXPECT_EQ(event.getDisplayId(), args.displayId);
-
- const auto& keyEvent = static_cast<const KeyEvent&>(event);
- EXPECT_EQ(keyEvent.getEventTime(), args.eventTime);
- EXPECT_EQ(keyEvent.getAction(), args.action);
- });
+ ScopedFlagOverride(std::function<bool()> read, std::function<void(bool)> write, bool value)
+ : mInitialValue(read()), mWriteValue(write) {
+ mWriteValue(value);
}
-
- void assertFilterInputEventWasCalled(const NotifyMotionArgs& args, vec2 point) {
- assertFilterInputEventWasCalledInternal([&](const InputEvent& event) {
- ASSERT_EQ(event.getType(), InputEventType::MOTION);
- EXPECT_EQ(event.getDisplayId(), args.displayId);
-
- const auto& motionEvent = static_cast<const MotionEvent&>(event);
- EXPECT_EQ(motionEvent.getEventTime(), args.eventTime);
- EXPECT_EQ(motionEvent.getAction(), args.action);
- EXPECT_NEAR(motionEvent.getX(0), point.x, MotionEvent::ROUNDING_PRECISION);
- EXPECT_NEAR(motionEvent.getY(0), point.y, MotionEvent::ROUNDING_PRECISION);
- EXPECT_NEAR(motionEvent.getRawX(0), point.x, MotionEvent::ROUNDING_PRECISION);
- EXPECT_NEAR(motionEvent.getRawY(0), point.y, MotionEvent::ROUNDING_PRECISION);
- });
- }
-
- void assertFilterInputEventWasNotCalled() {
- std::scoped_lock lock(mLock);
- ASSERT_EQ(nullptr, mFilteredEvent);
- }
-
- void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mConfigurationChangedTime)
- << "Timed out waiting for configuration changed call";
- ASSERT_EQ(*mConfigurationChangedTime, when);
- mConfigurationChangedTime = std::nullopt;
- }
-
- void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mLastNotifySwitch);
- // We do not check id because it is not exposed to the policy
- EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
- EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
- EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
- EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
- mLastNotifySwitch = std::nullopt;
- }
-
- void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
- std::scoped_lock lock(mLock);
- ASSERT_EQ(touchedToken, mOnPointerDownToken);
- mOnPointerDownToken.clear();
- }
-
- void assertOnPointerDownWasNotCalled() {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mOnPointerDownToken == nullptr)
- << "Expected onPointerDownOutsideFocus to not have been called";
- }
-
- // This function must be called soon after the expected ANR timer starts,
- // because we are also checking how much time has passed.
- void assertNotifyNoFocusedWindowAnrWasCalled(
- std::chrono::nanoseconds timeout,
- const std::shared_ptr<InputApplicationHandle>& expectedApplication) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- std::shared_ptr<InputApplicationHandle> application;
- ASSERT_NO_FATAL_FAILURE(
- application = getAnrTokenLockedInterruptible(timeout, mAnrApplications, lock));
- ASSERT_EQ(expectedApplication, application);
- }
-
- void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
- const sp<WindowInfoHandle>& window) {
- LOG_ALWAYS_FATAL_IF(window == nullptr, "window should not be null");
- assertNotifyWindowUnresponsiveWasCalled(timeout, window->getToken(),
- window->getInfo()->ownerPid);
- }
-
- void assertNotifyWindowUnresponsiveWasCalled(std::chrono::nanoseconds timeout,
- const sp<IBinder>& expectedToken,
- std::optional<gui::Pid> expectedPid) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- AnrResult result;
- ASSERT_NO_FATAL_FAILURE(result =
- getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock));
- ASSERT_EQ(expectedToken, result.token);
- ASSERT_EQ(expectedPid, result.pid);
- }
-
- /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
- sp<IBinder> getUnresponsiveWindowToken(std::chrono::nanoseconds timeout) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- AnrResult result = getAnrTokenLockedInterruptible(timeout, mAnrWindows, lock);
- const auto& [token, _] = result;
- return token;
- }
-
- void assertNotifyWindowResponsiveWasCalled(const sp<IBinder>& expectedToken,
- std::optional<gui::Pid> expectedPid) {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- AnrResult result;
- ASSERT_NO_FATAL_FAILURE(
- result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock));
- ASSERT_EQ(expectedToken, result.token);
- ASSERT_EQ(expectedPid, result.pid);
- }
-
- /** Wrap call with ASSERT_NO_FATAL_FAILURE() to ensure the return value is valid. */
- sp<IBinder> getResponsiveWindowToken() {
- std::unique_lock lock(mLock);
- android::base::ScopedLockAssertion assumeLocked(mLock);
- AnrResult result = getAnrTokenLockedInterruptible(0s, mResponsiveWindows, lock);
- const auto& [token, _] = result;
- return token;
- }
-
- void assertNotifyAnrWasNotCalled() {
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mAnrApplications.empty());
- ASSERT_TRUE(mAnrWindows.empty());
- ASSERT_TRUE(mResponsiveWindows.empty())
- << "ANR was not called, but please also consume the 'connection is responsive' "
- "signal";
- }
-
- PointerCaptureRequest assertSetPointerCaptureCalled(const sp<WindowInfoHandle>& window,
- bool enabled) {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- if (!mPointerCaptureChangedCondition
- .wait_for(lock, 100ms, [this, enabled, window]() REQUIRES(mLock) {
- if (enabled) {
- return mPointerCaptureRequest->isEnable() &&
- mPointerCaptureRequest->window == window->getToken();
- } else {
- return !mPointerCaptureRequest->isEnable();
- }
- })) {
- ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << window->getName() << ", "
- << enabled << ") to be called.";
- return {};
- }
- auto request = *mPointerCaptureRequest;
- mPointerCaptureRequest.reset();
- return request;
- }
-
- void assertSetPointerCaptureNotCalled() {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- if (mPointerCaptureChangedCondition.wait_for(lock, 100ms) != std::cv_status::timeout) {
- FAIL() << "Expected setPointerCapture(request) to not be called, but was called. "
- "enabled = "
- << std::to_string(mPointerCaptureRequest->isEnable());
- }
- mPointerCaptureRequest.reset();
- }
-
- void assertDropTargetEquals(const InputDispatcherInterface& dispatcher,
- const sp<IBinder>& targetToken) {
- dispatcher.waitForIdle();
- std::scoped_lock lock(mLock);
- ASSERT_TRUE(mNotifyDropWindowWasCalled);
- ASSERT_EQ(targetToken, mDropTargetWindowToken);
- mNotifyDropWindowWasCalled = false;
- }
-
- void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
- std::optional<sp<IBinder>> receivedToken =
- getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
- mNotifyInputChannelBroken);
- ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token";
- ASSERT_EQ(token, *receivedToken);
- }
-
- /**
- * Set policy timeout. A value of zero means next key will not be intercepted.
- */
- void setInterceptKeyTimeout(std::chrono::milliseconds timeout) {
- mInterceptKeyTimeout = timeout;
- }
-
- std::chrono::nanoseconds getKeyWaitingForEventsTimeout() override { return 500ms; }
-
- void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; }
-
- void assertUserActivityNotPoked() {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- std::optional<UserActivityPokeEvent> pokeEvent =
- getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
- mNotifyUserActivity);
-
- ASSERT_FALSE(pokeEvent) << "Expected user activity not to have been poked";
- }
-
- /**
- * Asserts that a user activity poke has happened. The earliest recorded poke event will be
- * cleared after this call.
- *
- * If an expected UserActivityPokeEvent is provided, asserts that the given event is the
- * earliest recorded poke event.
- */
- void assertUserActivityPoked(std::optional<UserActivityPokeEvent> expectedPokeEvent = {}) {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- std::optional<UserActivityPokeEvent> pokeEvent =
- getItemFromStorageLockedInterruptible(500ms, mUserActivityPokeEvents, lock,
- mNotifyUserActivity);
- ASSERT_TRUE(pokeEvent) << "Expected a user poke event";
-
- if (expectedPokeEvent) {
- ASSERT_EQ(expectedPokeEvent, *pokeEvent);
- }
- }
-
- void assertNotifyDeviceInteractionWasCalled(int32_t deviceId, std::set<gui::Uid> uids) {
- ASSERT_EQ(std::make_pair(deviceId, uids), mNotifiedInteractions.popWithTimeout(100ms));
- }
-
- void assertNotifyDeviceInteractionWasNotCalled() {
- ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms));
- }
-
- void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) {
- std::scoped_lock lock(mLock);
- mUnhandledKeyHandler = handler;
- }
-
- void assertUnhandledKeyReported(int32_t keycode) {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
- std::optional<int32_t> unhandledKeycode =
- getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock,
- mNotifyUnhandledKey);
- ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported";
- ASSERT_EQ(unhandledKeycode, keycode);
- }
-
- void assertUnhandledKeyNotReported() {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
- std::optional<int32_t> unhandledKeycode =
- getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock,
- mNotifyUnhandledKey);
- ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported";
- }
+ ~ScopedFlagOverride() { mWriteValue(mInitialValue); }
private:
- std::mutex mLock;
- std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
- std::optional<nsecs_t> mConfigurationChangedTime GUARDED_BY(mLock);
- sp<IBinder> mOnPointerDownToken GUARDED_BY(mLock);
- std::optional<NotifySwitchArgs> mLastNotifySwitch GUARDED_BY(mLock);
-
- std::condition_variable mPointerCaptureChangedCondition;
-
- std::optional<PointerCaptureRequest> mPointerCaptureRequest GUARDED_BY(mLock);
-
- // ANR handling
- std::queue<std::shared_ptr<InputApplicationHandle>> mAnrApplications GUARDED_BY(mLock);
- std::queue<AnrResult> mAnrWindows GUARDED_BY(mLock);
- std::queue<AnrResult> mResponsiveWindows GUARDED_BY(mLock);
- std::condition_variable mNotifyAnr;
- std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
- std::condition_variable mNotifyInputChannelBroken;
-
- sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
- bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
-
- std::condition_variable mNotifyUserActivity;
- std::queue<UserActivityPokeEvent> mUserActivityPokeEvents;
-
- std::chrono::milliseconds mInterceptKeyTimeout = 0ms;
-
- std::chrono::nanoseconds mStaleEventTimeout = 1000ms;
-
- BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions;
-
- std::condition_variable mNotifyUnhandledKey;
- std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock);
- std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock);
-
- // All three ANR-related callbacks behave the same way, so we use this generic function to wait
- // for a specific container to become non-empty. When the container is non-empty, return the
- // first entry from the container and erase it.
- template <class T>
- T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
- std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
- // If there is an ANR, Dispatcher won't be idle because there are still events
- // in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
- // before checking if ANR was called.
- // Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
- // to provide it some time to act. 100ms seems reasonable.
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
- const std::chrono::time_point start = std::chrono::steady_clock::now();
- std::optional<T> token =
- getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
- if (!token.has_value()) {
- ADD_FAILURE() << "Did not receive the ANR callback";
- return {};
- }
-
- const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- // Ensure that the ANR didn't get raised too early. We can't be too strict here because
- // the dispatcher started counting before this function was called
- if (std::chrono::abs(timeout - waited) > 100ms) {
- ADD_FAILURE() << "ANR was raised too early or too late. Expected "
- << std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
- << "ms, but waited "
- << std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
- << "ms instead";
- }
- return *token;
- }
-
- template <class T>
- std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
- std::queue<T>& storage,
- std::unique_lock<std::mutex>& lock,
- std::condition_variable& condition)
- REQUIRES(mLock) {
- condition.wait_for(lock, timeout,
- [&storage]() REQUIRES(mLock) { return !storage.empty(); });
- if (storage.empty()) {
- return std::nullopt;
- }
- T item = storage.front();
- storage.pop();
- return std::make_optional(item);
- }
-
- void notifyConfigurationChanged(nsecs_t when) override {
- std::scoped_lock lock(mLock);
- mConfigurationChangedTime = when;
- }
-
- void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
- const std::string&) override {
- std::scoped_lock lock(mLock);
- mAnrWindows.push({connectionToken, pid});
- mNotifyAnr.notify_all();
- }
-
- void notifyWindowResponsive(const sp<IBinder>& connectionToken,
- std::optional<gui::Pid> pid) override {
- std::scoped_lock lock(mLock);
- mResponsiveWindows.push({connectionToken, pid});
- mNotifyAnr.notify_all();
- }
-
- void notifyNoFocusedWindowAnr(
- const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
- std::scoped_lock lock(mLock);
- mAnrApplications.push(applicationHandle);
- mNotifyAnr.notify_all();
- }
-
- void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override {
- std::scoped_lock lock(mLock);
- mBrokenInputChannels.push(connectionToken);
- mNotifyInputChannelBroken.notify_all();
- }
-
- void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
-
- void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
- const std::vector<float>& values) override {}
-
- void notifySensorAccuracy(int deviceId, InputDeviceSensorType sensorType,
- InputDeviceSensorAccuracy accuracy) override {}
-
- void notifyVibratorState(int32_t deviceId, bool isOn) override {}
-
- bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
- std::scoped_lock lock(mLock);
- switch (inputEvent.getType()) {
- case InputEventType::KEY: {
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(inputEvent);
- mFilteredEvent = std::make_unique<KeyEvent>(keyEvent);
- break;
- }
-
- case InputEventType::MOTION: {
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(inputEvent);
- mFilteredEvent = std::make_unique<MotionEvent>(motionEvent);
- break;
- }
- default: {
- ADD_FAILURE() << "Should only filter keys or motions";
- break;
- }
- }
- return true;
- }
-
- void interceptKeyBeforeQueueing(const KeyEvent& inputEvent, uint32_t&) override {
- if (inputEvent.getAction() == AKEY_EVENT_ACTION_UP) {
- // Clear intercept state when we handled the event.
- mInterceptKeyTimeout = 0ms;
- }
- }
-
- void interceptMotionBeforeQueueing(int32_t, uint32_t, int32_t, nsecs_t, uint32_t&) override {}
-
- nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
- nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count();
- // Clear intercept state so we could dispatch the event in next wake.
- mInterceptKeyTimeout = 0ms;
- return delay;
- }
-
- std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event,
- uint32_t) override {
- std::scoped_lock lock(mLock);
- mReportedUnhandledKeycodes.emplace(event.getKeyCode());
- mNotifyUnhandledKey.notify_all();
- return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt;
- }
-
- void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
- uint32_t policyFlags) override {
- std::scoped_lock lock(mLock);
- /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
- * essentially a passthrough for notifySwitch.
- */
- mLastNotifySwitch =
- NotifySwitchArgs(InputEvent::nextId(), when, policyFlags, switchValues, switchMask);
- }
-
- void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) override {
- std::scoped_lock lock(mLock);
- mNotifyUserActivity.notify_all();
- mUserActivityPokeEvents.push({eventTime, eventType, displayId});
- }
-
- bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override {
- return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout;
- }
-
- void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {
- std::scoped_lock lock(mLock);
- mOnPointerDownToken = newToken;
- }
-
- void setPointerCapture(const PointerCaptureRequest& request) override {
- std::scoped_lock lock(mLock);
- mPointerCaptureRequest = {request};
- mPointerCaptureChangedCondition.notify_all();
- }
-
- void notifyDropWindow(const sp<IBinder>& token, float x, float y) override {
- std::scoped_lock lock(mLock);
- mNotifyDropWindowWasCalled = true;
- mDropTargetWindowToken = token;
- }
-
- void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
- const std::set<gui::Uid>& uids) override {
- ASSERT_TRUE(mNotifiedInteractions.emplace(deviceId, uids));
- }
-
- void assertFilterInputEventWasCalledInternal(
- const std::function<void(const InputEvent&)>& verify) {
- std::scoped_lock lock(mLock);
- ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- verify(*mFilteredEvent);
- mFilteredEvent = nullptr;
- }
-
- gui::Uid getPackageUid(std::string) override { return gui::Uid::INVALID; }
+ const bool mInitialValue;
+ std::function<void(bool)> mWriteValue;
};
+
+typedef bool (*readFlagValueFunction)();
+typedef void (*writeFlagValueFunction)(bool);
+
+/**
+ * Use this macro to locally override a flag value.
+ * Example usage:
+ * SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+ * Note: this works by creating a local variable in your current scope. Don't call this twice for
+ * the same flag, because the variable names will clash!
+ */
+#define SCOPED_FLAG_OVERRIDE(NAME, VALUE) \
+ readFlagValueFunction read##NAME = com::android::input::flags::NAME; \
+ writeFlagValueFunction write##NAME = com::android::input::flags::NAME; \
+ ScopedFlagOverride override##NAME(read##NAME, write##NAME, (VALUE))
+
} // namespace
// --- 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;
+ std::shared_ptr<VerifyingTrace> mVerifyingTrace;
void SetUp() override {
- gVerifyingTrace->reset();
+ mVerifyingTrace = std::make_shared<VerifyingTrace>();
+ FakeWindowHandle::sOnEventReceivedCallback = [this](const auto& _1, const auto& _2) {
+ handleEventReceivedByWindow(_1, _2);
+ };
+
mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy,
std::make_unique<FakeInputTracingBackend>(
- gVerifyingTrace));
+ mVerifyingTrace));
mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
@@ -690,12 +196,35 @@
}
void TearDown() override {
- ASSERT_NO_FATAL_FAILURE(gVerifyingTrace->verifyExpectedEventsTraced());
+ ASSERT_NO_FATAL_FAILURE(mVerifyingTrace->verifyExpectedEventsTraced());
+ FakeWindowHandle::sOnEventReceivedCallback = nullptr;
+
ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.reset();
mDispatcher.reset();
}
+ void handleEventReceivedByWindow(const std::unique_ptr<InputEvent>& event,
+ const gui::WindowInfo& info) {
+ if (!event) {
+ return;
+ }
+
+ switch (event->getType()) {
+ case InputEventType::KEY: {
+ mVerifyingTrace->expectKeyDispatchTraced(static_cast<KeyEvent&>(*event), info.id);
+ break;
+ }
+ case InputEventType::MOTION: {
+ mVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event),
+ info.id);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
/**
* Used for debugging when writing the test
*/
@@ -715,7 +244,7 @@
request.token = window->getToken();
request.windowName = window->getName();
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
- request.displayId = window->getInfo()->displayId;
+ request.displayId = window->getInfo()->displayId.val();
mDispatcher->setFocusedWindow(request);
}
};
@@ -724,7 +253,7 @@
KeyEvent event;
// Rejects undefined key actions.
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE,
INVALID_HMAC,
/*action=*/-1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
@@ -734,7 +263,7 @@
<< "Should reject key events with undefined action.";
// Rejects ACTION_MULTIPLE since it is not supported despite being defined in the API.
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -907,613 +436,16 @@
namespace {
static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 500ms;
-// Default input dispatching timeout if there is no focused application or paused window
-// from which to determine an appropriate dispatching timeout.
-static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
- android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
- android::base::HwTimeoutMultiplier());
-
-class FakeInputReceiver {
-public:
- explicit FakeInputReceiver(std::unique_ptr<InputChannel> clientChannel, const std::string name)
- : mConsumer(std::move(clientChannel)), mName(name) {}
-
- std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = false) {
- auto [consumeSeq, event] = receiveEvent(timeout);
- if (!consumeSeq) {
- return nullptr;
- }
- finishEvent(*consumeSeq, handled);
- return std::move(event);
- }
-
- /**
- * Receive an event without acknowledging it.
- * Return the sequence number that could later be used to send finished signal.
- */
- std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent(
- std::chrono::milliseconds timeout) {
- uint32_t consumeSeq;
- std::unique_ptr<InputEvent> event;
-
- std::chrono::time_point start = std::chrono::steady_clock::now();
- status_t status = WOULD_BLOCK;
- while (status == WOULD_BLOCK) {
- InputEvent* rawEventPtr = nullptr;
- status = mConsumer.consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
- &rawEventPtr);
- event = std::unique_ptr<InputEvent>(rawEventPtr);
- std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
- if (elapsed > timeout) {
- break;
- }
- }
-
- if (status == WOULD_BLOCK) {
- // Just means there's no event available.
- return std::make_pair(std::nullopt, nullptr);
- }
-
- if (status != OK) {
- ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
- return std::make_pair(std::nullopt, nullptr);
- }
- if (event == nullptr) {
- ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
- }
- return std::make_pair(consumeSeq, std::move(event));
- }
-
- /**
- * To be used together with "receiveEvent" to complete the consumption of an event.
- */
- void finishEvent(uint32_t consumeSeq, bool handled = true) {
- const status_t status = mConsumer.sendFinishedSignal(consumeSeq, handled);
- ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
- }
-
- void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
- const status_t status = mConsumer.sendTimeline(inputEventId, timeline);
- ASSERT_EQ(OK, status);
- }
-
- void consumeEvent(InputEventType expectedEventType, int32_t expectedAction,
- std::optional<int32_t> expectedDisplayId,
- std::optional<int32_t> expectedFlags) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
-
- ASSERT_NE(nullptr, event) << mName.c_str()
- << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << " expected " << ftl::enum_string(expectedEventType)
- << " event, got " << *event;
-
- if (expectedDisplayId.has_value()) {
- EXPECT_EQ(expectedDisplayId, event->getDisplayId());
- }
-
- switch (expectedEventType) {
- case InputEventType::KEY: {
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*event);
- ASSERT_THAT(keyEvent, WithKeyAction(expectedAction));
- if (expectedFlags.has_value()) {
- EXPECT_EQ(expectedFlags.value(), keyEvent.getFlags());
- }
- break;
- }
- case InputEventType::MOTION: {
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
- ASSERT_THAT(motionEvent, WithMotionAction(expectedAction));
- if (expectedFlags.has_value()) {
- EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags());
- }
- break;
- }
- case InputEventType::FOCUS: {
- FAIL() << "Use 'consumeFocusEvent' for FOCUS events";
- }
- case InputEventType::CAPTURE: {
- FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
- }
- case InputEventType::TOUCH_MODE: {
- FAIL() << "Use 'consumeTouchModeEvent' for TOUCH_MODE events";
- }
- case InputEventType::DRAG: {
- FAIL() << "Use 'consumeDragEvent' for DRAG events";
- }
- }
- }
-
- std::unique_ptr<MotionEvent> consumeMotion() {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
-
- if (event == nullptr) {
- ADD_FAILURE() << mName << ": expected a MotionEvent, but didn't get one.";
- return nullptr;
- }
-
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << mName << " expected a MotionEvent, got " << *event;
- return nullptr;
- }
- return std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
- }
-
- void consumeMotionEvent(const ::testing::Matcher<MotionEvent>& matcher) {
- std::unique_ptr<MotionEvent> motionEvent = consumeMotion();
- ASSERT_NE(nullptr, motionEvent) << "Did not get a motion event, but expected " << matcher;
- ASSERT_THAT(*motionEvent, matcher);
- }
-
- void consumeFocusEvent(bool hasFocus, bool inTouchMode) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- ASSERT_NE(nullptr, event) << mName.c_str()
- << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::FOCUS, event->getType())
- << "Instead of FocusEvent, got " << *event;
-
- ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
- << mName.c_str() << ": event displayId should always be NONE.";
-
- FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
- EXPECT_EQ(hasFocus, focusEvent.getHasFocus());
- }
-
- void consumeCaptureEvent(bool hasCapture) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- ASSERT_NE(nullptr, event) << mName.c_str()
- << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::CAPTURE, event->getType())
- << "Instead of CaptureEvent, got " << *event;
-
- ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
- << mName.c_str() << ": event displayId should always be NONE.";
-
- const auto& captureEvent = static_cast<const CaptureEvent&>(*event);
- EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
- }
-
- void consumeDragEvent(bool isExiting, float x, float y) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- ASSERT_NE(nullptr, event) << mName.c_str()
- << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::DRAG, event->getType()) << "Instead of DragEvent, got " << *event;
-
- EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
- << mName.c_str() << ": event displayId should always be NONE.";
-
- const auto& dragEvent = static_cast<const DragEvent&>(*event);
- EXPECT_EQ(isExiting, dragEvent.isExiting());
- EXPECT_EQ(x, dragEvent.getX());
- EXPECT_EQ(y, dragEvent.getY());
- }
-
- void consumeTouchModeEvent(bool inTouchMode) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- ASSERT_NE(nullptr, event) << mName.c_str()
- << ": consumer should have returned non-NULL event.";
- ASSERT_EQ(InputEventType::TOUCH_MODE, event->getType())
- << "Instead of TouchModeEvent, got " << *event;
-
- ASSERT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
- << mName.c_str() << ": event displayId should always be NONE.";
- const auto& touchModeEvent = static_cast<const TouchModeEvent&>(*event);
- EXPECT_EQ(inTouchMode, touchModeEvent.isInTouchMode());
- }
-
- void assertNoEvents(std::chrono::milliseconds timeout) {
- std::unique_ptr<InputEvent> event = consume(timeout);
- if (event == nullptr) {
- return;
- }
- if (event->getType() == InputEventType::KEY) {
- KeyEvent& keyEvent = static_cast<KeyEvent&>(*event);
- ADD_FAILURE() << "Received key event " << keyEvent;
- } else if (event->getType() == InputEventType::MOTION) {
- MotionEvent& motionEvent = static_cast<MotionEvent&>(*event);
- ADD_FAILURE() << "Received motion event " << motionEvent;
- } else if (event->getType() == InputEventType::FOCUS) {
- FocusEvent& focusEvent = static_cast<FocusEvent&>(*event);
- ADD_FAILURE() << "Received focus event, hasFocus = "
- << (focusEvent.getHasFocus() ? "true" : "false");
- } else if (event->getType() == InputEventType::CAPTURE) {
- const auto& captureEvent = static_cast<CaptureEvent&>(*event);
- ADD_FAILURE() << "Received capture event, pointerCaptureEnabled = "
- << (captureEvent.getPointerCaptureEnabled() ? "true" : "false");
- } else if (event->getType() == InputEventType::TOUCH_MODE) {
- const auto& touchModeEvent = static_cast<TouchModeEvent&>(*event);
- ADD_FAILURE() << "Received touch mode event, inTouchMode = "
- << (touchModeEvent.isInTouchMode() ? "true" : "false");
- }
- FAIL() << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
- }
-
- sp<IBinder> getToken() { return mConsumer.getChannel()->getConnectionToken(); }
-
- int getChannelFd() { return mConsumer.getChannel()->getFd(); }
-
-private:
- InputConsumer mConsumer;
- DynamicInputEventFactory mEventFactory;
-
- std::string mName;
-};
-
-class FakeWindowHandle : public WindowInfoHandle {
-public:
- static const int32_t WIDTH = 600;
- static const int32_t HEIGHT = 800;
-
- FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
- const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId, bool createInputChannel = true)
- : mName(name) {
- sp<IBinder> token;
- if (createInputChannel) {
- base::Result<std::unique_ptr<InputChannel>> channel =
- dispatcher->createInputChannel(name);
- token = (*channel)->getConnectionToken();
- mInputReceiver = std::make_unique<FakeInputReceiver>(std::move(*channel), name);
- }
-
- inputApplicationHandle->updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
-
- mInfo.token = token;
- mInfo.id = sId++;
- mInfo.name = name;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.alpha = 1.0;
- mInfo.frame = Rect(0, 0, WIDTH, HEIGHT);
- mInfo.transform.set(0, 0);
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
- mInfo.ownerPid = WINDOW_PID;
- mInfo.ownerUid = WINDOW_UID;
- mInfo.displayId = displayId;
- mInfo.inputConfig = WindowInfo::InputConfig::DEFAULT;
- }
-
- sp<FakeWindowHandle> clone(int32_t displayId) {
- sp<FakeWindowHandle> handle = sp<FakeWindowHandle>::make(mInfo.name + "(Mirror)");
- handle->mInfo = mInfo;
- handle->mInfo.displayId = displayId;
- handle->mInfo.id = sId++;
- handle->mInputReceiver = mInputReceiver;
- return handle;
- }
-
- void setTouchable(bool touchable) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_TOUCHABLE, !touchable);
- }
-
- void setFocusable(bool focusable) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_FOCUSABLE, !focusable);
- }
-
- void setVisible(bool visible) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible);
- }
-
- void setDispatchingTimeout(std::chrono::nanoseconds timeout) {
- mInfo.dispatchingTimeout = timeout;
- }
-
- void setPaused(bool paused) {
- mInfo.setInputConfig(WindowInfo::InputConfig::PAUSE_DISPATCHING, paused);
- }
-
- void setPreventSplitting(bool preventSplitting) {
- mInfo.setInputConfig(WindowInfo::InputConfig::PREVENT_SPLITTING, preventSplitting);
- }
-
- void setSlippery(bool slippery) {
- mInfo.setInputConfig(WindowInfo::InputConfig::SLIPPERY, slippery);
- }
-
- void setWatchOutsideTouch(bool watchOutside) {
- mInfo.setInputConfig(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH, watchOutside);
- }
-
- void setSpy(bool spy) { mInfo.setInputConfig(WindowInfo::InputConfig::SPY, spy); }
-
- void setInterceptsStylus(bool interceptsStylus) {
- mInfo.setInputConfig(WindowInfo::InputConfig::INTERCEPTS_STYLUS, interceptsStylus);
- }
-
- void setDropInput(bool dropInput) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT, dropInput);
- }
-
- void setDropInputIfObscured(bool dropInputIfObscured) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DROP_INPUT_IF_OBSCURED, dropInputIfObscured);
- }
-
- void setNoInputChannel(bool noInputChannel) {
- mInfo.setInputConfig(WindowInfo::InputConfig::NO_INPUT_CHANNEL, noInputChannel);
- }
-
- void setDisableUserActivity(bool disableUserActivity) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DISABLE_USER_ACTIVITY, disableUserActivity);
- }
-
- void setGlobalStylusBlocksTouch(bool shouldGlobalStylusBlockTouch) {
- mInfo.setInputConfig(WindowInfo::InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH,
- shouldGlobalStylusBlockTouch);
- }
-
- void setAlpha(float alpha) { mInfo.alpha = alpha; }
-
- void setTouchOcclusionMode(TouchOcclusionMode mode) { mInfo.touchOcclusionMode = mode; }
-
- void setApplicationToken(sp<IBinder> token) { mInfo.applicationInfo.token = token; }
-
- void setFrame(const Rect& frame, const ui::Transform& displayTransform = ui::Transform()) {
- mInfo.frame = frame;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(frame);
-
- const Rect logicalDisplayFrame = displayTransform.transform(frame);
- ui::Transform translate;
- translate.set(-logicalDisplayFrame.left, -logicalDisplayFrame.top);
- mInfo.transform = translate * displayTransform;
- }
-
- void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
-
- void setIsWallpaper(bool isWallpaper) {
- mInfo.setInputConfig(WindowInfo::InputConfig::IS_WALLPAPER, isWallpaper);
- }
-
- void setDupTouchToWallpaper(bool hasWallpaper) {
- mInfo.setInputConfig(WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER, hasWallpaper);
- }
-
- void setTrustedOverlay(bool trustedOverlay) {
- mInfo.setInputConfig(WindowInfo::InputConfig::TRUSTED_OVERLAY, trustedOverlay);
- }
-
- void setWindowTransform(float dsdx, float dtdx, float dtdy, float dsdy) {
- mInfo.transform.set(dsdx, dtdx, dtdy, dsdy);
- }
-
- void setWindowScale(float xScale, float yScale) { setWindowTransform(xScale, 0, 0, yScale); }
-
- void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); }
-
- std::unique_ptr<KeyEvent> consumeKey(bool handled = true) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled);
- if (event == nullptr) {
- ADD_FAILURE() << "No event";
- return nullptr;
- }
- if (event->getType() != InputEventType::KEY) {
- ADD_FAILURE() << "Instead of key event, got " << event;
- return nullptr;
- }
- return std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event.release()));
- }
-
- void consumeKeyEvent(const ::testing::Matcher<KeyEvent>& matcher) {
- std::unique_ptr<KeyEvent> keyEvent = consumeKey();
- ASSERT_NE(nullptr, keyEvent);
- ASSERT_THAT(*keyEvent, matcher);
- }
-
- void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeKeyEvent(AllOf(WithKeyAction(ACTION_DOWN), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
- }
-
- void consumeKeyUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
- consumeKeyEvent(AllOf(WithKeyAction(ACTION_UP), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
- }
-
- void consumeMotionCancel(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags | AMOTION_EVENT_FLAG_CANCELED)));
- }
-
- void consumeMotionMove(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
- WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
- }
-
- void consumeMotionDown(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- consumeAnyMotionDown(expectedDisplayId, expectedFlags);
- }
-
- void consumeAnyMotionDown(std::optional<int32_t> expectedDisplayId = std::nullopt,
- std::optional<int32_t> expectedFlags = std::nullopt) {
- consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN),
- testing::Conditional(expectedDisplayId.has_value(),
- WithDisplayId(*expectedDisplayId), testing::_),
- testing::Conditional(expectedFlags.has_value(), WithFlags(*expectedFlags),
- testing::_)));
- }
-
- void consumeMotionPointerDown(int32_t pointerIdx,
- int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
- }
-
- void consumeMotionPointerUp(int32_t pointerIdx, int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeMotionEvent(AllOf(WithMotionAction(action), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
- }
-
- void consumeMotionUp(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDisplayId(expectedDisplayId),
- WithFlags(expectedFlags)));
- }
-
- void consumeMotionOutside(int32_t expectedDisplayId = ADISPLAY_ID_DEFAULT,
- int32_t expectedFlags = 0) {
- consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE),
- WithDisplayId(expectedDisplayId), WithFlags(expectedFlags)));
- }
-
- void consumeMotionOutsideWithZeroedCoords() {
- consumeMotionEvent(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_OUTSIDE), WithRawCoords(0, 0)));
- }
-
- void consumeFocusEvent(bool hasFocus, bool inTouchMode = true) {
- ASSERT_NE(mInputReceiver, nullptr)
- << "Cannot consume events from a window with no receiver";
- mInputReceiver->consumeFocusEvent(hasFocus, inTouchMode);
- }
-
- void consumeCaptureEvent(bool hasCapture) {
- ASSERT_NE(mInputReceiver, nullptr)
- << "Cannot consume events from a window with no receiver";
- mInputReceiver->consumeCaptureEvent(hasCapture);
- }
-
- std::unique_ptr<MotionEvent> consumeMotionEvent(
- const ::testing::Matcher<MotionEvent>& matcher = testing::_) {
- std::unique_ptr<InputEvent> event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED);
- if (event == nullptr) {
- ADD_FAILURE() << "No event";
- return nullptr;
- }
- if (event->getType() != InputEventType::MOTION) {
- ADD_FAILURE() << "Instead of motion event, got " << *event;
- return nullptr;
- }
- std::unique_ptr<MotionEvent> motionEvent =
- std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event.release()));
- EXPECT_THAT(*motionEvent, matcher);
- return motionEvent;
- }
-
- void consumeDragEvent(bool isExiting, float x, float y) {
- mInputReceiver->consumeDragEvent(isExiting, x, y);
- }
-
- void consumeTouchModeEvent(bool inTouchMode) {
- ASSERT_NE(mInputReceiver, nullptr)
- << "Cannot consume events from a window with no receiver";
- mInputReceiver->consumeTouchModeEvent(inTouchMode);
- }
-
- std::pair<std::optional<uint32_t>, std::unique_ptr<InputEvent>> receiveEvent() {
- return receive();
- }
-
- void finishEvent(uint32_t sequenceNum) {
- ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
- mInputReceiver->finishEvent(sequenceNum);
- }
-
- void sendTimeline(int32_t inputEventId, std::array<nsecs_t, GraphicsTimeline::SIZE> timeline) {
- ASSERT_NE(mInputReceiver, nullptr) << "Invalid receive event on window with no receiver";
- mInputReceiver->sendTimeline(inputEventId, timeline);
- }
-
- 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(timeout);
- }
-
- sp<IBinder> getToken() { return mInfo.token; }
-
- const std::string& getName() { return mName; }
-
- void setOwnerInfo(gui::Pid ownerPid, gui::Uid ownerUid) {
- mInfo.ownerPid = ownerPid;
- mInfo.ownerUid = ownerUid;
- }
-
- gui::Pid getPid() const { return mInfo.ownerPid; }
-
- void destroyReceiver() { mInputReceiver = nullptr; }
-
- int getChannelFd() { return mInputReceiver->getChannelFd(); }
-
- // FakeWindowHandle uses this consume method to ensure received events are added to the trace.
- std::unique_ptr<InputEvent> consume(std::chrono::milliseconds timeout, bool handled = true) {
- if (mInputReceiver == nullptr) {
- LOG(FATAL) << "Cannot consume event from a window with no input event receiver";
- }
- std::unique_ptr<InputEvent> event = mInputReceiver->consume(timeout, handled);
- if (event == nullptr) {
- ADD_FAILURE() << "Consume failed: no event";
- }
- expectReceivedEventTraced(event);
- return event;
- }
-
-private:
- FakeWindowHandle(std::string name) : mName(name){};
- const std::string mName;
- 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), mInfo.id);
- break;
- }
- case InputEventType::MOTION: {
- gVerifyingTrace->expectMotionDispatchTraced(static_cast<MotionEvent&>(*event),
- mInfo.id);
- break;
- }
- default:
- break;
- }
- }
-};
-
-std::atomic<int32_t> FakeWindowHandle::sId{1};
class FakeMonitorReceiver {
public:
- FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name, int32_t displayId)
+ FakeMonitorReceiver(InputDispatcher& dispatcher, const std::string name,
+ ui::LogicalDisplayId displayId)
: mInputReceiver(*dispatcher.createInputMonitor(displayId, name, MONITOR_PID), name) {}
sp<IBinder> getToken() { return mInputReceiver.getToken(); }
- void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeKeyDown(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver.consumeEvent(InputEventType::KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
}
@@ -1525,22 +457,22 @@
void finishEvent(uint32_t consumeSeq) { return mInputReceiver.finishEvent(consumeSeq); }
- void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionDown(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_DOWN,
expectedDisplayId, expectedFlags);
}
- void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionMove(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_MOVE,
expectedDisplayId, expectedFlags);
}
- void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionUp(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver.consumeEvent(InputEventType::MOTION, AMOTION_EVENT_ACTION_UP,
expectedDisplayId, expectedFlags);
}
- void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ void consumeMotionCancel(ui::LogicalDisplayId expectedDisplayId, int32_t expectedFlags = 0) {
mInputReceiver.consumeMotionEvent(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_CANCEL),
WithDisplayId(expectedDisplayId),
@@ -1568,7 +500,7 @@
static InputEventInjectionResult injectKey(
InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE,
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
bool allowKeyRepeat = true, std::optional<gui::Uid> targetUid = {},
@@ -1590,30 +522,30 @@
static void assertInjectedKeyTimesOut(InputDispatcher& dispatcher) {
InputEventInjectionResult result =
- injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ui::ADISPLAY_ID_NONE,
InputEventInjectionSync::WAIT_FOR_RESULT, CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
if (result != InputEventInjectionResult::TIMED_OUT) {
FAIL() << "Injection should have timed out, but got " << ftl::enum_string(result);
}
}
-static InputEventInjectionResult injectKeyDown(InputDispatcher& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDown(
+ InputDispatcher& dispatcher, ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId);
}
// Inject a down event that has key repeat disabled. This allows InputDispatcher to idle without
// sending a subsequent key up. When key repeat is enabled, the dispatcher cannot idle because it
// has to be woken up to process the repeating key.
-static InputEventInjectionResult injectKeyDownNoRepeat(InputDispatcher& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyDownNoRepeat(
+ InputDispatcher& dispatcher, ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, displayId,
InputEventInjectionSync::WAIT_FOR_RESULT, INJECT_EVENT_TIMEOUT,
/*allowKeyRepeat=*/false);
}
-static InputEventInjectionResult injectKeyUp(InputDispatcher& dispatcher,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static InputEventInjectionResult injectKeyUp(
+ InputDispatcher& dispatcher, ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, displayId);
}
@@ -1627,7 +559,7 @@
}
static InputEventInjectionResult injectMotionEvent(
- InputDispatcher& dispatcher, int32_t action, int32_t source, int32_t displayId,
+ InputDispatcher& dispatcher, int32_t action, int32_t source, ui::LogicalDisplayId displayId,
const PointF& position = {100, 200},
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
@@ -1653,18 +585,19 @@
}
static InputEventInjectionResult injectMotionDown(InputDispatcher& dispatcher, int32_t source,
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, location);
}
static InputEventInjectionResult injectMotionUp(InputDispatcher& dispatcher, int32_t source,
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const PointF& location = {100, 200}) {
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, location);
}
-static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
+static NotifyKeyArgs generateKeyArgs(int32_t action,
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
@@ -1674,8 +607,8 @@
return args;
}
-static NotifyKeyArgs generateSystemShortcutArgs(int32_t action,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static NotifyKeyArgs generateSystemShortcutArgs(
+ int32_t action, ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
@@ -1685,8 +618,8 @@
return args;
}
-static NotifyKeyArgs generateAssistantKeyArgs(int32_t action,
- int32_t displayId = ADISPLAY_ID_NONE) {
+static NotifyKeyArgs generateAssistantKeyArgs(
+ int32_t action, ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
@@ -1697,7 +630,7 @@
}
[[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
- int32_t displayId,
+ ui::LogicalDisplayId displayId,
const std::vector<PointF>& points) {
size_t pointerCount = points.size();
if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
@@ -1734,7 +667,8 @@
return generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, points);
}
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source,
+ ui::LogicalDisplayId displayId) {
return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
}
@@ -1803,7 +737,7 @@
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Inject a MotionEvent to an unknown display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
@@ -1897,7 +831,7 @@
// Both foreground window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
@@ -1907,13 +841,13 @@
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
- wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Now the foreground window goes away, but the wallpaper stays
mDispatcher->onWindowInfosChanged({{*wallpaperWindow->getInfo()}, {}, 0, 0});
foregroundWindow->consumeMotionCancel();
// Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
- wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -1978,7 +912,7 @@
// Both foreground window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
@@ -1986,7 +920,7 @@
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionMove();
- wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Wallpaper closes its channel, but the window remains.
wallpaperWindow->destroyReceiver();
@@ -1998,6 +932,301 @@
foregroundWindow->consumeMotionCancel();
}
+/**
+ * Two windows: left and right, and a separate wallpaper window underneath each. Device A sends a
+ * down event to the left window. Device B sends a down event to the right window. Next, the right
+ * window disappears. Both the right window and its wallpaper window should receive cancel event.
+ * The left window and its wallpaper window should not receive any events.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceDisappearingWindowWithWallpaperWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left foreground window",
+ ADISPLAY_ID_DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ADISPLAY_ID_DEFAULT);
+ rightForegroundWindow->setFrame(Rect(100, 0, 200, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ rightWallpaperWindow->setFrame(Rect(100, 0, 200, 100));
+ rightWallpaperWindow->setIsWallpaper(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftForegroundWindow->getInfo(), *leftWallpaperWindow->getInfo(),
+ *rightForegroundWindow->getInfo(), *rightWallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+ leftWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceA),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ rightForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+ rightWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+
+ // Now right foreground window disappears, but right wallpaper window remains.
+ mDispatcher->onWindowInfosChanged(
+ {{*leftForegroundWindow->getInfo(), *leftWallpaperWindow->getInfo(),
+ *rightWallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ // Left foreground window and left wallpaper window still exist, and should not receive any
+ // events.
+ leftForegroundWindow->assertNoEvents();
+ leftWallpaperWindow->assertNoEvents();
+ // Since right foreground window disappeared, right wallpaper window and right foreground window
+ // should receive cancel events.
+ rightForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
+ rightWallpaperWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_CANCELED)));
+}
+
+/**
+ * Three windows arranged horizontally and without any overlap. Every window has a
+ * wallpaper window underneath. The middle window also has SLIPPERY flag.
+ * Device A sends a down event to the left window. Device B sends a down event to the middle window.
+ * Next, device B sends move event to the right window. Touch for device B should slip from the
+ * middle window to the right window. Also, the right wallpaper window should receive a down event.
+ * The middle window and its wallpaper window should receive a cancel event. The left window should
+ * not receive any events. If device B continues to report events, the right window and its
+ * wallpaper window should receive remaining events.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSlipperyTouchWithWallpaperWindow) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left foreground window",
+ ADISPLAY_ID_DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> middleForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle foreground window",
+ ADISPLAY_ID_DEFAULT);
+ middleForegroundWindow->setFrame(Rect(100, 0, 200, 100));
+ middleForegroundWindow->setDupTouchToWallpaper(true);
+ middleForegroundWindow->setSlippery(true);
+ sp<FakeWindowHandle> middleWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ middleWallpaperWindow->setFrame(Rect(100, 0, 200, 100));
+ middleWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ADISPLAY_ID_DEFAULT);
+ rightForegroundWindow->setFrame(Rect(200, 0, 300, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ rightWallpaperWindow->setFrame(Rect(200, 0, 300, 100));
+ rightWallpaperWindow->setIsWallpaper(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftForegroundWindow->getInfo(), *leftWallpaperWindow->getInfo(),
+ *middleForegroundWindow->getInfo(), *middleWallpaperWindow->getInfo(),
+ *rightForegroundWindow->getInfo(), *rightWallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+ // Device A sends a DOWN event to the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+ leftWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceA),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+ // Device B sends a DOWN event to the middle window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ middleForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+ middleWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+ // Move the events of device B to the top of the right window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(50))
+ .deviceId(deviceB)
+ .build());
+ middleForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
+ middleWallpaperWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_CANCELED)));
+ rightForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+ rightWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+ // Make sure the window on the right can receive the remaining events.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(251).y(51))
+ .deviceId(deviceB)
+ .build());
+ leftForegroundWindow->assertNoEvents();
+ leftWallpaperWindow->assertNoEvents();
+ middleForegroundWindow->assertNoEvents();
+ middleWallpaperWindow->assertNoEvents();
+ rightForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+ rightWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+}
+
+/**
+ * Similar to the test above, we have three windows, they are arranged horizontally and without any
+ * overlap, and every window has a wallpaper window. The middle window is a simple window, without
+ * any special flags. Device A reports a down event that lands in left window. Device B sends a down
+ * event to the middle window and then touch is transferred from the middle window to the right
+ * window. The right window and its wallpaper window should receive a down event. The middle window
+ * and its wallpaper window should receive a cancel event. The left window should not receive any
+ * events. Subsequent events reported by device B should go to the right window and its wallpaper.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left foreground window",
+ ADISPLAY_ID_DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> middleForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle foreground window",
+ ADISPLAY_ID_DEFAULT);
+ middleForegroundWindow->setFrame(Rect(100, 0, 200, 100));
+ middleForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> middleWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ middleWallpaperWindow->setFrame(Rect(100, 0, 200, 100));
+ middleWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ADISPLAY_ID_DEFAULT);
+ rightForegroundWindow->setFrame(Rect(200, 0, 300, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ADISPLAY_ID_DEFAULT);
+ rightWallpaperWindow->setFrame(Rect(200, 0, 300, 100));
+ rightWallpaperWindow->setIsWallpaper(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftForegroundWindow->getInfo(), *leftWallpaperWindow->getInfo(),
+ *middleForegroundWindow->getInfo(), *middleWallpaperWindow->getInfo(),
+ *rightForegroundWindow->getInfo(), *rightWallpaperWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+ // Device A touch down on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+ leftWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceA),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+ // Device B touch down on the middle window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ middleForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+ middleWallpaperWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
+
+ // Transfer touch from the middle window to the right window.
+ ASSERT_TRUE(mDispatcher->transferTouchGesture(middleForegroundWindow->getToken(),
+ rightForegroundWindow->getToken()));
+
+ middleForegroundWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
+ middleWallpaperWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_CANCELED)));
+ rightForegroundWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN),
+ WithDeviceId(deviceB),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ rightWallpaperWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+
+ // Make sure the right window can receive the remaining events.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(251).y(51))
+ .deviceId(deviceB)
+ .build());
+ leftForegroundWindow->assertNoEvents();
+ leftWallpaperWindow->assertNoEvents();
+ middleForegroundWindow->assertNoEvents();
+ middleWallpaperWindow->assertNoEvents();
+ rightForegroundWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE),
+ WithDeviceId(deviceB),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+ rightWallpaperWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE)));
+}
+
class ShouldSplitTouchFixture : public InputDispatcherTest,
public ::testing::WithParamInterface<bool> {};
INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture,
@@ -2029,7 +1258,7 @@
// Both top window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Second finger down on the top window
const MotionEvent secondFingerDownEvent =
@@ -2042,10 +1271,9 @@
injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
-
foregroundWindow->consumeMotionPointerDown(/*pointerIndex=*/1);
wallpaperWindow->consumeMotionPointerDown(/*pointerIndex=*/1, ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags);
+ EXPECTED_WALLPAPER_FLAGS);
const MotionEvent secondFingerUpEvent =
MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
@@ -2059,7 +1287,7 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionPointerUp(0);
- wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
@@ -2074,7 +1302,7 @@
INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -2116,7 +1344,7 @@
// Both foreground window and its wallpaper should receive the touch down
leftWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Second finger down on the right window
const MotionEvent secondFingerDownEvent =
@@ -2134,14 +1362,14 @@
// Since the touch is split, right window gets ACTION_DOWN
rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
wallpaperWindow->consumeMotionPointerDown(/*pointerIndex=*/1, ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags);
+ EXPECTED_WALLPAPER_FLAGS);
// Now, leftWindow, which received the first finger, disappears.
mDispatcher->onWindowInfosChanged(
{{*rightWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
leftWindow->consumeMotionCancel();
// Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too.
- wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// The pointer that's still down on the right window moves, and goes to the right window only.
// As far as the dispatcher's concerned though, both pointers are still present.
@@ -2196,7 +1424,7 @@
// Both foreground window and its wallpaper should receive the touch down
leftWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Move to right window, the left window should receive cancel.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -2206,7 +1434,7 @@
leftWindow->consumeMotionCancel();
rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -2363,7 +1591,9 @@
* This test reproduces a crash where there is a mismatch between the downTime and eventTime.
* In the buggy implementation, a tap on the right window would cause a crash.
*/
-TEST_F(InputDispatcherTest, HoverFromLeftToRightAndTap) {
+TEST_F(InputDispatcherTest, HoverFromLeftToRightAndTap_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
+
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2459,6 +1689,99 @@
}
/**
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is hovered from the right window into the left window.
+ * Next, we tap on the left window, where the cursor was last seen.
+ * The second tap is done onto the right window.
+ * The mouse and tap are from two different devices.
+ * We technically don't need to set the downtime / eventtime for these events, but setting these
+ * explicitly helps during debugging.
+ * This test reproduces a crash where there is a mismatch between the downTime and eventTime.
+ * In the buggy implementation, a tap on the right window would cause a crash.
+ */
+TEST_F(InputDispatcherTest, HoverFromLeftToRightAndTap) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+ // All times need to start at the current time, otherwise the dispatcher will drop the events as
+ // stale.
+ const nsecs_t baseTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const int32_t mouseDeviceId = 6;
+ const int32_t touchDeviceId = 4;
+ // Move the cursor from right
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 20)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(100))
+ .build());
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+
+ // .. to the left window
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .downTime(baseTime + 10)
+ .eventTime(baseTime + 30)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(100))
+ .build());
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT));
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+ // Now tap the left window
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 40)
+ .eventTime(baseTime + 40)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ // release tap
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 40)
+ .eventTime(baseTime + 50)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
+
+ // Tap the window on the right
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 60)
+ .eventTime(baseTime + 60)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ // release tap
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .downTime(baseTime + 60)
+ .eventTime(baseTime + 70)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_UP));
+
+ // No more events
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* Start hovering in a window. While this hover is still active, make another window appear on top.
* The top, obstructing window has no input channel, so it's not supposed to receive input.
* While the top window is present, the hovering is stopped.
@@ -2606,6 +1929,7 @@
* touch is dropped, because stylus should be preferred over touch.
*/
TEST_F(InputDispatcherMultiDeviceTest, StylusDownBlocksTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2648,11 +1972,60 @@
}
/**
+ * One window. Stylus down on the window. Next, touch from another device goes down. Ensure that
+ * touch is not dropped, because multiple devices are allowed to be active in the same window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownDoesNotBlockTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+
+ window->assertNoEvents();
+}
+
+/**
* One window and one spy window. Stylus down on the window. Next, touch from another device goes
* down. Ensure that touch is dropped, because stylus should be preferred over touch.
* Similar test as above, but with added SPY window.
*/
TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyBlocksTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2706,10 +2079,74 @@
}
/**
+ * One window and one spy window. Stylus down on the window. Next, touch from another device goes
+ * down. Ensure that touch is not dropped, because multiple devices can be active at the same time.
+ * Similar test as above, but with added SPY window.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusDownWithSpyDoesNotBlockTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Touch move
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Subsequent stylus movements are delivered correctly
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
+}
+
+/**
* One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
* touch is dropped, because stylus hover takes precedence.
*/
TEST_F(InputDispatcherMultiDeviceTest, StylusHoverBlocksTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2757,10 +2194,65 @@
}
/**
+ * One window. Stylus hover on the window. Next, touch from another device goes down. Ensure that
+ * touch is not dropped, because stylus hover and touch can be both active at the same time.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverDoesNotBlockTouchDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ // Touch move on window
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Subsequent stylus movements are delivered correctly
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
+ WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
+
+ // and subsequent touches continue to work
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ window->assertNoEvents();
+}
+
+/**
* One window. Touch down on the window. Then, stylus hover on the window from another device.
* Ensure that touch is canceled, because stylus hover should take precedence.
*/
TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusHover) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2810,11 +2302,66 @@
}
/**
+ * One window. Touch down on the window. Then, stylus hover on the window from another device.
+ * Ensure that touch is not canceled, because stylus hover can be active at the same time as touch.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusHover) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus hover on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ // Stylus hover movement is received normally
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
+ WithDeviceId(stylusDeviceId), WithCoords(100, 110)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE),
+ WithDeviceId(stylusDeviceId), WithCoords(101, 111)));
+
+ // Subsequent touch movements also work
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(142).y(147))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId),
+ WithCoords(142, 147)));
+
+ window->assertNoEvents();
+}
+
+/**
* One window. Stylus down on the window. Then, stylus from another device goes down. Ensure that
* the latest stylus takes over. That is, old stylus should be canceled and the new stylus should
* become active.
*/
TEST_F(InputDispatcherMultiDeviceTest, LatestStylusWins) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2862,10 +2409,59 @@
}
/**
+ * One window. Stylus down on the window. Then, stylus from another device goes down. Ensure that
+ * both stylus devices can function simultaneously.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TwoStylusDevicesActiveAtTheSameTime) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t stylusDeviceId1 = 3;
+ constexpr int32_t stylusDeviceId2 = 5;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(99).y(100))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId1)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId1)));
+
+ // Second stylus down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(9).y(10))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId2)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(10).y(11))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId2)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId2)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId1)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId1)));
+ window->assertNoEvents();
+}
+
+/**
* One window. Touch down on the window. Then, stylus down on the window from another device.
* Ensure that is canceled, because stylus down should be preferred over touch.
*/
TEST_F(InputDispatcherMultiDeviceTest, TouchIsCanceledByStylusDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -2906,13 +2502,65 @@
}
/**
+ * One window. Touch down on the window. Then, stylus down on the window from another device.
+ * Ensure that both touch and stylus are functioning independently.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, TouchIsNotCanceledByStylusDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ constexpr int32_t touchDeviceId = 4;
+ constexpr int32_t stylusDeviceId = 2;
+
+ // Touch down on window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(140).y(145))
+ .build());
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(141).y(146))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // Stylus down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Subsequent stylus movements are delivered correctly
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(111))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId),
+ WithCoords(101, 111)));
+
+ // Touch continues to work too
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(148).y(149))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+}
+
+/**
* Two windows: a window on the left and a window on the right.
* Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
* down. Then, on the left window, also place second touch pointer down.
* This test tries to reproduce a crash.
* In the buggy implementation, second pointer down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceSplitTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -2991,6 +2639,88 @@
/**
* Two windows: a window on the left and a window on the right.
+ * Mouse is clicked on the left window and remains down. Touch is touched on the right and remains
+ * down. Then, on the left window, also place second touch pointer down.
+ * This test tries to reproduce a crash.
+ * In the buggy implementation, second pointer down on the left window would cause a crash.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceSplitTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+
+ // Start hovering over the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Mouse down on left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // First touch pointer down on right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->assertNoEvents();
+
+ rightWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ // Second touch pointer down on left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
+ .build());
+ // Since this is now a new splittable pointer going down on the left window, and it's coming
+ // from a different device, it will be split and delivered to left window separately.
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ // This MOVE event is not necessary (doesn't carry any new information), but it's there in the
+ // current implementation.
+ const std::map<int32_t, PointF> expectedPointers{{0, PointF{100, 100}}};
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers)));
+
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
+ * Two windows: a window on the left and a window on the right.
* Mouse is hovered on the left window and stylus is hovered on the right window.
*/
TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHover) {
@@ -3050,7 +2780,8 @@
* Stylus down on the left window and remains down. Touch goes down on the right and remains down.
* Check the stream that's received by the spy.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) {
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -3120,6 +2851,83 @@
}
/**
+ * Three windows: a window on the left and a window on the right.
+ * And a spy window that's positioned above all of them.
+ * Stylus down on the left window and remains down. Touch goes down on the right and remains down.
+ * Check the stream that's received by the spy.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceWithSpy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus down on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue. They should be delivered to the left window and to the spy window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+
+ // Further touch MOVE events keep going to the right window and to the spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(310).y(110))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* Three windows: a window on the left, a window on the right, and a spy window positioned above
* both.
* Check hover in left window and touch down in the right window.
@@ -3128,6 +2936,7 @@
* respectively.
*/
TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverBlocksTouchWithSpy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -3195,6 +3004,84 @@
}
/**
+ * Three windows: a window on the left, a window on the right, and a spy window positioned above
+ * both.
+ * Check hover in left window and touch down in the right window.
+ * At first, spy should receive hover. Next, spy should receive touch.
+ * At the same time, left and right should be getting independent streams of hovering and touch,
+ * respectively.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MultiDeviceHoverDoesNotBlockTouchWithSpy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spyWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 1;
+ const int32_t touchDeviceId = 2;
+
+ // Stylus hover on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(stylusDeviceId)));
+
+ // Touch down on the right window.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ leftWindow->assertNoEvents();
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Stylus movements continue. They should be delivered to the left window and the spy.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(110).y(110))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+
+ // Touch movements continue. They should be delivered to the right window and the spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(301).y(101))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spyWindow->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* On a single window, use two different devices: mouse and touch.
* Touch happens first, with two pointers going down, and then the first pointer leaving.
* Mouse is clicked next, which causes the touch stream to be aborted with ACTION_CANCEL.
@@ -3202,7 +3089,8 @@
* because the mouse is currently down, and a POINTER_DOWN event from the touchscreen does not
* represent a new gesture.
*/
-TEST_F(InputDispatcherMultiDeviceTest, MixedTouchAndMouseWithPointerDown) {
+TEST_F(InputDispatcherMultiDeviceTest, MixedTouchAndMouseWithPointerDown_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3275,10 +3163,89 @@
}
/**
+ * On a single window, use two different devices: mouse and touch.
+ * Touch happens first, with two pointers going down, and then the first pointer leaving.
+ * Mouse is clicked next, which should not interfere with the touch stream.
+ * Finally, a second touch pointer goes down again. Ensure the second touch pointer is also
+ * delivered correctly.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, MixedTouchAndMouseWithPointerDown) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+
+ // First touch pointer down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ // Second touch pointer down
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+ .build());
+ // First touch pointer lifts. The second one remains down
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_0_UP));
+
+ // Mouse down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(320).y(100))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Second touch pointer down.
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_0_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(350).y(100))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_0_DOWN), WithDeviceId(touchDeviceId),
+ WithPointerCount(2u)));
+
+ // Mouse movements should continue to work
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(330).y(110))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(mouseDeviceId)));
+
+ window->assertNoEvents();
+}
+
+/**
* Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event cancels
* the injected event.
*/
-TEST_F(InputDispatcherMultiDeviceTest, UnfinishedInjectedEvent) {
+TEST_F(InputDispatcherMultiDeviceTest, UnfinishedInjectedEvent_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3311,6 +3278,40 @@
}
/**
+ * Inject a touch down and then send a new event via 'notifyMotion'. Ensure the new event runs
+ * parallel to the injected event.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, UnfinishedInjectedEvent) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 400, 400));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const int32_t touchDeviceId = 4;
+ // Pretend a test injects an ACTION_DOWN mouse event, but forgets to lift up the touch after
+ // completion.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher,
+ MotionEventBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
+ .build()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(VIRTUAL_KEYBOARD_ID)));
+
+ // Now a real touch comes. The injected pointer will remain, and the new gesture will also be
+ // allowed through.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* This test is similar to the test above, but the sequence of injected events is different.
*
* Two windows: a window on the left and a window on the right.
@@ -3324,7 +3325,8 @@
* This test reproduces a crash where there is a mismatch between the downTime and eventTime.
* In the buggy implementation, second finger down on the left window would cause a crash.
*/
-TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) {
+TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
@@ -3396,11 +3398,88 @@
}
/**
+ * This test is similar to the test above, but the sequence of injected events is different.
+ *
+ * Two windows: a window on the left and a window on the right.
+ * Mouse is hovered over the left window.
+ * Next, we tap on the left window, where the cursor was last seen.
+ *
+ * After that, we send one finger down onto the right window, and then a second finger down onto
+ * the left window.
+ * The touch is split, so this last gesture should cause 2 ACTION_DOWN events, one in the right
+ * window (first), and then another on the left window (second).
+ * This test reproduces a crash where there is a mismatch between the downTime and eventTime.
+ * In the buggy implementation, second finger down on the left window would cause a crash.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, HoverTapAndSplitTouch) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 400, 200));
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ const int32_t mouseDeviceId = 6;
+ const int32_t touchDeviceId = 4;
+ // Hover over the left window. Keep the cursor there.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
+
+ // Tap on left window
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithDeviceId(touchDeviceId)));
+
+ // First finger down on right window
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .build());
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+
+ // Second finger down on the left window
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(300).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(100).y(100))
+ .build());
+ leftWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_DOWN));
+ rightWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_MOVE));
+
+ // No more events
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
+/**
* Start hovering with a stylus device, and then tap with a touch device. Ensure no crash occurs.
* While the touch is down, new hover events from the stylus device should be ignored. After the
* touch is gone, stylus hovering should start working again.
*/
TEST_F(InputDispatcherMultiDeviceTest, StylusHoverIgnoresTouchTap) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -3462,6 +3541,61 @@
}
/**
+ * Start hovering with a stylus device, and then tap with a touch device. Ensure no crash occurs.
+ * While the touch is down, hovering from the stylus is not affected. After the touch is gone,
+ * check that the stylus hovering continues to work.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, StylusHoverWithTouchTap) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const int32_t stylusDeviceId = 5;
+ const int32_t touchDeviceId = 4;
+ // Start hovering with stylus
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // Finger down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Continue hovering with stylus.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(60).y(60))
+ .build());
+ // Hovers continue to work
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+
+ // Lift up the finger
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_UP), WithDeviceId(touchDeviceId)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(70).y(70))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithDeviceId(stylusDeviceId)));
+ window->assertNoEvents();
+}
+
+/**
* If stylus is down anywhere on the screen, then touches should not be delivered to windows that
* have InputConfig::GLOBAL_STYLUS_BLOCKS_TOUCH.
*
@@ -3707,7 +3841,8 @@
* ACTION_DOWN event because that's a new gesture, and pilfering should no longer be active.
* While the mouse is down, new move events from the touch device should be ignored.
*/
-TEST_F(InputDispatcherTest, TouchPilferAndMouseMove) {
+TEST_F(InputDispatcherTest, TouchPilferAndMouseMove_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
@@ -3804,6 +3939,114 @@
}
/**
+ * Start hovering with a mouse, and then tap with a touch device. Pilfer the touch stream.
+ * Next, click with the mouse device. Both windows (spy and regular) should receive the new mouse
+ * ACTION_DOWN event because that's a new gesture, and pilfering should no longer be active.
+ * While the mouse is down, new move events from the touch device should continue to work.
+ */
+TEST_F(InputDispatcherTest, TouchPilferAndMouseMove) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 200, 200));
+
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ const int32_t mouseDeviceId = 7;
+ const int32_t touchDeviceId = 4;
+
+ // Hover a bit with mouse first
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Start touching
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(55).y(55))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Pilfer the stream
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindow->getToken()));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+ // Hover is not pilfered! Only touch.
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(60).y(60))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+
+ // Mouse down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+ spyWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Mouse move!
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(mouseDeviceId)));
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(mouseDeviceId)));
+
+ // Touch move!
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(65).y(65))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ // No more events
+ spyWindow->assertNoEvents();
+ window->assertNoEvents();
+}
+
+/**
* On the display, have a single window, and also an area where there's no window.
* First pointer touches the "no window" area of the screen. Second pointer touches the window.
* Make sure that the window receives the second pointer, and first pointer is simply ignored.
@@ -4016,7 +4259,8 @@
* The clicking of mouse is a new ACTION_DOWN event. Since it's from a different device, the
* currently active gesture should be canceled, and the new one should proceed.
*/
-TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) {
+TEST_F(InputDispatcherTest, TwoPointersDownMouseClick_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -4070,6 +4314,65 @@
window->assertNoEvents();
}
+/**
+ * Put two fingers down (and don't release them) and click the mouse button.
+ * The clicking of mouse is a new ACTION_DOWN event. Since it's from a different device, the
+ * currently active gesture should not be canceled, and the new one should proceed in parallel.
+ */
+TEST_F(InputDispatcherTest, TwoPointersDownMouseClick) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const int32_t touchDeviceId = 4;
+ const int32_t mouseDeviceId = 6;
+
+ // Two pointers down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .build());
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(100))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(120).y(120))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ window->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
+
+ // Send a series of mouse events for a mouse click
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(mouseDeviceId)));
+
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .buttonState(AMOTION_EVENT_BUTTON_PRIMARY)
+ .actionButton(AMOTION_EVENT_BUTTON_PRIMARY)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
+ .build());
+ window->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS));
+
+ // Try to send more touch events while the mouse is down. Since it's a continuation of an
+ // already active gesture, it should be sent normally.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(101).y(101))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(121).y(121))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+ window->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, HoverWithSpyWindows) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -4102,7 +4405,8 @@
spyWindow->assertNoEvents();
}
-TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
+TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindow =
@@ -4208,6 +4512,102 @@
spyWindow->assertNoEvents();
}
+TEST_F(InputDispatcherTest, MouseAndTouchWithSpyWindows) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Send mouse cursor to the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(100).y(100))
+ .build());
+
+ // Move mouse cursor
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(110).y(110))
+ .build());
+
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithSource(AINPUT_SOURCE_MOUSE)));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+ // Touch down on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+
+ // pilfer the motion, retaining the gesture on the spy window.
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spyWindow->getToken()));
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+ // Mouse hover is not pilfered
+
+ // Touch UP on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
+ .build());
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+
+ // Previously, a touch was pilfered. However, that gesture was just finished. Now, we are going
+ // to send a new gesture. It should again go to both windows (spy and the window below), just
+ // like the first gesture did, before pilfering. The window configuration has not changed.
+
+ // One more tap - DOWN
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+
+ // Touch UP on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(250))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_UP), WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+
+ // Mouse movement continues normally as well
+ // Move mouse cursor
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(120).y(130))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+ spyWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->assertNoEvents();
+ spyWindow->assertNoEvents();
+}
+
// This test is different from the test above that HOVER_ENTER and HOVER_EXIT events are injected
// directly in this test.
TEST_F(InputDispatcherTest, HoverEnterMouseClickAndHoverExit) {
@@ -4333,7 +4733,8 @@
/**
* If mouse is hovering when the touch goes down, the hovering should be stopped via HOVER_EXIT.
*/
-TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
+TEST_F(InputDispatcherTest, TouchDownAfterMouseHover_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -4364,11 +4765,43 @@
}
/**
+ * If mouse is hovering when the touch goes down, the hovering should not be stopped.
+ */
+TEST_F(InputDispatcherTest, TouchDownAfterMouseHover) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const int32_t mouseDeviceId = 7;
+ const int32_t touchDeviceId = 4;
+
+ // Start hovering with the mouse
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .deviceId(mouseDeviceId)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(10).y(10))
+ .build());
+ window->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_HOVER_ENTER), WithDeviceId(mouseDeviceId)));
+
+ // Touch goes down
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+}
+
+/**
* Inject a mouse hover event followed by a tap from touchscreen.
* The tap causes a HOVER_EXIT event to be generated because the current event
* stream's source has been switched.
*/
-TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
@@ -4402,6 +4835,45 @@
WithSource(AINPUT_SOURCE_TOUCHSCREEN))));
}
+/**
+ * Send a mouse hover event followed by a tap from touchscreen.
+ * The tap causes a HOVER_EXIT event to be generated because the current event
+ * stream's source has been switched.
+ */
+TEST_F(InputDispatcherTest, MouseHoverAndTouchTap) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(0, ToolType::MOUSE).x(50).y(50))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ // Tap on the window
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT),
+ WithSource(AINPUT_SOURCE_MOUSE)));
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(10).y(10))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN)));
+}
+
TEST_F(InputDispatcherTest, HoverEnterMoveRemoveWindowsInSecondDisplay) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> windowDefaultDisplay =
@@ -4626,6 +5098,96 @@
}
/**
+ * Three windows:
+ * - Left window
+ * - Right window
+ * - Outside window(watch for ACTION_OUTSIDE events)
+ * The windows "left" and "outside" share the same owner, the window "right" has a different owner,
+ * In order to allow the outside window can receive the ACTION_OUTSIDE events, the outside window is
+ * positioned above the "left" and "right" windows, and it doesn't overlap with them.
+ *
+ * First, device A report a down event landed in the right window, the outside window can receive
+ * an ACTION_OUTSIDE event that with zeroed coordinates, the device B report a down event landed
+ * in the left window, the outside window can receive an ACTION_OUTSIDE event the with valid
+ * coordinates, after these, device A and device B continue report MOVE event, the right and left
+ * window can receive it, but outside window event can't receive it.
+ */
+TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinatesWhenMultiDevice) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect{0, 0, 100, 100});
+ leftWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
+
+ sp<FakeWindowHandle> outsideWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Outside Window",
+ ADISPLAY_ID_DEFAULT);
+ outsideWindow->setFrame(Rect{100, 100, 200, 200});
+ outsideWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
+ outsideWindow->setWatchOutsideTouch(true);
+
+ std::shared_ptr<FakeApplicationHandle> anotherApplication =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Right Window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect{100, 0, 200, 100});
+ rightWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202});
+
+ // OutsideWindow must be above left window and right window to receive ACTION_OUTSIDE events
+ // when left window or right window is tapped
+ mDispatcher->onWindowInfosChanged(
+ {{*outsideWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ // Tap on right window use device A
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+ // Right window is belonged to another owner, so outsideWindow should receive ACTION_OUTSIDE
+ // with zeroed coords.
+ outsideWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithDeviceId(deviceA), WithCoords(0, 0)));
+
+ // Tap on left window use device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+ rightWindow->assertNoEvents();
+ // Because new gesture down on the left window that has the same owner with outside Window, the
+ // outside Window should receive the ACTION_OUTSIDE with coords.
+ outsideWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_OUTSIDE), WithDeviceId(deviceB), WithCoords(-50, -50)));
+
+ // Ensure that windows that can only accept outside do not receive remaining gestures
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(51).y(51))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+ rightWindow->assertNoEvents();
+ outsideWindow->assertNoEvents();
+}
+
+/**
* This test documents the behavior of WATCH_OUTSIDE_TOUCH. The window will get ACTION_OUTSIDE when
* a another pointer causes ACTION_DOWN to be sent to another window for the first time. Only one
* ACTION_OUTSIDE event is sent per gesture.
@@ -5008,8 +5570,8 @@
ASSERT_NE(nullptr, event);
EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, event->getAction());
- // The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so the
- // coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y
+ // The cancel event is sent to windowDefaultDisplay of the ADISPLAY_ID_DEFAULT display, so
+ // the coordinates of the cancel are converted by windowDefaultDisplay's transform, the x and y
// coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of
// SECOND_DISPLAY_ID, the x and y coordinates are 200
EXPECT_EQ(100, event->getX(0));
@@ -5030,7 +5592,7 @@
removeAllWindowsAndDisplays();
}
- void addDisplayInfo(int displayId, const ui::Transform& transform) {
+ void addDisplayInfo(ui::LogicalDisplayId displayId, const ui::Transform& transform) {
gui::DisplayInfo info;
info.displayId = displayId;
info.transform = transform;
@@ -5499,7 +6061,7 @@
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Dispatcher reports pointer down outside focus for the wallpaper
mFakePolicy->assertOnPointerDownEquals(wallpaper->getToken());
@@ -5510,7 +6072,7 @@
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
- wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// There should not be any changes to the focused window when transferring touch
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertOnPointerDownWasNotCalled());
@@ -5671,7 +6233,7 @@
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
wallpaper2->assertNoEvents();
// Transfer touch focus to the second window
@@ -5682,9 +6244,9 @@
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
- wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, EXPECTED_WALLPAPER_FLAGS);
wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
@@ -5694,7 +6256,7 @@
secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
wallpaper1->assertNoEvents();
wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ EXPECTED_WALLPAPER_FLAGS | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
// For the cases of single pointer touch and two pointers non-split touch, the api's
@@ -6719,7 +7281,7 @@
verifiedEvent.eventTimeNanos += 1;
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
- verifiedEvent.displayId += 1;
+ verifiedEvent.displayId = ui::LogicalDisplayId{verifiedEvent.displayId.val() + 1};
ASSERT_NE(initialHmac, mDispatcher->sign(verifiedEvent));
verifiedEvent.action += 1;
@@ -6764,7 +7326,7 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ui::ADISPLAY_ID_NONE);
windowTop->assertNoEvents();
}
@@ -6829,7 +7391,7 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ui::ADISPLAY_ID_NONE);
}
TEST_F(InputDispatcherTest, SetFocusedWindow_TransferFocusTokenNotFocusable) {
@@ -6852,7 +7414,7 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Event should be dropped.
- windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowTop->consumeKeyDown(ui::ADISPLAY_ID_NONE);
windowSecond->assertNoEvents();
}
@@ -7064,6 +7626,154 @@
appearingWindow->assertNoEvents();
}
+/**
+ * Three windows:
+ * - left window, which has FLAG_SLIPPERY, so it supports slippery exit
+ * - right window
+ * - spy window
+ * The three windows do not overlap.
+ *
+ * We have two devices reporting events:
+ * - Device A reports ACTION_DOWN, which lands in the left window
+ * - Device B reports ACTION_DOWN, which lands in the spy window.
+ * - Now, device B reports ACTION_MOVE events which move to the right window.
+ *
+ * The right window should not receive any events because the spy window is not a foreground window,
+ * and also it does not support slippery touches.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSpyWindowSlipTest) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(200, 0, 300, 100));
+ spyWindow->setSpy(true);
+ spyWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *spyWindow->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ // Tap on left window with device A
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Tap on spy window with device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(50))
+ .deviceId(deviceB)
+ .build());
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to right window with device B. Touches should not slip to the right window, because spy
+ // window is not a foreground window, and it does not have FLAG_SLIPPERY
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+}
+
+/**
+ * Three windows arranged horizontally and without any overlap.
+ * The left and right windows have FLAG_SLIPPERY. The middle window does not have any special flags.
+ *
+ * We have two devices reporting events:
+ * - Device A reports ACTION_DOWN which lands in the left window
+ * - Device B reports ACTION_DOWN which lands in the right window
+ * - Device B reports ACTION_MOVE that shifts to the middle window.
+ * This should cause touches for Device B to slip from the right window to the middle window.
+ * The right window should receive ACTION_CANCEL for device B and the
+ * middle window should receive down event for Device B.
+ * If device B reports more ACTION_MOVE events, the middle window should receive remaining events.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSlipperyWindowTest) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> middleWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "middle window",
+ ADISPLAY_ID_DEFAULT);
+ middleWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 300, 100));
+ rightWindow->setSlippery(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *middleWindow->getInfo(), *rightWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ // Tap on left window with device A
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Tap on right window with device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(50))
+ .deviceId(deviceB)
+ .build());
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to middle window with device B. Touches should slip to middle window, because right
+ // window is a foreground window that's associated with device B and has FLAG_SLIPPERY.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to middle window with device A. Touches should slip to middle window, because left
+ // window is a foreground window that's associated with device A and has FLAG_SLIPPERY.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceA)));
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Ensure that middle window can receive the remaining move events.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->assertNoEvents();
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+ rightWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
using Uid = gui::Uid;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
@@ -7217,6 +7927,60 @@
spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id)));
}
+/**
+ * When a device reports a DOWN event, which lands in a window that supports splits, and then the
+ * device then reports a POINTER_DOWN, which lands in the location of a non-existing window, then
+ * the previous window should receive this event and not be dropped.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
+ .build());
+
+ window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_1_DOWN)));
+}
+
+/**
+ * When deviceA reports a DOWN event, which lands in a window that supports splits, and then deviceB
+ * also reports a DOWN event, which lands in the location of a non-existing window, then the
+ * previous window should receive deviceB's event and it should be dropped.
+ */
+TEST_F(InputDispatcherMultiDeviceTest, SecondDeviceDownEventDroppedWithoutWindowTarget) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
+ .deviceId(deviceB)
+ .build());
+ window->assertNoEvents();
+}
+
class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
protected:
std::shared_ptr<FakeApplicationHandle> mApp;
@@ -7755,13 +8519,13 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
- windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ui::ADISPLAY_ID_NONE);
// Remove all windows in secondary display.
mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
// Old focus should receive a cancel event.
- windowInSecondary->consumeKeyUp(ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED);
+ windowInSecondary->consumeKeyUp(ui::ADISPLAY_ID_NONE, AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
@@ -7806,12 +8570,12 @@
// If specific a display, it will dispatch to the focused window of particular display,
// or it will dispatch to the focused window of focused display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ui::ADISPLAY_ID_NONE))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
- windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
- monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeMotionDown(ui::ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeMotionDown(ui::ADISPLAY_ID_NONE);
}
// Test per-display input monitors for key event.
@@ -7827,8 +8591,8 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
- windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ui::ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeKeyDown(ui::ADISPLAY_ID_NONE);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) {
@@ -7987,7 +8751,7 @@
class InputFilterTest : public InputDispatcherTest {
protected:
- void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
+ void testNotifyMotion(ui::LogicalDisplayId displayId, bool expectToBeFiltered,
const ui::Transform& transform = ui::Transform()) {
NotifyMotionArgs motionArgs;
@@ -8112,7 +8876,7 @@
const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
event.initialize(InputEvent::nextId(), injectedDeviceId, AINPUT_SOURCE_KEYBOARD,
- ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A,
+ ui::ADISPLAY_ID_NONE, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A,
KEY_A, AMETA_NONE, /*repeatCount=*/0, eventTime, eventTime);
const int32_t additionalPolicyFlags =
POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
@@ -8206,7 +8970,7 @@
mWindow->consumeFocusEvent(true);
}
- void notifyAndConsumeMotion(int32_t action, uint32_t source, int32_t displayId,
+ void notifyAndConsumeMotion(int32_t action, uint32_t source, ui::LogicalDisplayId displayId,
nsecs_t eventTime) {
mDispatcher->notifyMotion(MotionArgsBuilder(action, source)
.displayId(displayId)
@@ -8721,7 +9485,7 @@
// Send a regular key and respond, which should not cause an ANR.
TEST_F(InputDispatcherSingleWindowAnr, WhenKeyIsConsumed_NoAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher));
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -8797,7 +9561,8 @@
// injection times out (instead of failing).
const InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, 50ms, /*allowKeyRepeat=*/false);
+ InputEventInjectionSync::WAIT_FOR_RESULT, 50ms,
+ /*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result);
const std::chrono::duration timeout = mApplication->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyNoFocusedWindowAnrWasCalled(timeout, mApplication);
@@ -8820,7 +9585,7 @@
std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count();
// Define a valid key down event that is stale (too old).
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE,
INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A,
AMETA_NONE, /*repeatCount=*/0, eventTime, eventTime);
@@ -9501,7 +10266,8 @@
InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms);
+ InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// 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.
@@ -9604,7 +10370,8 @@
// Key will not be sent anywhere because we have no focused window. It will remain pending.
InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -9706,7 +10473,8 @@
// Pretend we are injecting KEYCODE_BACK, but it doesn't actually matter what key it is.
InputEventInjectionResult result =
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -9715,7 +10483,8 @@
.pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
.build());
result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms,
+ InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// The key that was injected is blocking the dispatcher, so the navigation bar shouldn't be
@@ -9837,7 +10606,7 @@
mWindow->consumeFocusEvent(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
}
// A focused & mirrored window remains focused only if the window and its mirror are both
@@ -9849,10 +10618,10 @@
mWindow->consumeFocusEvent(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::ADISPLAY_ID_NONE);
mMirror->setFocusable(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9874,20 +10643,20 @@
mWindow->consumeFocusEvent(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::ADISPLAY_ID_NONE);
mMirror->setVisible(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::ADISPLAY_ID_NONE);
mWindow->setVisible(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9908,20 +10677,20 @@
mWindow->consumeFocusEvent(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::ADISPLAY_ID_NONE);
// single window is removed but the window token remains focused
mDispatcher->onWindowInfosChanged({{*mMirror->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mMirror->consumeKeyDown(ADISPLAY_ID_NONE);
+ mMirror->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mMirror->consumeKeyUp(ADISPLAY_ID_NONE);
+ mMirror->consumeKeyUp(ui::ADISPLAY_ID_NONE);
// Both windows are removed
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
@@ -11728,11 +12497,11 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeKeyDown(ADISPLAY_ID_NONE);
+ window->consumeKeyDown(ui::ADISPLAY_ID_NONE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeKeyUp(ADISPLAY_ID_NONE);
+ window->consumeKeyUp(ui::ADISPLAY_ID_NONE);
spy->assertNoEvents();
}
@@ -12007,7 +12776,8 @@
* Pilfer from spy window.
* Check that the pilfering only affects the pointers that are actually being received by the spy.
*/
-TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer) {
+TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
sp<FakeWindowHandle> spy = createSpy();
spy->setFrame(Rect(0, 0, 200, 200));
sp<FakeWindowHandle> leftWindow = createForeground();
@@ -12065,6 +12835,83 @@
rightWindow->assertNoEvents();
}
+/**
+ * A window on the left and a window on the right. Also, a spy window that's above all of the
+ * windows, and spanning both left and right windows.
+ * Send simultaneous motion streams from two different devices, one to the left window, and another
+ * to the right window.
+ * Pilfer from spy window.
+ * Check that the pilfering affects all of the pointers that are actually being received by the spy.
+ * The spy should receive both the touch and the stylus events after pilfer.
+ */
+TEST_F(InputDispatcherPilferPointersTest, MultiDevicePilfer) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ sp<FakeWindowHandle> spy = createSpy();
+ spy->setFrame(Rect(0, 0, 200, 200));
+ sp<FakeWindowHandle> leftWindow = createForeground();
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+
+ sp<FakeWindowHandle> rightWindow = createForeground();
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ constexpr int32_t stylusDeviceId = 1;
+ constexpr int32_t touchDeviceId = 2;
+
+ mDispatcher->onWindowInfosChanged(
+ {{*spy->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()}, {}, 0, 0});
+
+ // Stylus down on left window and spy
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
+ .build());
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(stylusDeviceId)));
+
+ // Finger down on right window and spy
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .build());
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(touchDeviceId)));
+
+ // Act: pilfer from spy. Spy is currently receiving touch events.
+ EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
+ leftWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(stylusDeviceId)));
+ rightWindow->consumeMotionEvent(
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(touchDeviceId)));
+
+ // Continue movements from both stylus and touch. Touch and stylus will be delivered to spy
+ // Instead of sending the two MOVE events for each input device together, and then receiving
+ // them both, process them one at at time. InputConsumer is always in the batching mode, which
+ // means that the two MOVE events will be initially put into a batch. Once the events are
+ // batched, the 'consume' call may result in any of the MOVE events to be sent first (depending
+ // on the implementation of InputConsumer), which would mean that the order of the received
+ // events could be different depending on whether there are 1 or 2 events pending in the
+ // InputChannel at the time the test calls 'consume'. To make assertions simpler here, and to
+ // avoid this confusing behaviour, send and receive each MOVE event separately.
+ mDispatcher->notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(stylusDeviceId)
+ .pointer(PointerBuilder(0, ToolType::STYLUS).x(51).y(52))
+ .build());
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(stylusDeviceId)));
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .deviceId(touchDeviceId)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(52))
+ .build());
+ spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(touchDeviceId)));
+
+ spy->assertNoEvents();
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherPilferPointersTest, NoPilferingWithHoveringPointers) {
auto window = createForeground();
auto spy = createSpy();
@@ -12234,7 +13081,8 @@
}
InputEventInjectionResult injectTargetedKey(int32_t action) const {
- return inputdispatcher::injectKey(*mDispatcher, action, /*repeatCount=*/0, ADISPLAY_ID_NONE,
+ return inputdispatcher::injectKey(*mDispatcher, action, /*repeatCount=*/0,
+ ui::ADISPLAY_ID_NONE,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, /*allowKeyRepeat=*/false, {mUid},
mPolicyFlags);
@@ -12266,7 +13114,7 @@
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
- window->consumeKeyDown(ADISPLAY_ID_NONE);
+ window->consumeKeyDown(ui::ADISPLAY_ID_NONE);
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
@@ -12339,7 +13187,7 @@
randosSpy->consumeFocusEvent(true);
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher));
- randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
+ randosSpy->consumeKeyDown(ui::ADISPLAY_ID_NONE);
window->assertNoEvents();
}
@@ -12529,7 +13377,8 @@
/*pointerId=*/0));
}
-TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse) {
+TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse_legacy) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
@@ -12599,4 +13448,76 @@
/*pointerId=*/0));
}
+/**
+ * TODO(b/313689709) - correctly support multiple mouse devices, because they should be controlling
+ * the same cursor, and therefore have a shared motion event stream.
+ */
+TEST_F(InputDispatcherPointerInWindowTest, MultipleDevicesControllingOneMouse) {
+ SCOPED_FLAG_OVERRIDE(enable_multi_device_same_window_stream, true);
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
+ ADISPLAY_ID_DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Right Window", ADISPLAY_ID_DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+
+ mDispatcher->onWindowInfosChanged({{*left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Hover move into the window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(50).y(50))
+ .rawXCursorPosition(50)
+ .rawYCursorPosition(50)
+ .deviceId(DEVICE_ID)
+ .build());
+
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Move the mouse with another device
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(51).y(50))
+ .rawXCursorPosition(51)
+ .rawYCursorPosition(50)
+ .deviceId(SECOND_DEVICE_ID)
+ .build());
+ left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+
+ // TODO(b/313689709): InputDispatcher's touch state is not updated, even though the window gets
+ // a HOVER_EXIT from the first device.
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ SECOND_DEVICE_ID,
+ /*pointerId=*/0));
+
+ // Move the mouse outside the window. Document the current behavior, where the window does not
+ // receive HOVER_EXIT even though the mouse left the window.
+ mDispatcher->notifyMotion(
+ MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::MOUSE).x(150).y(50))
+ .rawXCursorPosition(150)
+ .rawYCursorPosition(50)
+ .deviceId(SECOND_DEVICE_ID)
+ .build());
+
+ right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ SECOND_DEVICE_ID,
+ /*pointerId=*/0));
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 2aecab9..fea1349 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -29,14 +29,8 @@
using testing::Return;
void InputMapperUnitTest::SetUpWithBus(int bus) {
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y);
mFakePolicy = sp<FakeInputReaderPolicy>::make();
- EXPECT_CALL(mMockInputReaderContext, getPointerController(DEVICE_ID))
- .WillRepeatedly(Return(mFakePointerController));
-
EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get()));
EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
@@ -178,8 +172,8 @@
return device;
}
-void InputMapperTest::setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
- ui::Rotation orientation,
+void InputMapperTest::setDisplayInfoAndReconfigure(ui::LogicalDisplayId displayId, int32_t width,
+ int32_t height, ui::Rotation orientation,
const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index e176a65..5bd8cda 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -40,8 +40,6 @@
protected:
static constexpr int32_t EVENTHUB_ID = 1;
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
- static constexpr float INITIAL_CURSOR_X = 400;
- static constexpr float INITIAL_CURSOR_Y = 240;
virtual void SetUp() override { SetUpWithBus(0); }
virtual void SetUpWithBus(int bus);
@@ -66,7 +64,6 @@
InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- std::shared_ptr<FakePointerController> mFakePointerController;
MockInputReaderContext mMockInputReaderContext;
std::unique_ptr<InputDevice> mDevice;
@@ -131,7 +128,7 @@
args...);
}
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ void setDisplayInfoAndReconfigure(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType);
diff --git a/services/inputflinger/tests/InputProcessorConverter_test.cpp b/services/inputflinger/tests/InputProcessorConverter_test.cpp
index 4b42f4b..1556532 100644
--- a/services/inputflinger/tests/InputProcessorConverter_test.cpp
+++ b/services/inputflinger/tests/InputProcessorConverter_test.cpp
@@ -17,7 +17,6 @@
#include "../InputCommonConverter.h"
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <utils/BitSet.h>
using namespace aidl::android::hardware::input;
@@ -39,7 +38,7 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.5);
static constexpr nsecs_t downTime = 2;
NotifyMotionArgs motionArgs(/*sequenceNum=*/1, /*eventTime=*/downTime, /*readTime=*/2,
- /*deviceId=*/3, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ /*deviceId=*/3, AINPUT_SOURCE_ANY, ui::ADISPLAY_ID_DEFAULT,
/*policyFlags=*/4, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0,
/*flags=*/0, AMETA_NONE, /*buttonState=*/0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
diff --git a/services/inputflinger/tests/InputProcessor_test.cpp b/services/inputflinger/tests/InputProcessor_test.cpp
index 3b7cbfa..5606a91 100644
--- a/services/inputflinger/tests/InputProcessor_test.cpp
+++ b/services/inputflinger/tests/InputProcessor_test.cpp
@@ -16,7 +16,6 @@
#include "../InputProcessor.h"
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include "TestInputListener.h"
@@ -45,7 +44,7 @@
coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
static constexpr nsecs_t downTime = 2;
NotifyMotionArgs motionArgs(/*sequenceNum=*/1, /*eventTime=*/downTime, /*readTime=*/2,
- /*deviceId=*/3, AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT,
+ /*deviceId=*/3, AINPUT_SOURCE_ANY, ui::ADISPLAY_ID_DEFAULT,
/*policyFlags=*/4, AMOTION_EVENT_ACTION_DOWN, /*actionButton=*/0,
/*flags=*/0, AMETA_NONE, /*buttonState=*/0,
MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE,
@@ -81,7 +80,7 @@
TEST_F(InputProcessorTest, SendToNextStage_NotifyKeyArgs) {
// Create a basic key event and send to processor
NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3,
- AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, /*policyFlags=*/0,
+ AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_DEFAULT, /*policyFlags=*/0,
AKEY_EVENT_ACTION_DOWN, /*flags=*/4, AKEYCODE_HOME, /*scanCode=*/5,
AMETA_NONE, /*downTime=*/6);
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 1d46c9a..fcc52a8 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -40,13 +40,11 @@
#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <ui/Rotation.h>
#include <thread>
#include "FakeEventHub.h"
#include "FakeInputReaderPolicy.h"
-#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InstrumentedInputReader.h"
#include "TestConstants.h"
@@ -61,13 +59,14 @@
using std::chrono_literals::operator""s;
// Arbitrary display properties.
-static constexpr int32_t DISPLAY_ID = 0;
+static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
static const std::string DISPLAY_UNIQUE_ID = "local:1";
-static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
+static constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID =
+ ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
static constexpr int32_t DISPLAY_WIDTH = 480;
static constexpr int32_t DISPLAY_HEIGHT = 800;
-static constexpr int32_t VIRTUAL_DISPLAY_ID = 1;
+static constexpr ui::LogicalDisplayId VIRTUAL_DISPLAY_ID = ui::LogicalDisplayId{1};
static constexpr int32_t VIRTUAL_DISPLAY_WIDTH = 400;
static constexpr int32_t VIRTUAL_DISPLAY_HEIGHT = 500;
static const char* VIRTUAL_DISPLAY_UNIQUE_ID = "virtual:1";
@@ -359,7 +358,7 @@
virtual void fadePointer() {
}
- virtual std::optional<int32_t> getAssociatedDisplay() {
+ virtual std::optional<ui::LogicalDisplayId> getAssociatedDisplay() {
if (mViewport) {
return std::make_optional(mViewport->displayId);
}
@@ -418,8 +417,8 @@
const std::string externalUniqueId = "local:1";
const std::string virtualUniqueId1 = "virtual:2";
const std::string virtualUniqueId2 = "virtual:3";
- constexpr int32_t virtualDisplayId1 = 2;
- constexpr int32_t virtualDisplayId2 = 3;
+ constexpr ui::LogicalDisplayId virtualDisplayId1 = ui::LogicalDisplayId{2};
+ constexpr ui::LogicalDisplayId virtualDisplayId2 = ui::LogicalDisplayId{3};
// Add an internal viewport
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
@@ -476,8 +475,8 @@
TEST_F(InputReaderPolicyTest, Viewports_TwoOfSameType) {
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
- constexpr int32_t displayId1 = 2;
- constexpr int32_t displayId2 = 3;
+ constexpr ui::LogicalDisplayId displayId1 = ui::LogicalDisplayId{2};
+ constexpr ui::LogicalDisplayId displayId2 = ui::LogicalDisplayId{3};
std::vector<ViewportType> types = {ViewportType::INTERNAL, ViewportType::EXTERNAL,
ViewportType::VIRTUAL};
@@ -521,13 +520,13 @@
TEST_F(InputReaderPolicyTest, Viewports_ByTypeReturnsDefaultForInternal) {
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
- constexpr int32_t nonDefaultDisplayId = 2;
- static_assert(nonDefaultDisplayId != ADISPLAY_ID_DEFAULT,
- "Test display ID should not be ADISPLAY_ID_DEFAULT");
+ constexpr ui::LogicalDisplayId nonDefaultDisplayId = ui::LogicalDisplayId{2};
+ ASSERT_NE(nonDefaultDisplayId, ui::ADISPLAY_ID_DEFAULT)
+ << "Test display ID should not be ui::ADISPLAY_ID_DEFAULT ";
// Add the default display first and ensure it gets returned.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ mFakePolicy->addDisplayViewport(ui::ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
@@ -537,7 +536,7 @@
std::optional<DisplayViewport> viewport =
mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(viewport);
- ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_DEFAULT, viewport->displayId);
ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
// Add the default display second to make sure order doesn't matter.
@@ -545,13 +544,13 @@
mFakePolicy->addDisplayViewport(nonDefaultDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT,
ui::ROTATION_0, /*isActive=*/true, uniqueId2, NO_PORT,
ViewportType::INTERNAL);
- mFakePolicy->addDisplayViewport(ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ mFakePolicy->addDisplayViewport(ui::ADISPLAY_ID_DEFAULT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
ui::ROTATION_0, /*isActive=*/true, uniqueId1, NO_PORT,
ViewportType::INTERNAL);
viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL);
ASSERT_TRUE(viewport);
- ASSERT_EQ(ADISPLAY_ID_DEFAULT, viewport->displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_DEFAULT, viewport->displayId);
ASSERT_EQ(ViewportType::INTERNAL, viewport->type);
}
@@ -562,8 +561,8 @@
constexpr ViewportType type = ViewportType::EXTERNAL;
const std::string uniqueId1 = "uniqueId1";
const std::string uniqueId2 = "uniqueId2";
- constexpr int32_t displayId1 = 1;
- constexpr int32_t displayId2 = 2;
+ constexpr ui::LogicalDisplayId displayId1 = ui::LogicalDisplayId{1};
+ constexpr ui::LogicalDisplayId displayId2 = ui::LogicalDisplayId{2};
const uint8_t hdmi1 = 0;
const uint8_t hdmi2 = 1;
const uint8_t hdmi3 = 2;
@@ -1349,8 +1348,6 @@
sp<FakeInputReaderPolicy> mFakePolicy;
std::unique_ptr<InputReaderInterface> mReader;
- std::shared_ptr<FakePointerController> mFakePointerController;
-
constexpr static auto EVENT_HAPPENED_TIMEOUT = 2000ms;
constexpr static auto EVENT_DID_NOT_HAPPEN_TIMEOUT = 30ms;
@@ -1359,8 +1356,6 @@
GTEST_SKIP();
#endif
mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
setupInputReader();
}
@@ -1603,7 +1598,7 @@
mDeviceInfo = *info;
}
- void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ void setDisplayInfoAndReconfigure(ui::LogicalDisplayId displayId, int32_t width, int32_t height,
ui::Rotation orientation, const std::string& uniqueId,
std::optional<uint8_t> physicalPort,
ViewportType viewportType) {
@@ -1654,8 +1649,6 @@
} else {
mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID);
}
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
InputReaderIntegrationTest::setupInputReader();
@@ -2907,7 +2900,7 @@
const auto initialGeneration = mDevice->getGeneration();
unused += mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
InputReaderConfiguration::Change::DISPLAY_INFO);
- ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId());
+ ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueIdByPort());
ASSERT_GT(mDevice->getGeneration(), initialGeneration);
ASSERT_EQ(mDevice->getDeviceInfo().getAssociatedDisplayId(), SECONDARY_DISPLAY_ID);
}
@@ -3251,7 +3244,7 @@
void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
int32_t originalKeyCode, int32_t rotatedKeyCode,
- int32_t displayId = ADISPLAY_ID_NONE);
+ ui::LogicalDisplayId displayId = ui::ADISPLAY_ID_NONE);
};
/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
@@ -3264,7 +3257,8 @@
void KeyboardInputMapperTest::testDPadKeyRotation(KeyboardInputMapper& mapper,
int32_t originalScanCode, int32_t originalKeyCode,
- int32_t rotatedKeyCode, int32_t displayId) {
+ int32_t rotatedKeyCode,
+ ui::LogicalDisplayId displayId) {
NotifyKeyArgs args;
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, originalScanCode, 1);
@@ -3590,14 +3584,14 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, args.displayId);
prepareDisplay(ui::ROTATION_0);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 1);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_UP, 0);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
- ASSERT_EQ(ADISPLAY_ID_NONE, args.displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, args.displayId);
}
TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
@@ -3622,7 +3616,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
ASSERT_EQ(DISPLAY_ID, args.displayId);
- constexpr int32_t newDisplayId = 2;
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
clearViewports();
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, NO_PORT, ViewportType::INTERNAL);
@@ -3837,7 +3831,7 @@
ASSERT_FALSE(device2->isEnabled());
// Prepare second display.
- constexpr int32_t newDisplayId = 2;
+ constexpr ui::LogicalDisplayId newDisplayId = ui::LogicalDisplayId{2};
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
UNIQUE_ID, hdmi1, ViewportType::INTERNAL);
setDisplayInfoAndReconfigure(newDisplayId, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
@@ -5344,10 +5338,6 @@
}
TEST_F(SingleTouchInputMapperTest, Process_DoesntCheckPhysicalFrameForTouchpads) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(fakePointerController);
-
addConfigurationProperty("touch.deviceType", "pointer");
prepareAxes(POSITION);
prepareDisplay(ui::ROTATION_0);
@@ -6192,52 +6182,6 @@
ASSERT_EQ(AINPUT_SOURCE_TOUCH_NAVIGATION, mapper.getSources());
}
-TEST_F(SingleTouchInputMapperTest, Process_WhenConfigEnabled_ShouldShowDirectStylusPointer) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(ui::ROTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(fakePointerController);
- mFakePolicy->setStylusPointerIconEnabled(true);
- SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
-
- processKey(mapper, BTN_TOOL_PEN, 1);
- processMove(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithToolType(ToolType::STYLUS),
- WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
- ASSERT_TRUE(fakePointerController->isPointerShown());
- ASSERT_NO_FATAL_FAILURE(
- fakePointerController->assertPosition(toDisplayX(100), toDisplayY(200)));
-}
-
-TEST_F(SingleTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirectStylusPointer) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(ui::ROTATION_0);
- prepareButtons();
- prepareAxes(POSITION);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(fakePointerController);
- mFakePolicy->setStylusPointerIconEnabled(false);
- SingleTouchInputMapper& mapper = constructAndAddMapper<SingleTouchInputMapper>();
-
- processKey(mapper, BTN_TOOL_PEN, 1);
- processMove(mapper, 100, 200);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithToolType(ToolType::STYLUS),
- WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
- ASSERT_FALSE(fakePointerController->isPointerShown());
-}
-
TEST_F(SingleTouchInputMapperTest, WhenDeviceTypeIsChangedToTouchNavigation_updatesDeviceType) {
// Initialize the device without setting device source to touch navigation.
addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -8717,21 +8661,12 @@
}
TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) {
- // Setup for second display.
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- fakePointerController->setPosition(100, 200);
- mFakePolicy->setPointerController(fakePointerController);
-
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
prepareSecondaryDisplay(ViewportType::EXTERNAL);
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
- // Check source is mouse that would obtain the PointerController.
ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
NotifyMotionArgs motionArgs;
@@ -8740,7 +8675,7 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_NONE, motionArgs.displayId);
}
/**
@@ -8920,97 +8855,6 @@
WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
}
-TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) {
- // Setup the first touch screen device.
- prepareAxes(POSITION | ID | SLOT);
- addConfigurationProperty("touch.deviceType", "touchScreen");
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- // Create the second touch screen device, and enable multi fingers.
- const std::string USB2 = "USB2";
- const std::string DEVICE_NAME2 = "TOUCHSCREEN2";
- constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
- constexpr int32_t SECOND_EVENTHUB_ID = EVENTHUB_ID + 1;
- std::shared_ptr<InputDevice> device2 =
- newDevice(SECOND_DEVICE_ID, DEVICE_NAME2, USB2, SECOND_EVENTHUB_ID,
- ftl::Flags<InputDeviceClass>(0));
-
- mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_X, RAW_X_MIN, RAW_X_MAX,
- /*flat=*/0, /*fuzz=*/0);
- mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_POSITION_Y, RAW_Y_MIN, RAW_Y_MAX,
- /*flat=*/0, /*fuzz=*/0);
- mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_TRACKING_ID, RAW_ID_MIN, RAW_ID_MAX,
- /*flat=*/0, /*fuzz=*/0);
- mFakeEventHub->addAbsoluteAxis(SECOND_EVENTHUB_ID, ABS_MT_SLOT, RAW_SLOT_MIN, RAW_SLOT_MAX,
- /*flat=*/0, /*fuzz=*/0);
- mFakeEventHub->setAbsoluteAxisValue(SECOND_EVENTHUB_ID, ABS_MT_SLOT, /*value=*/0);
- mFakeEventHub->addConfigurationProperty(SECOND_EVENTHUB_ID, String8("touch.deviceType"),
- String8("touchScreen"));
-
- // Setup the second touch screen device.
- device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
- MultiTouchInputMapper& mapper2 = device2->constructAndAddMapper<
- MultiTouchInputMapper>(SECOND_EVENTHUB_ID, mFakePolicy->getReaderConfiguration());
- std::list<NotifyArgs> unused =
- device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- /*changes=*/{});
- unused += device2->reset(ARBITRARY_TIME);
-
- // Setup PointerController.
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(fakePointerController);
-
- // Setup policy for associated displays and show touches.
- const uint8_t hdmi1 = 0;
- const uint8_t hdmi2 = 1;
- mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
- mFakePolicy->addInputPortAssociation(USB2, hdmi2);
- mFakePolicy->setShowTouches(true);
-
- // Create displays.
- prepareDisplay(ui::ROTATION_0, hdmi1);
- prepareSecondaryDisplay(ViewportType::EXTERNAL, hdmi2);
-
- // Default device will reconfigure above, need additional reconfiguration for another device.
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::DISPLAY_INFO |
- InputReaderConfiguration::Change::SHOW_TOUCHES);
-
- // Two fingers down at default display.
- int32_t x1 = 100, y1 = 125, x2 = 300, y2 = 500;
- processPosition(mapper, x1, y1);
- processId(mapper, 1);
- processSlot(mapper, 1);
- processPosition(mapper, x2, y2);
- processId(mapper, 2);
- processSync(mapper);
-
- std::map<int32_t, std::vector<int32_t>>::const_iterator iter =
- fakePointerController->getSpots().find(DISPLAY_ID);
- ASSERT_TRUE(iter != fakePointerController->getSpots().end());
- ASSERT_EQ(size_t(2), iter->second.size());
-
- // Two fingers down at second display.
- processPosition(mapper2, x1, y1);
- processId(mapper2, 1);
- processSlot(mapper2, 1);
- processPosition(mapper2, x2, y2);
- processId(mapper2, 2);
- processSync(mapper2);
-
- iter = fakePointerController->getSpots().find(SECONDARY_DISPLAY_ID);
- ASSERT_TRUE(iter != fakePointerController->getSpots().end());
- ASSERT_EQ(size_t(2), iter->second.size());
-
- // Disable the show touches configuration and ensure the spots are cleared.
- mFakePolicy->setShowTouches(false);
- unused += device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
- InputReaderConfiguration::Change::SHOW_TOUCHES);
-
- ASSERT_TRUE(fakePointerController->getSpots().empty());
-}
-
TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) {
prepareAxes(POSITION);
addConfigurationProperty("touch.deviceType", "touchScreen");
@@ -9046,7 +8890,7 @@
// Test all 4 orientations
for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
- SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+ SCOPED_TRACE(StringPrintf("Orientation %s", ftl::enum_string(orientation).c_str()));
clearViewports();
prepareDisplay(orientation);
std::vector<TouchVideoFrame> frames{frame};
@@ -9071,7 +8915,7 @@
// Test all 4 orientations
for (ui::Rotation orientation : ftl::enum_range<ui::Rotation>()) {
- SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation));
+ SCOPED_TRACE(StringPrintf("Orientation %s", ftl::enum_string(orientation).c_str()));
clearViewports();
prepareDisplay(orientation);
std::vector<TouchVideoFrame> frames{frame};
@@ -9703,58 +9547,6 @@
WithToolType(ToolType::STYLUS))));
}
-TEST_F(MultiTouchInputMapperTest, Process_WhenConfigEnabled_ShouldShowDirectStylusPointer) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(ui::ROTATION_0);
- prepareAxes(POSITION | ID | SLOT | TOOL_TYPE | PRESSURE);
- // Add BTN_TOOL_PEN to statically show stylus support, since using ABS_MT_TOOL_TYPE can only
- // indicate stylus presence dynamically.
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(fakePointerController);
- mFakePolicy->setStylusPointerIconEnabled(true);
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- processId(mapper, FIRST_TRACKING_ID);
- processPressure(mapper, RAW_PRESSURE_MIN);
- processPosition(mapper, 100, 200);
- processToolType(mapper, MT_TOOL_PEN);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithToolType(ToolType::STYLUS),
- WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
- ASSERT_TRUE(fakePointerController->isPointerShown());
- ASSERT_NO_FATAL_FAILURE(
- fakePointerController->assertPosition(toDisplayX(100), toDisplayY(200)));
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_WhenConfigDisabled_ShouldNotShowDirectStylusPointer) {
- addConfigurationProperty("touch.deviceType", "touchScreen");
- prepareDisplay(ui::ROTATION_0);
- prepareAxes(POSITION | ID | SLOT | TOOL_TYPE | PRESSURE);
- // Add BTN_TOOL_PEN to statically show stylus support, since using ABS_MT_TOOL_TYPE can only
- // indicate stylus presence dynamically.
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOOL_PEN, 0, AKEYCODE_UNKNOWN, 0);
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(fakePointerController);
- mFakePolicy->setStylusPointerIconEnabled(false);
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- processId(mapper, FIRST_TRACKING_ID);
- processPressure(mapper, RAW_PRESSURE_MIN);
- processPosition(mapper, 100, 200);
- processToolType(mapper, MT_TOOL_PEN);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithToolType(ToolType::STYLUS),
- WithPointerCoords(0, toDisplayX(100), toDisplayY(200)))));
- ASSERT_FALSE(fakePointerController->isPointerShown());
-}
-
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {
@@ -9780,7 +9572,7 @@
processPosition(mapper, 100, 100);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+ ASSERT_EQ(ui::ADISPLAY_ID_DEFAULT, motionArgs.displayId);
// Expect the event to be sent to the external viewport if it is present.
prepareSecondaryDisplay(ViewportType::EXTERNAL);
@@ -9790,168 +9582,15 @@
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-TEST_F(MultiTouchInputMapperTest, Process_TouchpadCapture) {
- // we need a pointer controller for mouse mode of touchpad (start pointer at 0,0)
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- fakePointerController->setPosition(0, 0);
-
- // prepare device and capture
+// TODO(b/281840344): Remove the test when the old touchpad stack is removed. It is currently
+// unclear what the behavior of the touchpad logic in TouchInputMapper should do after the
+// PointerChoreographer refactor.
+TEST_F(MultiTouchInputMapperTest, DISABLED_Process_TouchpadPointer) {
+ // prepare device
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
- mFakePolicy->setPointerController(fakePointerController);
- MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
-
- // captured touchpad should be a touchpad source
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
-
- InputDeviceInfo deviceInfo = mDevice->getDeviceInfo();
-
- const InputDeviceInfo::MotionRange* relRangeX =
- deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, AINPUT_SOURCE_TOUCHPAD);
- ASSERT_NE(relRangeX, nullptr);
- ASSERT_EQ(relRangeX->min, -(RAW_X_MAX - RAW_X_MIN));
- ASSERT_EQ(relRangeX->max, RAW_X_MAX - RAW_X_MIN);
- const InputDeviceInfo::MotionRange* relRangeY =
- deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, AINPUT_SOURCE_TOUCHPAD);
- ASSERT_NE(relRangeY, nullptr);
- ASSERT_EQ(relRangeY->min, -(RAW_Y_MAX - RAW_Y_MIN));
- ASSERT_EQ(relRangeY->max, RAW_Y_MAX - RAW_Y_MIN);
-
- // run captured pointer tests - note that this is unscaled, so input listener events should be
- // identical to what the hardware sends (accounting for any
- // calibration).
- // FINGER 0 DOWN
- processSlot(mapper, 0);
- processId(mapper, 1);
- processPosition(mapper, 100 + RAW_X_MIN, 100 + RAW_Y_MIN);
- processKey(mapper, BTN_TOUCH, 1);
- processSync(mapper);
-
- // expect coord[0] to contain initial location of touch 0
- NotifyMotionArgs args;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(1U, args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, args.source);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 DOWN
- processSlot(mapper, 1);
- processId(mapper, 2);
- processPosition(mapper, 560 + RAW_X_MIN, 154 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ACTION_POINTER_1_DOWN, args.action);
- ASSERT_EQ(2U, args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(1, args.pointerProperties[1].id);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 560, 154, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 MOVE
- processPosition(mapper, 540 + RAW_X_MIN, 690 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain previous location, coord[1] to contain new touch 1 location
- // from move
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 100, 100, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 0 MOVE
- processSlot(mapper, 0);
- processPosition(mapper, 50 + RAW_X_MIN, 800 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain new touch 0 location, coord[1] to contain previous location
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 50, 800, 1, 0, 0, 0, 0, 0, 0, 0));
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[1], 540, 690, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // BUTTON DOWN
- processKey(mapper, BTN_LEFT, 1);
- processSync(mapper);
-
- // touchinputmapper design sends a move before button press
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
-
- // BUTTON UP
- processKey(mapper, BTN_LEFT, 0);
- processSync(mapper);
-
- // touchinputmapper design sends a move after button release
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
-
- // FINGER 0 UP
- processId(mapper, -1);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | 0x0000, args.action);
-
- // FINGER 1 MOVE
- processSlot(mapper, 1);
- processPosition(mapper, 320 + RAW_X_MIN, 900 + RAW_Y_MIN);
- processSync(mapper);
-
- // expect coord[0] to contain new location of touch 1, and properties[0].id to contain 1
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(1U, args.getPointerCount());
- ASSERT_EQ(1, args.pointerProperties[0].id);
- ASSERT_NO_FATAL_FAILURE(
- assertPointerCoords(args.pointerCoords[0], 320, 900, 1, 0, 0, 0, 0, 0, 0, 0));
-
- // FINGER 1 UP
- processId(mapper, -1);
- processKey(mapper, BTN_TOUCH, 0);
- processSync(mapper);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
-
- // non captured touchpad should be a mouse source
- mFakePolicy->setPointerCapture(/*window=*/nullptr);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-}
-
-TEST_F(MultiTouchInputMapperTest, Process_UnCapturedTouchpadPointer) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- fakePointerController->setPosition(0, 0);
-
- // prepare device and capture
- prepareDisplay(ui::ROTATION_0);
- prepareAxes(POSITION | ID | SLOT);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
- mFakeEventHub->addKey(EVENTHUB_ID, BTN_TOUCH, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(fakePointerController);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
// run uncaptured pointer tests - pushes out generic events
// FINGER 0 DOWN
@@ -10004,24 +9643,15 @@
ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
}
-TEST_F(MultiTouchInputMapperTest, WhenCapturedAndNotCaptured_GetSources) {
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
-
+TEST_F(MultiTouchInputMapperTest, Touchpad_GetSources) {
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT);
mFakeEventHub->addKey(EVENTHUB_ID, BTN_LEFT, 0, AKEYCODE_UNKNOWN, 0);
- mFakePolicy->setPointerController(fakePointerController);
mFakePolicy->setPointerCapture(/*window=*/nullptr);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
// uncaptured touchpad should be a pointer device
ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-
- // captured touchpad should be a touchpad device
- mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
// --- BluetoothMultiTouchInputMapperTest ---
@@ -10080,10 +9710,6 @@
float mPointerXZoomScale;
void preparePointerMode(int xAxisResolution, int yAxisResolution) {
addConfigurationProperty("touch.deviceType", "pointer");
- std::shared_ptr<FakePointerController> fakePointerController =
- std::make_shared<FakePointerController>();
- fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- fakePointerController->setPosition(0, 0);
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION);
@@ -10092,7 +9718,6 @@
// needs to be disabled, and the pointer gesture needs to be enabled.
mFakePolicy->setPointerCapture(/*window=*/nullptr);
mFakePolicy->setPointerGestureEnabled(true);
- mFakePolicy->setPointerController(fakePointerController);
float rawDiagonal = hypotf(RAW_X_MAX - RAW_X_MIN, RAW_Y_MAX - RAW_Y_MIN);
float displayDiagonal = hypotf(DISPLAY_WIDTH, DISPLAY_HEIGHT);
@@ -10598,6 +10223,28 @@
ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_BRIGHTNESS);
}
+TEST_F(LightControllerTest, MonoKeyboardMuteLight) {
+ RawLightInfo infoMono = {.id = 1,
+ .name = "mono_keyboard_mute",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS |
+ InputLightClass::KEYBOARD_MIC_MUTE,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(infoMono.id, std::move(infoMono));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_EQ(InputDeviceLightType::KEYBOARD_MIC_MUTE, lights[0].type);
+ ASSERT_EQ(0U, lights[0].preferredBrightnessLevels.size());
+}
+
TEST_F(LightControllerTest, MonoKeyboardBacklight) {
RawLightInfo infoMono = {.id = 1,
.name = "mono_keyboard_backlight",
diff --git a/services/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp
new file mode 100644
index 0000000..32acb5f
--- /dev/null
+++ b/services/inputflinger/tests/InputTraceSession.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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 "InputTraceSession.h"
+
+#include <NotifyArgsBuilders.h>
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <input/PrintTools.h>
+
+#include <utility>
+
+namespace android {
+
+using perfetto::protos::pbzero::AndroidInputEvent;
+using perfetto::protos::pbzero::AndroidInputEventConfig;
+using perfetto::protos::pbzero::AndroidKeyEvent;
+using perfetto::protos::pbzero::AndroidMotionEvent;
+using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent;
+
+// These operator<< definitions must be in the global namespace for them to be accessible to the
+// GTEST library. They cannot be in the anonymous namespace.
+static std::ostream& operator<<(std::ostream& out,
+ const std::variant<KeyEvent, MotionEvent>& event) {
+ std::visit([&](const auto& e) { out << e; }, event);
+ return out;
+}
+
+static std::ostream& operator<<(std::ostream& out,
+ const InputTraceSession::WindowDispatchEvent& event) {
+ out << "Window dispatch to windowId: " << event.window->getId() << ", event: " << event.event;
+ return out;
+}
+
+namespace {
+
+inline uint32_t getId(const std::variant<KeyEvent, MotionEvent>& event) {
+ return std::visit([&](const auto& e) { return e.getId(); }, event);
+}
+
+std::unique_ptr<perfetto::TracingSession> startTrace(
+ const std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)>& configure) {
+ protozero::HeapBuffered<AndroidInputEventConfig> inputEventConfig{};
+ configure(inputEventConfig);
+
+ perfetto::TraceConfig config;
+ config.add_buffers()->set_size_kb(1024); // Record up to 1 MiB.
+ auto* dataSourceConfig = config.add_data_sources()->mutable_config();
+ dataSourceConfig->set_name("android.input.inputevent");
+ dataSourceConfig->set_android_input_event_config_raw(inputEventConfig.SerializeAsString());
+
+ std::unique_ptr<perfetto::TracingSession> tracingSession(perfetto::Tracing::NewTrace());
+ tracingSession->Setup(config);
+ tracingSession->StartBlocking();
+ return tracingSession;
+}
+
+std::string stopTrace(std::unique_ptr<perfetto::TracingSession> tracingSession) {
+ tracingSession->StopBlocking();
+ std::vector<char> traceChars(tracingSession->ReadTraceBlocking());
+ return {traceChars.data(), traceChars.size()};
+}
+
+// Decodes the trace, and returns all of the traced input events, and whether they were each
+// traced as a redacted event.
+auto decodeTrace(const std::string& rawTrace) {
+ using namespace perfetto::protos::pbzero;
+
+ ArrayMap<AndroidMotionEvent::Decoder, bool /*redacted*/> tracedMotions;
+ ArrayMap<AndroidKeyEvent::Decoder, bool /*redacted*/> tracedKeys;
+ ArrayMap<AndroidWindowInputDispatchEvent::Decoder, bool /*redacted*/> tracedWindowDispatches;
+
+ Trace::Decoder trace{rawTrace};
+ if (trace.has_packet()) {
+ auto it = trace.packet();
+ while (it) {
+ TracePacket::Decoder packet{it->as_bytes()};
+ if (packet.has_android_input_event()) {
+ AndroidInputEvent::Decoder event{packet.android_input_event()};
+ if (event.has_dispatcher_motion_event()) {
+ tracedMotions.emplace_back(event.dispatcher_motion_event(),
+ /*redacted=*/false);
+ }
+ if (event.has_dispatcher_motion_event_redacted()) {
+ tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(),
+ /*redacted=*/true);
+ }
+ if (event.has_dispatcher_key_event()) {
+ tracedKeys.emplace_back(event.dispatcher_key_event(),
+ /*redacted=*/false);
+ }
+ if (event.has_dispatcher_key_event_redacted()) {
+ tracedKeys.emplace_back(event.dispatcher_key_event_redacted(),
+ /*redacted=*/true);
+ }
+ if (event.has_dispatcher_window_dispatch_event()) {
+ tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(),
+ /*redacted=*/false);
+ }
+ if (event.has_dispatcher_window_dispatch_event_redacted()) {
+ tracedWindowDispatches
+ .emplace_back(event.dispatcher_window_dispatch_event_redacted(),
+ /*redacted=*/true);
+ }
+ }
+ it++;
+ }
+ }
+ return std::tuple{std::move(tracedMotions), std::move(tracedKeys),
+ std::move(tracedWindowDispatches)};
+}
+
+bool eventMatches(const MotionEvent& expected, const AndroidMotionEvent::Decoder& traced) {
+ return static_cast<uint32_t>(expected.getId()) == traced.event_id();
+}
+
+bool eventMatches(const KeyEvent& expected, const AndroidKeyEvent::Decoder& traced) {
+ return static_cast<uint32_t>(expected.getId()) == traced.event_id();
+}
+
+bool eventMatches(const InputTraceSession::WindowDispatchEvent& expected,
+ const AndroidWindowInputDispatchEvent::Decoder& traced) {
+ return static_cast<uint32_t>(getId(expected.event)) == traced.event_id() &&
+ expected.window->getId() == traced.window_id();
+}
+
+template <typename ExpectedEvents, typename TracedEvents>
+void verifyExpectedEventsTraced(const ExpectedEvents& expectedEvents,
+ const TracedEvents& tracedEvents, std::string_view name) {
+ uint32_t totalExpectedCount = 0;
+
+ for (const auto& [expectedEvent, expectedLevel] : expectedEvents) {
+ int32_t totalMatchCount = 0;
+ int32_t redactedMatchCount = 0;
+ for (const auto& [tracedEvent, isRedacted] : tracedEvents) {
+ if (eventMatches(expectedEvent, tracedEvent)) {
+ totalMatchCount++;
+ if (isRedacted) {
+ redactedMatchCount++;
+ }
+ }
+ }
+ switch (expectedLevel) {
+ case Level::NONE:
+ ASSERT_EQ(totalMatchCount, 0) << "Event should not be traced, but it was traced"
+ << "\n\tExpected event: " << expectedEvent;
+ break;
+ case Level::REDACTED:
+ case Level::COMPLETE:
+ ASSERT_EQ(totalMatchCount, 1)
+ << "Event should match exactly one traced event, but it matched: "
+ << totalMatchCount << "\n\tExpected event: " << expectedEvent;
+ ASSERT_EQ(redactedMatchCount, expectedLevel == Level::REDACTED ? 1 : 0);
+ totalExpectedCount++;
+ break;
+ }
+ }
+
+ ASSERT_EQ(tracedEvents.size(), totalExpectedCount)
+ << "The number of traced " << name
+ << " events does not exactly match the number of expected events";
+}
+
+} // namespace
+
+InputTraceSession::InputTraceSession(
+ std::function<void(protozero::HeapBuffered<AndroidInputEventConfig>&)> configure)
+ : mPerfettoSession(startTrace(std::move(configure))) {}
+
+InputTraceSession::~InputTraceSession() {
+ const auto rawTrace = stopTrace(std::move(mPerfettoSession));
+ verifyExpectations(rawTrace);
+}
+
+void InputTraceSession::expectMotionTraced(Level level, const MotionEvent& event) {
+ mExpectedMotions.emplace_back(event, level);
+}
+
+void InputTraceSession::expectKeyTraced(Level level, const KeyEvent& event) {
+ mExpectedKeys.emplace_back(event, level);
+}
+
+void InputTraceSession::expectDispatchTraced(Level level, const WindowDispatchEvent& event) {
+ mExpectedWindowDispatches.emplace_back(event, level);
+}
+
+void InputTraceSession::verifyExpectations(const std::string& rawTrace) {
+ auto [tracedMotions, tracedKeys, tracedWindowDispatches] = decodeTrace(rawTrace);
+
+ verifyExpectedEventsTraced(mExpectedMotions, tracedMotions, "motion");
+ verifyExpectedEventsTraced(mExpectedKeys, tracedKeys, "key");
+ verifyExpectedEventsTraced(mExpectedWindowDispatches, tracedWindowDispatches,
+ "window dispatch");
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h
new file mode 100644
index 0000000..ed20bc8
--- /dev/null
+++ b/services/inputflinger/tests/InputTraceSession.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "FakeWindows.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <perfetto/config/android/android_input_event_config.pbzero.h>
+#include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/trace.pbzero.h>
+#include <perfetto/tracing.h>
+#include <variant>
+#include <vector>
+
+namespace android {
+
+/**
+ * Tracing level constants used for adding expectations to the InputTraceSession.
+ */
+enum class Level {
+ NONE,
+ REDACTED,
+ COMPLETE,
+};
+
+template <typename K, typename V>
+using ArrayMap = std::vector<std::pair<K, V>>;
+
+/**
+ * A scoped representation of a tracing session that is used to make assertions on the trace.
+ *
+ * When the trace session is created, an "android.input.inputevent" trace will be started
+ * synchronously with the given configuration. While the trace is ongoing, the caller must
+ * specify the events that are expected to be in the trace using the expect* methods.
+ *
+ * When the session is destroyed, the trace is stopped synchronously, and all expectations will
+ * be verified using the gtest framework. This acts as a strict verifier, where the verification
+ * will fail both if an expected event does not show up in the trace and if there is an extra
+ * event in the trace that was not expected. Ordering is NOT verified for any events.
+ */
+class InputTraceSession {
+public:
+ explicit InputTraceSession(
+ std::function<void(
+ protozero::HeapBuffered<perfetto::protos::pbzero::AndroidInputEventConfig>&)>
+ configure);
+
+ ~InputTraceSession();
+
+ void expectMotionTraced(Level level, const MotionEvent& event);
+
+ void expectKeyTraced(Level level, const KeyEvent& event);
+
+ struct WindowDispatchEvent {
+ std::variant<KeyEvent, MotionEvent> event;
+ sp<FakeWindowHandle> window;
+ };
+ void expectDispatchTraced(Level level, const WindowDispatchEvent& event);
+
+private:
+ std::unique_ptr<perfetto::TracingSession> mPerfettoSession;
+ ArrayMap<WindowDispatchEvent, Level> mExpectedWindowDispatches;
+ ArrayMap<MotionEvent, Level> mExpectedMotions;
+ ArrayMap<KeyEvent, Level> mExpectedKeys;
+
+ void verifyExpectations(const std::string& rawTrace);
+};
+
+} // namespace android
diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp
new file mode 100644
index 0000000..dfbbce3
--- /dev/null
+++ b/services/inputflinger/tests/InputTracingTest.cpp
@@ -0,0 +1,749 @@
+/*
+ * 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 "../InputCommonConverter.h"
+#include "../dispatcher/InputDispatcher.h"
+#include "../dispatcher/trace/InputTracingPerfettoBackend.h"
+#include "../dispatcher/trace/ThreadedBackend.h"
+#include "FakeApplicationHandle.h"
+#include "FakeInputDispatcherPolicy.h"
+#include "FakeWindows.h"
+#include "InputTraceSession.h"
+#include "TestEventMatchers.h"
+
+#include <NotifyArgsBuilders.h>
+#include <android-base/logging.h>
+#include <android/content/pm/IPackageManagerNative.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/trace.pbzero.h>
+#include <private/android_filesystem_config.h>
+#include <map>
+#include <vector>
+
+namespace android::inputdispatcher::trace {
+
+using perfetto::protos::pbzero::AndroidInputEventConfig;
+
+namespace {
+
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
+
+// Ensure common actions are interchangeable between keys and motions for convenience.
+static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_DOWN) ==
+ static_cast<int32_t>(AKEY_EVENT_ACTION_DOWN));
+static_assert(static_cast<int32_t>(AMOTION_EVENT_ACTION_UP) ==
+ static_cast<int32_t>(AKEY_EVENT_ACTION_UP));
+constexpr int32_t ACTION_DOWN = AMOTION_EVENT_ACTION_DOWN;
+constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+constexpr int32_t ACTION_UP = AMOTION_EVENT_ACTION_UP;
+constexpr int32_t ACTION_CANCEL = AMOTION_EVENT_ACTION_CANCEL;
+
+constexpr gui::Pid PID{1};
+
+constexpr gui::Uid ALLOWED_UID_1{10012};
+constexpr gui::Uid ALLOWED_UID_2{10013};
+constexpr gui::Uid DISALLOWED_UID_1{1};
+constexpr gui::Uid DISALLOWED_UID_2{99};
+constexpr gui::Uid UNLISTED_UID{12345};
+
+const std::string ALLOWED_PKG_1{"allowed.pkg.1"};
+const std::string ALLOWED_PKG_2{"allowed.pkg.2"};
+const std::string DISALLOWED_PKG_1{"disallowed.pkg.1"};
+const std::string DISALLOWED_PKG_2{"disallowed.pkg.2"};
+
+const std::map<std::string, gui::Uid> kPackageUidMap{
+ {ALLOWED_PKG_1, ALLOWED_UID_1},
+ {ALLOWED_PKG_2, ALLOWED_UID_2},
+ {DISALLOWED_PKG_1, DISALLOWED_UID_1},
+ {DISALLOWED_PKG_2, DISALLOWED_UID_2},
+};
+
+class FakePackageManager : public content::pm::IPackageManagerNativeDefault {
+public:
+ binder::Status getPackageUid(const ::std::string& pkg, int64_t flags, int32_t userId,
+ int32_t* outUid) override {
+ auto it = kPackageUidMap.find(pkg);
+ *outUid = it != kPackageUidMap.end() ? static_cast<int32_t>(it->second.val()) : -1;
+ return binder::Status::ok();
+ }
+};
+
+const sp<testing::NiceMock<FakePackageManager>> kPackageManager =
+ sp<testing::NiceMock<FakePackageManager>>::make();
+
+const std::shared_ptr<FakeApplicationHandle> APP = std::make_shared<FakeApplicationHandle>();
+
+} // namespace
+
+// --- InputTracingTest ---
+
+class InputTracingTest : public testing::Test {
+protected:
+ std::unique_ptr<FakeInputDispatcherPolicy> mFakePolicy;
+ std::unique_ptr<InputDispatcher> mDispatcher;
+
+ void SetUp() override {
+ impl::PerfettoBackend::sUseInProcessBackendForTest = true;
+ impl::PerfettoBackend::sPackageManagerProvider = []() { return kPackageManager; };
+ mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>();
+
+ auto tracingBackend = std::make_unique<impl::ThreadedBackend<impl::PerfettoBackend>>(
+ impl::PerfettoBackend());
+ mRequestTracerIdle = tracingBackend->getIdleWaiterForTesting();
+ mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, std::move(tracingBackend));
+
+ mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ ASSERT_EQ(OK, mDispatcher->start());
+ }
+
+ void TearDown() override {
+ ASSERT_EQ(OK, mDispatcher->stop());
+ mDispatcher.reset();
+ mFakePolicy.reset();
+ }
+
+ void waitForTracerIdle() {
+ mDispatcher->waitForIdle();
+ mRequestTracerIdle();
+ }
+
+ void setFocusedWindow(const sp<gui::WindowInfoHandle>& window) {
+ gui::FocusRequest request;
+ request.token = window->getToken();
+ request.windowName = window->getName();
+ request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
+ request.displayId = window->getInfo()->displayId.val();
+ mDispatcher->setFocusedWindow(request);
+ }
+
+ void tapAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows,
+ Level inboundTraceLevel, Level dispatchTraceLevel, InputTraceSession& s) {
+ const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(down);
+ s.expectMotionTraced(inboundTraceLevel, toMotionEvent(down));
+ for (const auto& window : windows) {
+ auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window});
+ }
+
+ const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(up);
+ s.expectMotionTraced(inboundTraceLevel, toMotionEvent(up));
+ for (const auto& window : windows) {
+ auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window});
+ }
+ }
+
+ void keypressAndExpect(const std::vector<const sp<FakeWindowHandle>>& windows,
+ Level inboundTraceLevel, Level dispatchTraceLevel,
+ InputTraceSession& s) {
+ const auto down = KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build();
+ mDispatcher->notifyKey(down);
+ s.expectKeyTraced(inboundTraceLevel, toKeyEvent(down));
+ for (const auto& window : windows) {
+ auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_DOWN));
+ s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window});
+ }
+
+ const auto up = KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build();
+ mDispatcher->notifyKey(up);
+ s.expectKeyTraced(inboundTraceLevel, toKeyEvent(up));
+ for (const auto& window : windows) {
+ auto consumed = window->consumeKeyEvent(WithKeyAction(ACTION_UP));
+ s.expectDispatchTraced(dispatchTraceLevel, {*consumed, window});
+ }
+ }
+
+private:
+ std::function<void()> mRequestTracerIdle;
+};
+
+TEST_F(InputTracingTest, EmptyConfigTracesNothing) {
+ InputTraceSession s{[](auto& config) {}};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+ keypressAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, TraceAll) {
+ InputTraceSession s{
+ [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+ keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, NoRulesTracesNothing) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+ keypressAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, EmptyRuleMatchesEverything) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match everything as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+ keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, UnspecifiedTracelLevel) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match everything, trace level unspecified
+ auto rule = config->add_rules();
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ // Event is not traced by default if trace level is unspecified
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+ keypressAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MatchSecureWindow) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match secure windows as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->set_match_secure(true);
+ }};
+
+ // Add a normal window and a spy window.
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Since neither are secure windows, events should not be traced.
+ tapAndExpect({spy, window}, Level::NONE, Level::NONE, s);
+
+ // Events should be matched as secure if any of the target windows is marked as secure.
+ spy->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ spy->setSecure(false);
+ window->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ spy->setSecure(true);
+ window->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ spy->setSecure(false);
+ window->setSecure(false);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+ tapAndExpect({spy, window}, Level::NONE, Level::NONE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MatchImeConnectionActive) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match IME Connection Active as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->set_match_ime_connection_active(true);
+ }};
+
+ // Add a normal window and a spy window.
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ // Since IME connection is not active, events should not be traced.
+ tapAndExpect({spy, window}, Level::NONE, Level::NONE, s);
+
+ mDispatcher->setInputMethodConnectionIsActive(true);
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ mDispatcher->setInputMethodConnectionIsActive(false);
+ tapAndExpect({spy, window}, Level::NONE, Level::NONE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MatchAllPackages) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match all package as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->add_match_all_packages(ALLOWED_PKG_1);
+ rule->add_match_all_packages(ALLOWED_PKG_2);
+ }};
+
+ // All windows are allowlisted.
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, ALLOWED_UID_1);
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setOwnerInfo(PID, ALLOWED_UID_2);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ auto systemSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ systemSpy->setOwnerInfo(PID, gui::Uid{AID_SYSTEM});
+ systemSpy->setSpy(true);
+ systemSpy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged(
+ {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ // Add a disallowed spy. This will result in the event not being traced for all windows.
+ auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1);
+ disallowedSpy->setSpy(true);
+ disallowedSpy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(),
+ *disallowedSpy->getInfo(), *window->getInfo()},
+ {},
+ 0,
+ 0});
+
+ tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s);
+
+ // Change the owner of the disallowed spy to one for which we don't have a package mapping.
+ disallowedSpy->setOwnerInfo(PID, UNLISTED_UID);
+ mDispatcher->onWindowInfosChanged({{*systemSpy->getInfo(), *spy->getInfo(),
+ *disallowedSpy->getInfo(), *window->getInfo()},
+ {},
+ 0,
+ 0});
+
+ tapAndExpect({systemSpy, spy, disallowedSpy, window}, Level::NONE, Level::NONE, s);
+
+ // Remove the disallowed spy. Events are traced again.
+ mDispatcher->onWindowInfosChanged(
+ {{*systemSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({systemSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MatchAnyPackages) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match any package as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->add_match_any_packages(ALLOWED_PKG_1);
+ rule->add_match_any_packages(ALLOWED_PKG_2);
+ }};
+
+ // Just a disallowed window. Events are not traced.
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, DISALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ // Add a spy for which we don't have a package mapping. Events are still not traced.
+ auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ disallowedSpy->setOwnerInfo(PID, UNLISTED_UID);
+ disallowedSpy->setSpy(true);
+ disallowedSpy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s);
+
+ // Add an allowed spy. Events are now traced for all packages.
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setOwnerInfo(PID, ALLOWED_UID_1);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged(
+ {{*disallowedSpy->getInfo(), *spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({disallowedSpy, spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ // Add another disallowed spy. Events are still traced.
+ auto disallowedSpy2 = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ disallowedSpy2->setOwnerInfo(PID, DISALLOWED_UID_2);
+ disallowedSpy2->setSpy(true);
+ disallowedSpy2->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *disallowedSpy2->getInfo(),
+ *spy->getInfo(), *window->getInfo()},
+ {},
+ 0,
+ 0});
+
+ tapAndExpect({disallowedSpy, disallowedSpy2, spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MultipleMatchersInOneRule) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Match all of the following conditions as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->add_match_all_packages(ALLOWED_PKG_1);
+ rule->add_match_all_packages(ALLOWED_PKG_2);
+ rule->add_match_any_packages(ALLOWED_PKG_1);
+ rule->add_match_any_packages(DISALLOWED_PKG_1);
+ rule->set_match_secure(false);
+ rule->set_match_ime_connection_active(false);
+ }};
+
+ // A single window into an allowed UID. Matches all matchers.
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, ALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ // Secure window does not match.
+ window->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ // IME Connection Active does not match.
+ window->setSecure(false);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ mDispatcher->setInputMethodConnectionIsActive(true);
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ // Event going to DISALLOWED_PKG_1 does not match because it's not listed in match_all_packages.
+ mDispatcher->setInputMethodConnectionIsActive(false);
+ auto disallowedSpy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ disallowedSpy->setOwnerInfo(PID, DISALLOWED_UID_1);
+ disallowedSpy->setSpy(true);
+ disallowedSpy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*disallowedSpy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({disallowedSpy, window}, Level::NONE, Level::NONE, s);
+
+ // Event going to ALLOWED_PKG_1 does not match because it's not listed in match_any_packages.
+ window->setOwnerInfo(PID, ALLOWED_UID_2);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({window}, Level::NONE, Level::NONE, s);
+
+ // All conditions match.
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setOwnerInfo(PID, ALLOWED_UID_1);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, MultipleRulesMatchInOrder) {
+ InputTraceSession s{[](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Don't trace secure events
+ auto rule1 = config->add_rules();
+ rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_NONE);
+ rule1->set_match_secure(true);
+ // Rule: Trace matched packages as COMPLETE when IME inactive
+ auto rule2 = config->add_rules();
+ rule2->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule2->add_match_all_packages(ALLOWED_PKG_1);
+ rule2->add_match_all_packages(ALLOWED_PKG_2);
+ rule2->set_match_ime_connection_active(false);
+ // Rule: Trace the rest of the events as REDACTED
+ auto rule3 = config->add_rules();
+ rule3->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED);
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, ALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ // Verify that the first rule that matches in the order that they are specified is the
+ // one that applies to the event.
+ mDispatcher->setInputMethodConnectionIsActive(true);
+ tapAndExpect({window}, Level::REDACTED, Level::REDACTED, s);
+
+ mDispatcher->setInputMethodConnectionIsActive(false);
+ auto spy = sp<FakeWindowHandle>::make(APP, mDispatcher, "Spy", DISPLAY_ID);
+ spy->setOwnerInfo(PID, ALLOWED_UID_2);
+ spy->setSpy(true);
+ spy->setTrustedOverlay(true);
+ spy->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({spy, window}, Level::NONE, Level::NONE, s);
+
+ spy->setSecure(false);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({spy, window}, Level::COMPLETE, Level::COMPLETE, s);
+
+ spy->setOwnerInfo(PID, DISALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
+
+ tapAndExpect({spy, window}, Level::REDACTED, Level::REDACTED, s);
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, TraceInboundEvents) {
+ InputTraceSession s{[](auto& config) {
+ // Only trace inbounds events - don't trace window dispatch
+ config->set_trace_dispatcher_input_events(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Trace everything as REDACTED
+ auto rule1 = config->add_rules();
+ rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED);
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, ALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // Only the inbound events are traced. No dispatch events are traced.
+ tapAndExpect({window}, Level::REDACTED, Level::NONE, s);
+
+ // Notify a down event, which should be traced.
+ const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ s.expectMotionTraced(Level::REDACTED, toMotionEvent(down));
+ mDispatcher->notifyMotion(down);
+ auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ s.expectDispatchTraced(Level::NONE, {*consumed, window});
+
+ // Force a cancel event to be synthesized. This should not be traced, because only inbound
+ // events are requested.
+ mDispatcher->cancelCurrentTouch();
+ consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ s.expectMotionTraced(Level::NONE, *consumed);
+ s.expectDispatchTraced(Level::NONE, {*consumed, window});
+
+ waitForTracerIdle();
+}
+
+TEST_F(InputTracingTest, TraceWindowDispatch) {
+ InputTraceSession s{[](auto& config) {
+ // Only trace window dispatch - don't trace event details
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Trace everything as REDACTED
+ auto rule1 = config->add_rules();
+ rule1->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED);
+ }};
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ window->setOwnerInfo(PID, ALLOWED_UID_1);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+
+ // Only dispatch events are traced. No inbound events are traced.
+ tapAndExpect({window}, Level::NONE, Level::REDACTED, s);
+
+ // Notify a down event; the dispatch should be traced.
+ const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ s.expectMotionTraced(Level::NONE, toMotionEvent(down));
+ mDispatcher->notifyMotion(down);
+ auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ s.expectDispatchTraced(Level::REDACTED, {*consumed, window});
+
+ // Force a cancel event to be synthesized. All events that are dispatched should be traced.
+ mDispatcher->cancelCurrentTouch();
+ consumed = window->consumeMotionEvent(WithMotionAction(ACTION_CANCEL));
+ s.expectMotionTraced(Level::NONE, *consumed);
+ s.expectDispatchTraced(Level::REDACTED, {*consumed, window});
+
+ waitForTracerIdle();
+}
+
+// TODO(b/336097719): Investigate flakiness and re-enable this test.
+TEST_F(InputTracingTest, DISABLED_SimultaneousTracingSessions) {
+ auto s1 = std::make_unique<InputTraceSession>(
+ [](auto& config) { config->set_mode(AndroidInputEventConfig::TRACE_MODE_TRACE_ALL); });
+
+ auto window = sp<FakeWindowHandle>::make(APP, mDispatcher, "Window", DISPLAY_ID);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+ window->consumeFocusEvent(true);
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1);
+ keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1);
+
+ auto s2 = std::make_unique<InputTraceSession>([](auto& config) {
+ config->set_trace_dispatcher_input_events(true);
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Trace all events as REDACTED when IME inactive
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_REDACTED);
+ rule->set_match_ime_connection_active(false);
+ });
+
+ auto s3 = std::make_unique<InputTraceSession>([](auto& config) {
+ // Only trace window dispatch
+ config->set_trace_dispatcher_window_dispatch(true);
+ config->set_mode(AndroidInputEventConfig::TRACE_MODE_USE_RULES);
+ // Rule: Trace non-secure events as COMPLETE
+ auto rule = config->add_rules();
+ rule->set_trace_level(AndroidInputEventConfig::TRACE_LEVEL_COMPLETE);
+ rule->set_match_secure(false);
+ });
+
+ // Down event should be recorded on all traces.
+ const auto down = MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(down);
+ s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(down));
+ s2->expectMotionTraced(Level::REDACTED, toMotionEvent(down));
+ s3->expectMotionTraced(Level::NONE, toMotionEvent(down));
+ auto consumed = window->consumeMotionEvent(WithMotionAction(ACTION_DOWN));
+ s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+ s2->expectDispatchTraced(Level::REDACTED, {*consumed, window});
+ s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+
+ // Move event when IME is active.
+ mDispatcher->setInputMethodConnectionIsActive(true);
+ const auto move1 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(move1);
+ s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move1));
+ s2->expectMotionTraced(Level::NONE, toMotionEvent(move1));
+ s3->expectMotionTraced(Level::NONE, toMotionEvent(move1));
+ consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+ s2->expectDispatchTraced(Level::NONE, {*consumed, window});
+ s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+
+ // Move event after window became secure.
+ mDispatcher->setInputMethodConnectionIsActive(false);
+ window->setSecure(true);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ const auto move2 = MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(move2);
+ s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(move2));
+ s2->expectMotionTraced(Level::REDACTED, toMotionEvent(move2));
+ s3->expectMotionTraced(Level::NONE, toMotionEvent(move2));
+ consumed = window->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
+ s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+ s2->expectDispatchTraced(Level::REDACTED, {*consumed, window});
+ s3->expectDispatchTraced(Level::NONE, {*consumed, window});
+
+ waitForTracerIdle();
+ s2.reset();
+
+ // Up event.
+ window->setSecure(false);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ const auto up = MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(100).y(110))
+ .build();
+ mDispatcher->notifyMotion(up);
+ s1->expectMotionTraced(Level::COMPLETE, toMotionEvent(up));
+ s3->expectMotionTraced(Level::NONE, toMotionEvent(up));
+ consumed = window->consumeMotionEvent(WithMotionAction(ACTION_UP));
+ s1->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+ s3->expectDispatchTraced(Level::COMPLETE, {*consumed, window});
+
+ waitForTracerIdle();
+ s3.reset();
+
+ tapAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1);
+ keypressAndExpect({window}, Level::COMPLETE, Level::COMPLETE, *s1);
+
+ waitForTracerIdle();
+ s1.reset();
+}
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index db89168..6389cdc 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -28,7 +28,6 @@
#include <EventHub.h>
#include <InputReaderBase.h>
#include <NotifyArgs.h>
-#include <PointerControllerInterface.h>
#include <StylusState.h>
#include <VibrationElement.h>
#include <android-base/logging.h>
@@ -54,10 +53,6 @@
MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode),
(override));
- MOCK_METHOD(void, fadePointer, (), (override));
- MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, getPointerController,
- (int32_t deviceId), (override));
-
MOCK_METHOD(void, requestTimeoutAtTime, (nsecs_t when), (override));
int32_t bumpGeneration() override { return ++mGeneration; }
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index b44529b..031b77d 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -72,8 +72,6 @@
}
void testPointerVisibilityForKeys(const std::vector<int32_t>& keyCodes, bool expectVisible) {
- EXPECT_CALL(mMockInputReaderContext, fadePointer)
- .Times(expectVisible ? 0 : keyCodes.size());
for (int32_t keyCode : keyCodes) {
process(EV_KEY, keyCode, 1);
process(EV_SYN, SYN_REPORT, 0);
@@ -84,7 +82,6 @@
void testTouchpadTapStateForKeys(const std::vector<int32_t>& keyCodes,
const bool expectPrevent) {
- EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).Times(keyCodes.size());
if (expectPrevent) {
EXPECT_CALL(mMockInputReaderContext, setPreventingTouchpadTaps(true))
.Times(keyCodes.size());
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 6606de8..01fd03e 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -20,7 +20,6 @@
#include <android-base/properties.h>
#include <binder/Binder.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <inttypes.h>
#include <linux/input.h>
#include <log/log.h>
@@ -44,7 +43,7 @@
identifier.product = productId;
auto info = InputDeviceInfo();
info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device",
- /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+ /*isExternal=*/false, /*hasMic=*/false, ui::ADISPLAY_ID_NONE);
return info;
}
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index d726385..437020f 100644
--- a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -35,7 +35,7 @@
using testing::SetArgPointee;
using testing::VariantWith;
-static constexpr int32_t DISPLAY_ID = 0;
+static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
static constexpr int32_t DISPLAY_WIDTH = 480;
static constexpr int32_t DISPLAY_HEIGHT = 800;
static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
diff --git a/services/inputflinger/tests/NotifyArgs_test.cpp b/services/inputflinger/tests/NotifyArgs_test.cpp
index 1536756..2e5ecc3 100644
--- a/services/inputflinger/tests/NotifyArgs_test.cpp
+++ b/services/inputflinger/tests/NotifyArgs_test.cpp
@@ -36,7 +36,7 @@
nsecs_t readTime = downTime++;
int32_t deviceId = 7;
uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
- int32_t displayId = 42;
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId{42};
uint32_t policyFlags = POLICY_FLAG_GESTURE;
int32_t action = AMOTION_EVENT_ACTION_HOVER_MOVE;
int32_t actionButton = AMOTION_EVENT_BUTTON_PRIMARY;
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 3b2565e..33e7277 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -15,7 +15,8 @@
*/
#include "../PointerChoreographer.h"
-
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <deque>
#include <vector>
@@ -27,6 +28,8 @@
namespace android {
+namespace input_flags = com::android::input::flags;
+
using ControllerType = PointerControllerInterface::ControllerType;
using testing::AllOf;
@@ -42,8 +45,9 @@
constexpr int32_t DEVICE_ID = 3;
constexpr int32_t SECOND_DEVICE_ID = DEVICE_ID + 1;
-constexpr int32_t DISPLAY_ID = 5;
-constexpr int32_t ANOTHER_DISPLAY_ID = 10;
+constexpr int32_t THIRD_DEVICE_ID = SECOND_DEVICE_ID + 1;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId{5};
+constexpr ui::LogicalDisplayId ANOTHER_DISPLAY_ID = ui::LogicalDisplayId{10};
constexpr int32_t DISPLAY_WIDTH = 480;
constexpr int32_t DISPLAY_HEIGHT = 800;
constexpr auto DRAWING_TABLET_SOURCE = AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS;
@@ -59,7 +63,7 @@
.axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20);
static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source,
- int32_t associatedDisplayId) {
+ ui::LogicalDisplayId associatedDisplayId) {
InputDeviceIdentifier identifier;
auto info = InputDeviceInfo();
@@ -69,7 +73,7 @@
return info;
}
-static std::vector<DisplayViewport> createViewports(std::vector<int32_t> displayIds) {
+static std::vector<DisplayViewport> createViewports(std::vector<ui::LogicalDisplayId> displayIds) {
std::vector<DisplayViewport> viewports;
for (auto displayId : displayIds) {
DisplayViewport viewport;
@@ -120,7 +124,7 @@
"reference to this PointerController";
}
- void assertPointerDisplayIdNotified(int32_t displayId) {
+ void assertPointerDisplayIdNotified(ui::LogicalDisplayId displayId) {
ASSERT_EQ(displayId, mPointerDisplayIdNotified);
mPointerDisplayIdNotified.reset();
}
@@ -130,7 +134,7 @@
private:
std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
mCreatedControllers;
- std::optional<int32_t> mPointerDisplayIdNotified;
+ std::optional<ui::LogicalDisplayId> mPointerDisplayIdNotified;
std::shared_ptr<PointerControllerInterface> createPointerController(
ControllerType type) override {
@@ -140,7 +144,8 @@
return pc;
}
- void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
+ void notifyPointerDisplayIdChanged(ui::LogicalDisplayId displayId,
+ const FloatPoint& position) override {
mPointerDisplayIdNotified = displayId;
}
};
@@ -197,13 +202,15 @@
TEST_F(PointerChoreographerTest, WhenMouseIsAddedCreatesPointerController) {
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
}
TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) {
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the mouse.
@@ -214,7 +221,7 @@
TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) {
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}});
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_NONE)}});
assertPointerControllerNotCreated();
}
@@ -248,7 +255,8 @@
// For a mouse event without a target display, default viewport should be set for
// the PointerController.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
ASSERT_TRUE(pc->isPointerShown());
@@ -260,7 +268,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
ASSERT_TRUE(firstDisplayPc->isPointerShown());
@@ -279,7 +288,8 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -288,7 +298,8 @@
TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) {
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -300,12 +311,13 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerDisplayIdNotified(ui::ADISPLAY_ID_NONE);
assertPointerControllerRemoved(pc);
}
@@ -316,7 +328,8 @@
// Set one viewport as a default mouse display ID.
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -332,7 +345,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -344,7 +358,7 @@
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(MOUSE_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
// Check that the PointerController updated the position and the pointer is shown.
@@ -360,7 +374,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -375,7 +390,7 @@
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(absoluteMousePointer)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
// Check that the PointerController updated the position and the pointer is shown.
@@ -397,7 +412,7 @@
// Add two devices, one unassociated and the other associated with non-default mouse display.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
@@ -433,7 +448,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -443,7 +459,8 @@
// Assume that pointer capture is enabled.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/1,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE, ADISPLAY_ID_NONE)}});
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE_RELATIVE,
+ ui::ADISPLAY_ID_NONE)}});
mChoreographer.notifyPointerCaptureChanged(
NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
@@ -458,7 +475,7 @@
.axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
.axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20))
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
// Check that there's no update on the PointerController.
@@ -467,7 +484,8 @@
// Check x-y coordinates, displayId and cursor position are not changed.
mTestListener.assertNotifyMotionWasCalled(
- AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20), WithDisplayId(ADISPLAY_ID_NONE),
+ AllOf(WithCoords(10, 20), WithRelativeMotion(10, 20),
+ WithDisplayId(ui::ADISPLAY_ID_NONE),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION)));
}
@@ -476,7 +494,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -495,7 +514,8 @@
// A mouse is connected, and the pointer is shown.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
@@ -505,15 +525,17 @@
// Add a second mouse is added, the pointer is shown again.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
- generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::ADISPLAY_ID_NONE)}});
ASSERT_TRUE(pc->isPointerShown());
// One of the mice is removed, and it does not cause the mouse pointer to fade, because
// we have one more mouse connected.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::ADISPLAY_ID_NONE)}});
assertPointerControllerNotRemoved(pc);
ASSERT_TRUE(pc->isPointerShown());
@@ -526,7 +548,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
@@ -536,8 +559,9 @@
// Adding a touchscreen device does not unfade the mouse pointer.
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
- generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
DISPLAY_ID)}});
ASSERT_FALSE(pc->isPointerShown());
@@ -548,6 +572,73 @@
ASSERT_FALSE(pc->isPointerShown());
}
+TEST_F(PointerChoreographerTest, DisabledMouseConnected) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ InputDeviceInfo mouseDeviceInfo =
+ generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE);
+ // Disable this mouse device.
+ mouseDeviceInfo.setEnabled(false);
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {mouseDeviceInfo}});
+
+ // Disabled mouse device should not create PointerController
+ assertPointerControllerNotCreated();
+}
+
+TEST_F(PointerChoreographerTest, MouseDeviceDisableLater) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ InputDeviceInfo mouseDeviceInfo =
+ generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE);
+
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {mouseDeviceInfo}});
+
+ auto pc = assertPointerControllerCreated(PointerControllerInterface::ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Now we disable this mouse device
+ mouseDeviceInfo.setEnabled(false);
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {mouseDeviceInfo}});
+
+ // Because the mouse device disabled, the PointerController should be removed.
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, MultipleEnabledAndDisabledMiceConnectionAndRemoval) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ InputDeviceInfo disabledMouseDeviceInfo =
+ generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE);
+ disabledMouseDeviceInfo.setEnabled(false);
+
+ InputDeviceInfo enabledMouseDeviceInfo =
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE);
+
+ InputDeviceInfo anotherEnabledMouseDeviceInfo =
+ generateTestDeviceInfo(THIRD_DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {disabledMouseDeviceInfo, enabledMouseDeviceInfo, anotherEnabledMouseDeviceInfo}});
+
+ // Mouse should show, because we have two enabled mice device.
+ auto pc = assertPointerControllerCreated(PointerControllerInterface::ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Now we remove one of enabled mice device.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {disabledMouseDeviceInfo, enabledMouseDeviceInfo}});
+
+ // Because we still have an enabled mouse device, pointer should still show.
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // We finally remove last enabled mouse device.
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {disabledMouseDeviceInfo}});
+
+ // The PointerController should be removed, because there is no enabled mouse device.
+ assertPointerControllerRemoved(pc);
+}
+
TEST_F(PointerChoreographerTest, WhenShowTouchesEnabledAndDisabledDoesNotCreatePointerController) {
// Disable show touches and add a touch device.
mChoreographer.setShowTouchesEnabled(false);
@@ -1086,7 +1177,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
}
@@ -1094,7 +1185,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the touchpad.
@@ -1135,7 +1226,8 @@
// For a touchpad event without a target display, default viewport should be set for
// the PointerController.
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
}
@@ -1148,7 +1240,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
@@ -1166,7 +1258,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -1177,7 +1269,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -1191,12 +1283,12 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerDisplayIdNotified(ui::ADISPLAY_ID_NONE);
assertPointerControllerRemoved(pc);
}
@@ -1210,11 +1302,11 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
- // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
+ // Set another viewport as a default mouse display ID. ui::ADISPLAY_ID_NONE will be notified
// before a touchpad event.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
@@ -1229,7 +1321,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1241,7 +1333,7 @@
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(TOUCHPAD_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
// Check that the PointerController updated the position and the pointer is shown.
@@ -1259,7 +1351,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1273,7 +1365,7 @@
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(-100).y(0))
.classification(MotionClassification::MULTI_FINGER_SWIPE)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
@@ -1287,7 +1379,7 @@
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(0).y(0))
.classification(MotionClassification::MULTI_FINGER_SWIPE)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -1304,7 +1396,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(100).y(0))
.classification(MotionClassification::MULTI_FINGER_SWIPE)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -1319,7 +1411,7 @@
.pointer(PointerBuilder(/*id=*/2, ToolType::FINGER).x(110).y(10))
.classification(MotionClassification::MULTI_FINGER_SWIPE)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
@@ -1338,7 +1430,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE),
+ ui::ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ANOTHER_DISPLAY_ID)}});
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1377,7 +1469,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1394,7 +1486,7 @@
mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD)
.pointer(FIRST_TOUCH_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
// Check that there's no update on the PointerController.
@@ -1403,7 +1495,7 @@
// Check x-y coordinates, displayId and cursor position are not changed.
mTestListener.assertNotifyMotionWasCalled(
- AllOf(WithCoords(100, 200), WithDisplayId(ADISPLAY_ID_NONE),
+ AllOf(WithCoords(100, 200), WithDisplayId(ui::ADISPLAY_ID_NONE),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION)));
}
@@ -1414,7 +1506,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -1432,7 +1524,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1446,7 +1539,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1461,7 +1555,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1476,7 +1571,8 @@
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertCustomPointerIconNotSet();
@@ -1499,7 +1595,7 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
@@ -1518,6 +1614,76 @@
firstMousePc->assertPointerIconNotSet();
}
+TEST_F_WITH_FLAGS(PointerChoreographerTest, HidesTouchSpotsOnMirroredDisplaysForSecureWindow,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch events to create PointerController
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // By default touch indicators should not be hidden
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // adding secure window on display should set flag to hide pointer indicators on corresponding
+ // mirrored display
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/true);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // removing the secure window should reset the state
+ windowInfo.inputConfig.clear(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY);
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+}
+
+TEST_F_WITH_FLAGS(PointerChoreographerTest,
+ DoesNotHidesTouchSpotsOnMirroredDisplaysForInvisibleWindow,
+ REQUIRES_FLAGS_ENABLED(
+ ACONFIG_FLAG(input_flags, hide_pointer_indicators_for_secure_windows))) {
+ // Add a touch device and enable show touches.
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+
+ // Emit touch events to create PointerController
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // By default touch indicators should not be hidden
+ auto pc = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+
+ // adding secure but hidden window on display should still not set flag to hide pointer
+ // indicators
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_VISIBLE;
+ mChoreographer.onWindowInfosChanged({windowInfo});
+ pc->assertIsHiddenOnMirroredDisplays(DISPLAY_ID, /*isHidden=*/false);
+ pc->assertIsHiddenOnMirroredDisplays(ANOTHER_DISPLAY_ID, /*isHidden=*/false);
+}
+
TEST_P(StylusTestFixture, SetsPointerIconForStylus) {
const auto& [name, source, controllerType] = GetParam();
@@ -1624,14 +1790,14 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, source, DISPLAY_ID)}});
mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
mChoreographer.notifyMotion(
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(MOUSE_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::ADISPLAY_ID_NONE)
.build());
auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
@@ -1659,7 +1825,7 @@
mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE),
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
@@ -1715,7 +1881,8 @@
// Hide the pointer on the display, and then connect the mouse.
mChoreographer.setPointerIconVisibility(DISPLAY_ID, false);
mChoreographer.notifyInputDevicesChanged(
- {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}});
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ui::ADISPLAY_ID_NONE)}});
auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, mousePc->getDisplayId());
@@ -1733,7 +1900,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::ADISPLAY_ID_NONE)}});
auto touchpadPc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, touchpadPc->getDisplayId());
@@ -1772,4 +1939,109 @@
ASSERT_FALSE(pc->isPointerShown());
}
+TEST_F(PointerChoreographerTest, DrawingTabletCanReportMouseEvent) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE)}});
+ // There should be no controller created when a drawing tablet is connected
+ assertPointerControllerNotCreated();
+
+ // But if it ends up reporting a mouse event, then the mouse controller will be created
+ // dynamically.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // The controller is removed when the drawing tablet is removed
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, MultipleDrawingTabletsReportMouseEvents) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // First drawing tablet is added
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotCreated();
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Second drawing tablet is added
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, DRAWING_TABLET_SOURCE,
+ ui::ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotRemoved(pc);
+
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(SECOND_DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // First drawing tablet is removed
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE)}});
+ assertPointerControllerNotRemoved(pc);
+
+ // Second drawing tablet is removed
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}});
+ assertPointerControllerRemoved(pc);
+}
+
+TEST_F(PointerChoreographerTest, MouseAndDrawingTabletReportMouseEvents) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+
+ // Mouse and drawing tablet connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::ADISPLAY_ID_NONE)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Drawing tablet reports a mouse event
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, DRAWING_TABLET_SOURCE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // Remove the mouse device
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, DRAWING_TABLET_SOURCE, ui::ADISPLAY_ID_NONE)}});
+
+ // The mouse controller should not be removed, because the drawing tablet has produced a
+ // mouse event, so we are treating it as a mouse too.
+ assertPointerControllerNotRemoved(pc);
+
+ mChoreographer.notifyInputDevicesChanged({/*id=*/0, {}});
+ assertPointerControllerRemoved(pc);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
index 9818176..c273e92 100644
--- a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
+++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
@@ -15,7 +15,6 @@
*/
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include "../PreferStylusOverTouchBlocker.h"
namespace android {
@@ -64,8 +63,9 @@
}
// Define a valid motion event.
- NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, deviceId, source, /*displayId=*/0,
- POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0,
+ NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, deviceId, source,
+ /*displayId=*/ui::ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, action,
+ /* actionButton */ 0,
/*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
@@ -439,7 +439,7 @@
InputDeviceInfo stylusDevice;
stylusDevice.initialize(STYLUS_DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1,
/*identifier=*/{}, "stylus device", /*external=*/false,
- /*hasMic=*/false, ADISPLAY_ID_NONE);
+ /*hasMic=*/false, ui::ADISPLAY_ID_NONE);
notifyInputDevicesChanged({stylusDevice});
// The touchscreen device was removed, so we no longer remember anything about it. We should
// again start blocking touch events from it.
diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index a3e8eaf..65fb9c6 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -145,7 +145,7 @@
class WithDisplayIdMatcher {
public:
using is_gtest_matcher = void;
- explicit WithDisplayIdMatcher(int32_t displayId) : mDisplayId(displayId) {}
+ explicit WithDisplayIdMatcher(ui::LogicalDisplayId displayId) : mDisplayId(displayId) {}
bool MatchAndExplain(const NotifyMotionArgs& args, std::ostream*) const {
return mDisplayId == args.displayId;
@@ -164,10 +164,10 @@
void DescribeNegationTo(std::ostream* os) const { *os << "wrong display id"; }
private:
- const int32_t mDisplayId;
+ const ui::LogicalDisplayId mDisplayId;
};
-inline WithDisplayIdMatcher WithDisplayId(int32_t displayId) {
+inline WithDisplayIdMatcher WithDisplayId(ui::LogicalDisplayId displayId) {
return WithDisplayIdMatcher(displayId);
}
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index a92dce5..245497c 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -19,9 +19,7 @@
#include <android-base/logging.h>
#include <gtest/gtest.h>
-#include <com_android_input_flags.h>
#include <thread>
-#include "FakePointerController.h"
#include "InputMapperTest.h"
#include "InterfaceMocks.h"
#include "TestEventMatchers.h"
@@ -39,17 +37,15 @@
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER;
constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT;
-constexpr int32_t DISPLAY_ID = 0;
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::ADISPLAY_ID_DEFAULT;
constexpr int32_t DISPLAY_WIDTH = 480;
constexpr int32_t DISPLAY_HEIGHT = 800;
constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
-namespace input_flags = com::android::input::flags;
-
/**
* Unit tests for TouchpadInputMapper.
*/
-class TouchpadInputMapperTestBase : public InputMapperUnitTest {
+class TouchpadInputMapperTest : public InputMapperUnitTest {
protected:
void SetUp() override {
InputMapperUnitTest::SetUp();
@@ -117,18 +113,7 @@
return base::ResultError("Axis not supported", NAME_NOT_FOUND);
});
createDevice();
- mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration,
- isPointerChoreographerEnabled());
- }
-
- virtual bool isPointerChoreographerEnabled() { return false; }
-};
-
-class TouchpadInputMapperTest : public TouchpadInputMapperTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(false);
- TouchpadInputMapperTestBase::SetUp();
+ mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
}
};
@@ -139,66 +124,6 @@
* but only after the button is released.
*/
TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) {
- std::list<NotifyArgs> args;
-
- args += process(EV_ABS, ABS_MT_TRACKING_ID, 1);
- args += process(EV_KEY, BTN_TOUCH, 1);
- setScanCodeState(KeyState::DOWN, {BTN_TOOL_FINGER});
- args += process(EV_KEY, BTN_TOOL_FINGER, 1);
- args += process(EV_ABS, ABS_MT_POSITION_X, 50);
- args += process(EV_ABS, ABS_MT_POSITION_Y, 50);
- args += process(EV_ABS, ABS_MT_PRESSURE, 1);
- args += process(EV_SYN, SYN_REPORT, 0);
- ASSERT_THAT(args, testing::IsEmpty());
-
- // Without this sleep, the test fails.
- // TODO(b/284133337): Figure out whether this can be removed
- std::this_thread::sleep_for(std::chrono::milliseconds(20));
-
- args += process(EV_KEY, BTN_LEFT, 1);
- setScanCodeState(KeyState::DOWN, {BTN_LEFT});
- args += process(EV_SYN, SYN_REPORT, 0);
-
- args += process(EV_KEY, BTN_LEFT, 0);
- setScanCodeState(KeyState::UP, {BTN_LEFT});
- args += process(EV_SYN, SYN_REPORT, 0);
- ASSERT_THAT(args,
- ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)),
- VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER))));
-
- // Liftoff
- args.clear();
- args += process(EV_ABS, ABS_MT_PRESSURE, 0);
- args += process(EV_ABS, ABS_MT_TRACKING_ID, -1);
- args += process(EV_KEY, BTN_TOUCH, 0);
- setScanCodeState(KeyState::UP, {BTN_TOOL_FINGER});
- args += process(EV_KEY, BTN_TOOL_FINGER, 0);
- args += process(EV_SYN, SYN_REPORT, 0);
- ASSERT_THAT(args, testing::IsEmpty());
-}
-
-class TouchpadInputMapperTestWithChoreographer : public TouchpadInputMapperTestBase {
-protected:
- void SetUp() override { TouchpadInputMapperTestBase::SetUp(); }
-
- bool isPointerChoreographerEnabled() override { return true; }
-};
-
-// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
-// logic can be removed.
-/**
- * Start moving the finger and then click the left touchpad button. Check whether HOVER_EXIT is
- * generated when hovering stops. Currently, it is not.
- * In the current implementation, HOVER_MOVE and ACTION_DOWN events are not sent out right away,
- * but only after the button is released.
- */
-TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) {
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
/*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
index 78f7291..80430d1 100644
--- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
+++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp
@@ -18,7 +18,6 @@
#include <android-base/silent_death_test.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
#include <linux/input.h>
#include <thread>
#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h"
@@ -89,7 +88,8 @@
// Define a valid motion event.
NotifyMotionArgs args(/*id=*/0, eventTime, /*readTime=*/0, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
- /*displayId=*/0, POLICY_FLAG_PASS_TO_USER, action, /*actionButton=*/0,
+ /*displayId=*/ui::ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, action,
+ /*actionButton=*/0,
/*flags=*/0, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
pointerCoords, /*xPrecision=*/0, /*yPrecision=*/0,
@@ -104,7 +104,7 @@
auto info = InputDeviceInfo();
info.initialize(DEVICE_ID, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias",
- /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
+ /*isExternal=*/false, /*hasMic=*/false, ui::ADISPLAY_ID_NONE);
info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat=*/0,
/*fuzz=*/0, X_RESOLUTION);
@@ -434,7 +434,7 @@
TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) {
// Create a basic key event and send to blocker
NotifyKeyArgs args(/*sequenceNum=*/1, /*eventTime=*/2, /*readTime=*/21, /*deviceId=*/3,
- AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, /*policyFlags=*/0,
+ AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_DEFAULT, /*policyFlags=*/0,
AKEY_EVENT_ACTION_DOWN, /*flags=*/4, AKEYCODE_HOME, /*scanCode=*/5,
AMETA_NONE, /*downTime=*/6);
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
index 81c3353..48e1954 100644
--- a/services/inputflinger/tests/fuzzers/Android.bp
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -178,7 +178,12 @@
shared_libs: [
"libinputreporter",
],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
srcs: [
+ ":inputdispatcher_common_test_sources",
"InputDispatcherFuzzer.cpp",
],
}
diff --git a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
index 885820f..812969b 100644
--- a/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
+++ b/services/inputflinger/tests/fuzzers/FuzzedInputStream.h
@@ -178,7 +178,7 @@
pointerCoords.push_back(coords);
}
- const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1);
+ const ui::LogicalDisplayId displayId{fdp.ConsumeIntegralInRange<int32_t>(0, maxDisplays - 1)};
const int32_t deviceId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DEVICES - 1);
// Current time +- 5 seconds
diff --git a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
index deb811d..0446d76 100644
--- a/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputClassifierFuzzer.cpp
@@ -54,7 +54,7 @@
mClassifier->notifyKey({/*sequenceNum=*/fdp.ConsumeIntegral<int32_t>(),
eventTime, readTime,
/*deviceId=*/fdp.ConsumeIntegral<int32_t>(),
- AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT,
+ AINPUT_SOURCE_KEYBOARD, ui::ADISPLAY_ID_DEFAULT,
/*policyFlags=*/fdp.ConsumeIntegral<uint32_t>(),
AKEY_EVENT_ACTION_DOWN,
/*flags=*/fdp.ConsumeIntegral<int32_t>(), AKEYCODE_HOME,
diff --git a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
index dc5a213..79a5ff6 100644
--- a/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputDispatcherFuzzer.cpp
@@ -18,7 +18,7 @@
#include <fuzzer/FuzzedDataProvider.h>
#include "../FakeApplicationHandle.h"
#include "../FakeInputDispatcherPolicy.h"
-#include "../FakeWindowHandle.h"
+#include "../FakeWindows.h"
#include "FuzzedInputStream.h"
#include "dispatcher/InputDispatcher.h"
#include "input/InputVerifier.h"
@@ -88,8 +88,9 @@
} // namespace
-sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, InputDispatcher& dispatcher,
- int32_t displayId) {
+sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp,
+ std::unique_ptr<InputDispatcher>& dispatcher,
+ ui::LogicalDisplayId displayId) {
static size_t windowNumber = 0;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++);
@@ -100,10 +101,11 @@
return window;
}
-void randomizeWindows(
- std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay,
- FuzzedDataProvider& fdp, InputDispatcher& dispatcher) {
- const int32_t displayId = fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1);
+void randomizeWindows(std::unordered_map<ui::LogicalDisplayId, std::vector<sp<FakeWindowHandle>>>&
+ windowsPerDisplay,
+ FuzzedDataProvider& fdp, std::unique_ptr<InputDispatcher>& dispatcher) {
+ const ui::LogicalDisplayId displayId{
+ fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1)};
std::vector<sp<FakeWindowHandle>>& windows = windowsPerDisplay[displayId];
fdp.PickValueInArray<std::function<void()>>({
@@ -142,12 +144,12 @@
NotifyStreamProvider streamProvider(fdp);
FakeInputDispatcherPolicy fakePolicy;
- InputDispatcher dispatcher(fakePolicy);
- dispatcher.setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
+ auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false);
// Start InputDispatcher thread
- dispatcher.start();
+ dispatcher->start();
- std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay;
+ std::unordered_map<ui::LogicalDisplayId, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay;
// Randomly invoke InputDispatcher api's until randomness is exhausted.
while (fdp.remaining_bytes() > 0) {
@@ -155,7 +157,7 @@
[&]() -> void {
std::optional<NotifyMotionArgs> motion = streamProvider.nextMotion();
if (motion) {
- dispatcher.notifyMotion(*motion);
+ dispatcher->notifyMotion(*motion);
}
},
[&]() -> void {
@@ -169,7 +171,7 @@
}
}
- dispatcher.onWindowInfosChanged(
+ dispatcher->onWindowInfosChanged(
{windowInfos, {}, /*vsyncId=*/0, /*timestamp=*/0});
},
// Consume on all the windows
@@ -187,7 +189,7 @@
})();
}
- dispatcher.stop();
+ dispatcher->stop();
return 0;
}
diff --git a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
index 9223287..34ea54c 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -119,7 +119,7 @@
return reader->getSensors(deviceId);
}
- bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) {
+ bool canDispatchToDisplay(int32_t deviceId, ui::LogicalDisplayId displayId) {
return reader->canDispatchToDisplay(deviceId, displayId);
}
@@ -241,7 +241,8 @@
},
[&]() -> void {
reader->canDispatchToDisplay(fdp->ConsumeIntegral<int32_t>(),
- fdp->ConsumeIntegral<int32_t>());
+ ui::LogicalDisplayId{
+ fdp->ConsumeIntegral<int32_t>()});
},
[&]() -> void {
reader->getKeyCodeForKeyLocation(fdp->ConsumeIntegral<int32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 7898126..25f2f2e 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -258,56 +258,16 @@
void sysfsNodeChanged(const std::string& sysfsNodePath) override {}
};
-class FuzzPointerController : public PointerControllerInterface {
- std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
-
-public:
- FuzzPointerController(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
- ~FuzzPointerController() {}
- std::optional<FloatRect> getBounds() const override {
- if (mFdp->ConsumeBool()) {
- return {};
- } else {
- return FloatRect{mFdp->ConsumeFloatingPoint<float>(),
- mFdp->ConsumeFloatingPoint<float>(),
- mFdp->ConsumeFloatingPoint<float>(),
- mFdp->ConsumeFloatingPoint<float>()};
- }
- }
- void move(float deltaX, float deltaY) override {}
- void setPosition(float x, float y) override {}
- FloatPoint getPosition() const override {
- return {mFdp->ConsumeFloatingPoint<float>(), mFdp->ConsumeFloatingPoint<float>()};
- }
- void fade(Transition transition) override {}
- void unfade(Transition transition) override {}
- void setPresentation(Presentation presentation) override {}
- void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) override {}
- void clearSpots() override {}
- int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); }
- void setDisplayViewport(const DisplayViewport& displayViewport) override {}
- void updatePointerIcon(PointerIconStyle iconId) override {}
- void setCustomPointerIcon(const SpriteIcon& icon) override {}
- std::string dump() override { return ""; }
-};
-
class FuzzInputReaderPolicy : public InputReaderPolicyInterface {
TouchAffineTransformation mTransform;
- std::shared_ptr<FuzzPointerController> mPointerController;
std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;
protected:
~FuzzInputReaderPolicy() {}
public:
- FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {
- mPointerController = std::make_shared<FuzzPointerController>(mFdp);
- }
+ FuzzInputReaderPolicy(std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp) : mFdp(mFdp) {}
void getReaderConfiguration(InputReaderConfiguration* outConfig) override {}
- std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) override {
- return mPointerController;
- }
void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) override {}
std::shared_ptr<KeyCharacterMap> getKeyboardLayoutOverlay(
const InputDeviceIdentifier& identifier,
@@ -325,7 +285,7 @@
void notifyStylusGestureStarted(int32_t, nsecs_t) {}
bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); }
std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
- int32_t associatedDisplayId) override {
+ ui::LogicalDisplayId associatedDisplayId) override {
return {};
}
};
@@ -359,10 +319,6 @@
bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override {
return mFdp->ConsumeBool();
}
- void fadePointer() override {}
- std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) override {
- return mPolicy->obtainPointerController(0);
- }
void requestTimeoutAtTime(nsecs_t when) override {}
int32_t bumpGeneration() override { return mFdp->ConsumeIntegral<int32_t>(); }
void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override {}
diff --git a/services/sensorservice/SensorEventConnection.cpp b/services/sensorservice/SensorEventConnection.cpp
index dc86577..3446f58 100644
--- a/services/sensorservice/SensorEventConnection.cpp
+++ b/services/sensorservice/SensorEventConnection.cpp
@@ -461,16 +461,18 @@
// is pre-Q, still permit delivering events to the app even if permission isn't granted
// (since this permission was only introduced in Q)
if ((event.type == SENSOR_TYPE_STEP_COUNTER || event.type == SENSOR_TYPE_STEP_DETECTOR) &&
- mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+ mTargetSdk > 0 && mTargetSdk <= __ANDROID_API_P__) {
+ success = true;
+ } else if (mUid == AID_SYSTEM) {
+ // Allow access if it is requested from system.
success = true;
} else {
int32_t sensorHandle = event.sensor;
String16 noteMsg("Sensor event (");
noteMsg.append(String16(mService->getSensorStringType(sensorHandle)));
noteMsg.append(String16(")"));
- int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid,
- mOpPackageName, mAttributionTag,
- noteMsg);
+ int32_t appOpMode = mService->sAppOpsManager.noteOp(iter->second, mUid, mOpPackageName,
+ mAttributionTag, noteMsg);
success = (appOpMode == AppOpsManager::MODE_ALLOWED);
}
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index e1c43c6..69e4309 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -2302,11 +2302,16 @@
// requirement to hold the AR permission to access Step Counter and Step Detector events
// was introduced.
canAccess = true;
+ } else if (IPCThreadState::self()->getCallingUid() == AID_SYSTEM) {
+ // Allow access if it is requested from system.
+ canAccess = true;
} else if (hasPermissionForSensor(sensor)) {
- // Ensure that the AppOp is allowed, or that there is no necessary app op for the sensor
+ // Ensure that the AppOp is allowed, or that there is no necessary app op
+ // for the sensor
if (opCode >= 0) {
- const int32_t appOpMode = sAppOpsManager.checkOp(opCode,
- IPCThreadState::self()->getCallingUid(), opPackageName);
+ const int32_t appOpMode =
+ sAppOpsManager.checkOp(opCode, IPCThreadState::self()->getCallingUid(),
+ opPackageName);
canAccess = (appOpMode == AppOpsManager::MODE_ALLOWED);
} else {
canAccess = true;
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
index 8d43f79..f20b213 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -13,4 +13,11 @@
namespace: "sensors"
description: "This flag controls if the callback onDynamicSensorsDisconnected is implemented or not."
bug: "316958439"
-}
\ No newline at end of file
+}
+
+flag {
+ name: "sensor_event_connection_send_event_require_nonnull_scratch"
+ namespace: "sensors"
+ description: "This flag controls we allow to pass in nullptr as scratch in SensorEventConnection::sendEvents()"
+ bug: "339306599"
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index dc69b81..8ca796e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -158,6 +158,7 @@
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
+ "Display/DisplayModeController.cpp",
"Display/DisplaySnapshot.cpp",
"DisplayDevice.cpp",
"DisplayHardware/AidlComposerHal.cpp",
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
index 5a1ec6f..3cef875 100644
--- a/services/surfaceflinger/BackgroundExecutor.cpp
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -19,6 +19,9 @@
#define LOG_TAG "BackgroundExecutor"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <processgroup/sched_policy.h>
+#include <pthread.h>
+#include <sched.h>
#include <utils/Log.h>
#include <mutex>
@@ -26,14 +29,24 @@
namespace android {
-ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor);
+namespace {
-BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
+void set_thread_priority(bool highPriority) {
+ set_sched_policy(0, highPriority ? SP_FOREGROUND : SP_BACKGROUND);
+ struct sched_param param = {0};
+ param.sched_priority = highPriority ? 2 : 0 /* must be 0 for non-RT */;
+ sched_setscheduler(gettid(), highPriority ? SCHED_FIFO : SCHED_NORMAL, ¶m);
+}
+
+} // anonymous namespace
+
+BackgroundExecutor::BackgroundExecutor(bool highPriority) {
// mSemaphore must be initialized before any calls to
// BackgroundExecutor::sendCallbacks. For this reason, we initialize it
// within the constructor instead of within mThread.
LOG_ALWAYS_FATAL_IF(sem_init(&mSemaphore, 0, 0), "sem_init failed");
- mThread = std::thread([&]() {
+ mThread = std::thread([&, highPriority]() {
+ set_thread_priority(highPriority);
while (!mDone) {
LOG_ALWAYS_FATAL_IF(sem_wait(&mSemaphore), "sem_wait failed (%d)", errno);
auto callbacks = mCallbacksQueue.pop();
@@ -45,6 +58,11 @@
}
}
});
+ if (highPriority) {
+ pthread_setname_np(mThread.native_handle(), "BckgrndExec HP");
+ } else {
+ pthread_setname_np(mThread.native_handle(), "BckgrndExec LP");
+ }
}
BackgroundExecutor::~BackgroundExecutor() {
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
index 66b7d7a..1b5fadd 100644
--- a/services/surfaceflinger/BackgroundExecutor.h
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -18,7 +18,6 @@
#include <ftl/small_vector.h>
#include <semaphore.h>
-#include <utils/Singleton.h>
#include <thread>
#include "LocklessQueue.h"
@@ -26,10 +25,20 @@
namespace android {
// Executes tasks off the main thread.
-class BackgroundExecutor : public Singleton<BackgroundExecutor> {
+class BackgroundExecutor {
public:
- BackgroundExecutor();
~BackgroundExecutor();
+
+ static BackgroundExecutor& getInstance() {
+ static BackgroundExecutor instance(true);
+ return instance;
+ }
+
+ static BackgroundExecutor& getLowPriorityInstance() {
+ static BackgroundExecutor instance(false);
+ return instance;
+ }
+
using Callbacks = ftl::SmallVector<std::function<void()>, 10>;
// Queues callbacks onto a work queue to be executed by a background thread.
// This is safe to call from multiple threads.
@@ -37,6 +46,8 @@
void flushQueue();
private:
+ BackgroundExecutor(bool highPriority);
+
sem_t mSemaphore;
std::atomic_bool mDone = false;
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index a52cc87..7fa58df 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -11,6 +11,7 @@
cc_defaults {
name: "libcompositionengine_defaults",
defaults: [
+ "aconfig_lib_cc_static_link.defaults",
"android.hardware.graphics.composer3-ndk_shared",
"android.hardware.power-ndk_shared",
"librenderengine_deps",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 843b5c5..dd0f985 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -34,12 +34,6 @@
using Layers = std::vector<sp<compositionengine::LayerFE>>;
using Outputs = std::vector<std::shared_ptr<compositionengine::Output>>;
-struct BorderRenderInfo {
- float width = 0;
- half4 color;
- std::vector<int32_t> layerIds;
-};
-
// Interface of composition engine power hint callback.
struct ICEPowerCallback {
virtual void notifyCpuLoadUp() = 0;
@@ -101,8 +95,6 @@
// TODO (b/255601557): Calculate per display.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
- std::vector<BorderRenderInfo> borderInfoList;
-
bool hasTrustedPresentationListener = false;
ICEPowerCallback* powerCallback = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index f1d6f52..d420838 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -321,8 +321,11 @@
const Region& flashRegion,
std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0;
virtual void setExpensiveRenderingExpected(bool enabled) = 0;
+ virtual void setHintSessionGpuStart(TimePoint startTime) = 0;
virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0;
+ virtual void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) = 0;
virtual bool isPowerHintSessionEnabled() = 0;
+ virtual bool isPowerHintSessionGpuReportingEnabled() = 0;
virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0;
virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 2dc9a1a..d87968f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -93,7 +93,10 @@
private:
bool isPowerHintSessionEnabled() override;
+ bool isPowerHintSessionGpuReportingEnabled() override;
+ void setHintSessionGpuStart(TimePoint startTime) override;
void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
+ void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
DisplayId mId;
bool mIsDisconnected = false;
Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index 911d67b..adcbbb9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -144,8 +144,11 @@
std::vector<LayerFE*>& outLayerFEs) override;
void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
+ void setHintSessionGpuStart(TimePoint startTime) override;
void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override;
+ void setHintSessionRequiresRenderEngine(bool requiresRenderEngine) override;
bool isPowerHintSessionEnabled() override;
+ bool isPowerHintSessionGpuReportingEnabled() override;
void dumpBase(std::string&) const;
// Implemented by the final implementation for the final state it uses.
@@ -162,7 +165,6 @@
private:
void dirtyEntireOutput();
- void updateCompositionStateForBorder(const compositionengine::CompositionRefreshArgs&);
compositionengine::OutputLayer* findLayerRequestingBackgroundComposition() const;
void finishPrepareFrame();
ui::Dataspace getBestDataspace(ui::Dataspace*, bool*) const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 6b1c318..f8ffde1 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -34,7 +34,6 @@
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/ProjectionSpace.h>
-#include <renderengine/BorderRenderInfo.h>
#include <ui/LayerStack.h>
#include <ui/Rect.h>
#include <ui/Region.h>
@@ -166,8 +165,6 @@
bool treat170mAsSrgb = false;
- std::vector<renderengine::BorderRenderInfo> borderInfoList;
-
uint64_t lastOutputLayerHash = 0;
uint64_t outputLayerHash = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
index e2d17ee..86bcf20 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h
@@ -143,7 +143,7 @@
compositionengine::OutputLayer* getBlurLayer() const;
- bool hasUnsupportedDataspace() const;
+ bool hasKnownColorShift() const;
bool hasProtectedLayers() const;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index dc3821c..5e3e3d8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -74,6 +74,7 @@
BlurRegions = 1u << 18,
HasProtectedContent = 1u << 19,
CachingHint = 1u << 20,
+ DimmingEnabled = 1u << 21,
};
// clang-format on
@@ -248,6 +249,10 @@
ui::Dataspace getDataspace() const { return mOutputDataspace.get(); }
+ hardware::graphics::composer::hal::PixelFormat getPixelFormat() const {
+ return mPixelFormat.get();
+ }
+
float getHdrSdrRatio() const {
return getOutputLayer()->getLayerFE().getCompositionState()->currentHdrSdrRatio;
};
@@ -258,6 +263,8 @@
gui::CachingHint getCachingHint() const { return mCachingHint.get(); }
+ bool isDimmingEnabled() const { return mIsDimmingEnabled.get(); }
+
float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; }
void dump(std::string& result) const;
@@ -498,7 +505,10 @@
return std::vector<std::string>{toString(cachingHint)};
}};
- static const constexpr size_t kNumNonUniqueFields = 19;
+ OutputLayerState<bool, LayerStateField::DimmingEnabled> mIsDimmingEnabled{
+ [](auto layer) { return layer->getLayerFE().getCompositionState()->dimmingEnabled; }};
+
+ static const constexpr size_t kNumNonUniqueFields = 20;
std::array<StateInterface*, kNumNonUniqueFields> getNonUniqueFields() {
std::array<const StateInterface*, kNumNonUniqueFields> constFields =
@@ -516,7 +526,7 @@
&mAlpha, &mLayerMetadata, &mVisibleRegion, &mOutputDataspace,
&mPixelFormat, &mColorTransform, &mCompositionType, &mSidebandStream,
&mBuffer, &mSolidColor, &mBackgroundBlurRadius, &mBlurRegions,
- &mFrameNumber, &mIsProtected, &mCachingHint};
+ &mFrameNumber, &mIsProtected, &mCachingHint, &mIsDimmingEnabled};
}
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 95ea3a4..3f3deae 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -134,8 +134,11 @@
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
MOCK_METHOD1(setPredictCompositionStrategy, void(bool));
MOCK_METHOD1(setTreat170mAsSrgb, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuStart, (TimePoint startTime));
MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence));
+ MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine));
MOCK_METHOD(bool, isPowerHintSessionEnabled, ());
+ MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, ());
};
} // namespace android::compositionengine::mock
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6428d08..c18be7a 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -252,10 +252,6 @@
auto& hwc = getCompositionEngine().getHwComposer();
const bool requiresClientComposition = anyLayersRequireClientComposition();
- if (isPowerHintSessionEnabled()) {
- mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition);
- }
-
const TimePoint hwcValidateStartTime = TimePoint::now();
if (status_t result = hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition,
@@ -416,10 +412,24 @@
return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession();
}
+bool Display::isPowerHintSessionGpuReportingEnabled() {
+ return mPowerAdvisor != nullptr && mPowerAdvisor->supportsGpuReporting();
+}
+
+// For ADPF GPU v0 this is expected to set start time to when the GPU commands are submitted with
+// fence returned, i.e. when RenderEngine flushes the commands and returns the draw fence.
+void Display::setHintSessionGpuStart(TimePoint startTime) {
+ mPowerAdvisor->setGpuStartTime(mId, startTime);
+}
+
void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) {
mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence));
}
+void Display::setHintSessionRequiresRenderEngine(bool requiresRenderEngine) {
+ mPowerAdvisor->setRequiresRenderEngine(mId, requiresRenderEngine);
+}
+
void Display::finishFrame(GpuCompositionResult&& result) {
// We only need to actually compose the display if:
// 1) It is being handled by hardware composer, which may need this to
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7b5b49d..84f3f25 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -464,6 +464,10 @@
setColorTransform(refreshArgs);
beginFrame();
+ if (isPowerHintSessionEnabled()) {
+ // always reset the flag before the composition prediction
+ setHintSessionRequiresRenderEngine(false);
+ }
GpuCompositionResult result;
const bool predictCompositionStrategy = canPredictCompositionStrategy(refreshArgs);
if (predictCompositionStrategy) {
@@ -819,44 +823,6 @@
forceClientComposition = false;
}
}
-
- updateCompositionStateForBorder(refreshArgs);
-}
-
-void Output::updateCompositionStateForBorder(
- const compositionengine::CompositionRefreshArgs& refreshArgs) {
- std::unordered_map<int32_t, const Region*> layerVisibleRegionMap;
- // Store a map of layerId to their computed visible region.
- for (auto* layer : getOutputLayersOrderedByZ()) {
- int layerId = (layer->getLayerFE()).getSequence();
- layerVisibleRegionMap[layerId] = &((layer->getState()).visibleRegion);
- }
- OutputCompositionState& outputCompositionState = editState();
- outputCompositionState.borderInfoList.clear();
- bool clientComposeTopLayer = false;
- for (const auto& borderInfo : refreshArgs.borderInfoList) {
- renderengine::BorderRenderInfo info;
- for (const auto& id : borderInfo.layerIds) {
- info.combinedRegion.orSelf(*(layerVisibleRegionMap[id]));
- }
-
- if (!info.combinedRegion.isEmpty()) {
- info.width = borderInfo.width;
- info.color = borderInfo.color;
- outputCompositionState.borderInfoList.emplace_back(std::move(info));
- clientComposeTopLayer = true;
- }
- }
-
- // In this situation we must client compose the top layer instead of using hwc
- // because we want to draw the border above all else.
- // This could potentially cause a bit of a performance regression if the top
- // layer would have been rendered using hwc originally.
- // TODO(b/227656283): Measure system's performance before enabling the border feature
- if (clientComposeTopLayer) {
- auto topLayer = getOutputLayerOrderedByZByIndex(getOutputLayerCount() - 1);
- (topLayer->editState()).forceClientComposition = true;
- }
}
void Output::planComposition() {
@@ -1248,8 +1214,7 @@
if (!optReadyFence) {
return;
}
-
- if (isPowerHintSessionEnabled()) {
+ if (isPowerHintSessionEnabled() && !isPowerHintSessionGpuReportingEnabled()) {
// get fence end time to know when gpu is complete in display
setHintSessionGpuFence(
std::make_unique<FenceTime>(sp<Fence>::make(dup(optReadyFence->get()))));
@@ -1393,8 +1358,20 @@
// If rendering was not successful, remove the request from the cache.
mClientCompositionRequestCache->remove(tex->getBuffer()->getId());
}
-
const auto fence = std::move(fenceResult).value_or(Fence::NO_FENCE);
+ if (isPowerHintSessionEnabled()) {
+ if (fence != Fence::NO_FENCE && fence->isValid() &&
+ !outputCompositionState.reusedClientComposition) {
+ setHintSessionRequiresRenderEngine(true);
+ if (isPowerHintSessionGpuReportingEnabled()) {
+ // the order of the two calls here matters as we should check if the previously
+ // tracked fence has signaled first and archive the previous start time
+ setHintSessionGpuStart(TimePoint::now());
+ setHintSessionGpuFence(
+ std::make_unique<FenceTime>(sp<Fence>::make(dup(fence->get()))));
+ }
+ }
+ }
if (auto timeStats = getCompositionEngine().getTimeStats()) {
if (fence->isValid()) {
@@ -1444,13 +1421,6 @@
// Compute the global color transform matrix.
clientCompositionDisplay.colorTransform = outputState.colorTransformMatrix;
- for (auto& info : outputState.borderInfoList) {
- renderengine::BorderRenderInfo borderInfo;
- borderInfo.width = info.width;
- borderInfo.color = info.color;
- borderInfo.combinedRegion = info.combinedRegion;
- clientCompositionDisplay.borderInfoList.emplace_back(std::move(borderInfo));
- }
clientCompositionDisplay.deviceHandlesColorTransform =
outputState.usesDeviceComposition || getSkipColorTransform();
return clientCompositionDisplay;
@@ -1577,14 +1547,26 @@
// The base class does nothing with this call.
}
+void Output::setHintSessionGpuStart(TimePoint) {
+ // The base class does nothing with this call.
+}
+
void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) {
// The base class does nothing with this call.
}
+void Output::setHintSessionRequiresRenderEngine(bool) {
+ // The base class does nothing with this call.
+}
+
bool Output::isPowerHintSessionEnabled() {
return false;
}
+bool Output::isPowerHintSessionGpuReportingEnabled() {
+ return false;
+}
+
void Output::presentFrameAndReleaseLayers() {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
index 39cf671..6683e67 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp
@@ -67,11 +67,6 @@
out.append("\n ");
dumpVal(out, "treat170mAsSrgb", treat170mAsSrgb);
-
- out.append("\n");
- for (const auto& borderRenderInfo : borderInfoList) {
- dumpVal(out, "borderRegion", borderRenderInfo.combinedRegion);
- }
out.append("\n");
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index 1f53588..ea9442d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -27,8 +27,7 @@
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
#include <ui/DebugUtils.h>
-#include <utils/Trace.h>
-
+#include <ui/HdrRenderTypeUtils.h>
#include <utils/Trace.h>
namespace android::compositionengine::impl::planner {
@@ -306,7 +305,7 @@
return false;
}
- if (hasUnsupportedDataspace()) {
+ if (hasKnownColorShift()) {
return false;
}
@@ -366,12 +365,21 @@
return mBlurLayer ? mBlurLayer->getOutputLayer() : nullptr;
}
-bool CachedSet::hasUnsupportedDataspace() const {
+bool CachedSet::hasKnownColorShift() const {
return std::any_of(mLayers.cbegin(), mLayers.cend(), [](const Layer& layer) {
auto dataspace = layer.getState()->getDataspace();
- const auto transfer = static_cast<ui::Dataspace>(dataspace & ui::Dataspace::TRANSFER_MASK);
- if (transfer == ui::Dataspace::TRANSFER_ST2084 || transfer == ui::Dataspace::TRANSFER_HLG) {
- // Skip HDR.
+
+ // Layers are never dimmed when rendering a cached set, meaning that we may ask HWC to
+ // dim a cached set. But this means that we can never cache any HDR layers so that we
+ // don't accidentally dim those layers.
+ const auto hdrType = getHdrRenderType(dataspace, layer.getState()->getPixelFormat(),
+ layer.getState()->getHdrSdrRatio());
+ if (hdrType != HdrRenderType::SDR) {
+ return true;
+ }
+
+ // Layers that have dimming disabled pretend that they're HDR.
+ if (!layer.getState()->isDimmingEnabled()) {
return true;
}
@@ -380,10 +388,6 @@
// to avoid flickering/color differences.
return true;
}
- // TODO(b/274804887): temp fix of overdimming issue, skip caching if hsdr/sdr ratio > 1.01f
- if (layer.getState()->getHdrSdrRatio() > 1.01f) {
- return true;
- }
return false;
});
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 0a5c43a..4bafed2 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -439,7 +439,7 @@
if (!layerDeniedFromCaching && layerIsInactive &&
(firstLayer || runHasFirstLayer || !layerHasBlur) &&
- !currentSet->hasUnsupportedDataspace()) {
+ !currentSet->hasKnownColorShift()) {
if (isPartOfRun) {
builder.increment();
} else {
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 0e3fdbb..10dc927 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <common/FlagManager.h>
#include <compositionengine/impl/planner/LayerState.h>
namespace {
@@ -70,6 +71,10 @@
if (field->getField() == LayerStateField::Buffer) {
continue;
}
+ if (FlagManager::getInstance().cache_when_source_crop_layer_only_moved() &&
+ field->getField() == LayerStateField::SourceCrop) {
+ continue;
+ }
android::hashCombineSingleHashed(hash, field->getHash());
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 575a30e..4612117 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -126,6 +126,7 @@
const std::unordered_map<std::string, bool>&());
MOCK_CONST_METHOD1(dump, void(std::string&));
+ MOCK_CONST_METHOD1(dumpOverlayProperties, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
MOCK_METHOD(hal::HWDisplayId, getPrimaryHwcDisplayId, (), (const, override));
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index 7253354..ed2ffa9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -38,10 +38,12 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+ MOCK_METHOD(bool, supportsGpuReporting, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
+ MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
@@ -51,8 +53,8 @@
(DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
(override));
MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
- MOCK_METHOD(void, setRequiresClientComposition,
- (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine),
+ (override));
MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
(override));
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index ea1d8e8..0dc3c9f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2017,8 +2017,17 @@
MOCK_METHOD0(presentFrameAndReleaseLayers, void());
MOCK_METHOD1(renderCachedSets, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&));
+ MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool requiresRenderEngine),
+ (override));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, (), (override));
};
+ OutputPresentTest() {
+ EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
+ }
+
StrictMock<OutputPartialMock> mOutput;
};
@@ -2032,6 +2041,7 @@
EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(false));
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(false));
EXPECT_CALL(mOutput, prepareFrame());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
@@ -2052,6 +2062,7 @@
EXPECT_CALL(mOutput, writeCompositionState(Ref(args)));
EXPECT_CALL(mOutput, setColorTransform(Ref(args)));
EXPECT_CALL(mOutput, beginFrame());
+ EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(false));
EXPECT_CALL(mOutput, canPredictCompositionStrategy(Ref(args))).WillOnce(Return(true));
EXPECT_CALL(mOutput, prepareFrameAsync());
EXPECT_CALL(mOutput, devOptRepaintFlash(Ref(args)));
@@ -2989,6 +3000,10 @@
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
bool(base::unique_fd*, std::shared_ptr<renderengine::ExternalTexture>*));
+ MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence),
+ (override));
+ MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, (), (override));
};
OutputFinishFrameTest() {
@@ -2997,6 +3012,8 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
+ EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
}
StrictMock<OutputPartialMock> mOutput;
@@ -3023,6 +3040,22 @@
mOutput.finishFrame(std::move(result));
}
+TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFenceWithAdpfGpuOff) {
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillOnce(Return(false));
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, updateProtectedContentState());
+ EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
+
+ impl::GpuCompositionResult result;
+ mOutput.finishFrame(std::move(result));
+}
+
TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
mOutput.mState.isEnabled = true;
@@ -3031,6 +3064,7 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _)).WillOnce(Return(true));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
impl::GpuCompositionResult result;
@@ -3058,6 +3092,7 @@
.WillOnce(DoAll(SetArgPointee<1>(texture), Return(true)));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION), _, _))
.WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 2.f));
impl::GpuCompositionResult result;
@@ -3068,6 +3103,7 @@
mOutput.mState.isEnabled = true;
mOutput.mState.strategyPrediction = CompositionStrategyPredictionState::SUCCESS;
InSequence seq;
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
impl::GpuCompositionResult result;
@@ -3090,6 +3126,7 @@
composeSurfaces(RegionEq(Region::INVALID_REGION), result.buffer,
Eq(ByRef(result.fence))))
.WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
mOutput.finishFrame(std::move(result));
}
@@ -3415,9 +3452,12 @@
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<LayerFE::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
+ MOCK_METHOD(void, setHintSessionGpuStart, (TimePoint startTime), (override));
MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence),
(override));
+ MOCK_METHOD(void, setHintSessionRequiresRenderEngine, (bool), (override));
MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override));
+ MOCK_METHOD(bool, isPowerHintSessionGpuReportingEnabled, (), (override));
};
OutputComposeSurfacesTest() {
@@ -3447,6 +3487,8 @@
EXPECT_CALL(mCompositionEngine, getTimeStats()).WillRepeatedly(Return(mTimeStats.get()));
EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
.WillRepeatedly(ReturnRef(kHdrCapabilities));
+ EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
}
struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
@@ -3712,10 +3754,57 @@
EXPECT_FALSE(mOutput.mState.reusedClientComposition);
// We do not expect another call to draw layers.
+ EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(_)).Times(0);
+ EXPECT_CALL(mOutput, setHintSessionGpuStart(_)).Times(0);
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
verify().execute().expectAFenceWasReturned();
EXPECT_TRUE(mOutput.mState.reusedClientComposition);
}
+TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChangesWithAdpfGpuOff) {
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillOnce(Return(false));
+ LayerFE::LayerSettings r1;
+ LayerFE::LayerSettings r2;
+
+ r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+ r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, kDefaultOutputDataspace, _))
+ .WillRepeatedly(Return(std::vector<LayerFE::LayerSettings>{r1, r2}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+
+ const auto otherOutputBuffer = std::make_shared<
+ renderengine::impl::
+ ExternalTexture>(sp<GraphicBuffer>::make(), mRenderEngine,
+ renderengine::impl::ExternalTexture::Usage::READABLE |
+ renderengine::impl::ExternalTexture::Usage::WRITEABLE);
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
+ .WillOnce(Return(mOutputBuffer))
+ .WillOnce(Return(otherOutputBuffer));
+ base::unique_fd fd(open("/dev/null", O_RDONLY));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
+ .WillRepeatedly([&](const renderengine::DisplaySettings&,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&,
+ base::unique_fd&&) -> ftl::Future<FenceResult> {
+ return ftl::yield<FenceResult>(sp<Fence>::make(std::move(fd)));
+ });
+
+ EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(true));
+ EXPECT_CALL(mOutput, setHintSessionGpuStart(_)).Times(0);
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_)).Times(0);
+ verify().execute().expectAFenceWasReturned();
+ EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+
+ verify().execute().expectAFenceWasReturned();
+ EXPECT_FALSE(mOutput.mState.reusedClientComposition);
+}
+
TEST_F(OutputComposeSurfacesTest, clientCompositionIfBufferChanges) {
LayerFE::LayerSettings r1;
LayerFE::LayerSettings r2;
@@ -3740,14 +3829,18 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_))
.WillOnce(Return(mOutputBuffer))
.WillOnce(Return(otherOutputBuffer));
+ base::unique_fd fd(open("/dev/null", O_RDONLY));
EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAre(r1, r2), _, _))
.WillRepeatedly([&](const renderengine::DisplaySettings&,
const std::vector<renderengine::LayerSettings>&,
const std::shared_ptr<renderengine::ExternalTexture>&,
base::unique_fd&&) -> ftl::Future<FenceResult> {
- return ftl::yield<FenceResult>(Fence::NO_FENCE);
+ return ftl::yield<FenceResult>(sp<Fence>::make(std::move(fd)));
});
+ EXPECT_CALL(mOutput, setHintSessionRequiresRenderEngine(true));
+ EXPECT_CALL(mOutput, setHintSessionGpuStart(_));
+ EXPECT_CALL(mOutput, setHintSessionGpuFence(_));
verify().execute().expectAFenceWasReturned();
EXPECT_FALSE(mOutput.mState.reusedClientComposition);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index d2eff75..54ee0ef 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -1339,6 +1339,55 @@
EXPECT_EQ(nullptr, overrideBuffer3);
}
+TEST_F(FlattenerTest, flattenLayers_skipsLayersDisablingDimming) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // The third layer disables dimming, which means it should not be cached
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ mTestLayers[2]->layerFECompositionState.dimmingEnabled = false;
+ mTestLayers[2]->layerState->update(&mTestLayers[2]->outputLayer);
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _))
+ .WillOnce(Return(ByMove(ftl::yield<FenceResult>(Fence::NO_FENCE))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt, true);
+
+ EXPECT_NE(nullptr, overrideBuffer1);
+ EXPECT_EQ(overrideBuffer1, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+}
+
TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
auto& layerState1 = mTestLayers[0]->layerState;
const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 044917e..03758b3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "LayerStateTest"
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
+#include <common/include/common/test/FlagUtils.h>
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/planner/LayerState.h>
#include <compositionengine/mock/LayerFE.h>
@@ -26,6 +27,7 @@
#include <log/log.h>
#include "android/hardware_buffer.h"
+#include "com_android_graphics_surfaceflinger_flags.h"
#include "compositionengine/LayerFECompositionState.h"
#include <aidl/android/hardware/graphics/composer3/Composition.h>
@@ -304,6 +306,16 @@
EXPECT_EQ(Composition::CLIENT, mLayerState->getCompositionType());
}
+TEST_F(LayerStateTest, getHdrSdrRatio) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.currentHdrSdrRatio = 2.f;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_EQ(2.f, mLayerState->getHdrSdrRatio());
+}
+
TEST_F(LayerStateTest, updateCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
@@ -454,6 +466,9 @@
}
TEST_F(LayerStateTest, compareSourceCrop) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.sourceCrop = sFloatRectOne;
LayerFECompositionState layerFECompositionState;
@@ -1033,6 +1048,47 @@
EXPECT_TRUE(otherLayerState->compare(*mLayerState));
}
+TEST_F(LayerStateTest, updateDimmingEnabled) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.dimmingEnabled = true;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ EXPECT_TRUE(mLayerState->isDimmingEnabled());
+
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.dimmingEnabled = false;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ ftl::Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
+ EXPECT_EQ(ftl::Flags<LayerStateField>(LayerStateField::DimmingEnabled), updates);
+ EXPECT_FALSE(mLayerState->isDimmingEnabled());
+}
+
+TEST_F(LayerStateTest, compareDimmingEnabled) {
+ OutputLayerCompositionState outputLayerCompositionState;
+ LayerFECompositionState layerFECompositionState;
+ layerFECompositionState.dimmingEnabled = true;
+ setupMocksForLayer(mOutputLayer, *mLayerFE, outputLayerCompositionState,
+ layerFECompositionState);
+ mLayerState = std::make_unique<LayerState>(&mOutputLayer);
+ mock::OutputLayer newOutputLayer;
+ sp<mock::LayerFE> newLayerFE = sp<mock::LayerFE>::make();
+ LayerFECompositionState layerFECompositionStateTwo;
+ layerFECompositionStateTwo.dimmingEnabled = false;
+ setupMocksForLayer(newOutputLayer, *newLayerFE, outputLayerCompositionState,
+ layerFECompositionStateTwo);
+ auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
+
+ verifyNonUniqueDifferingFields(*mLayerState, *otherLayerState, LayerStateField::DimmingEnabled);
+
+ EXPECT_TRUE(mLayerState->compare(*otherLayerState));
+ EXPECT_TRUE(otherLayerState->compare(*mLayerState));
+}
+
TEST_F(LayerStateTest, dumpDoesNotCrash) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 35d0ffb..a1210b4 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -18,6 +18,9 @@
#undef LOG_TAG
#define LOG_TAG "PredictorTest"
+#include <common/include/common/test/FlagUtils.h>
+#include "com_android_graphics_surfaceflinger_flags.h"
+
#include <compositionengine/impl/planner/Predictor.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/OutputLayer.h>
@@ -127,6 +130,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInSingleLayer) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -158,6 +164,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_matchesSingleDifferenceInMultiLayerStack) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -304,6 +313,9 @@
}
TEST_F(LayerStackTest, getApproximateMatch_doesNotMatchMultipleApproximations) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -347,6 +359,9 @@
};
TEST_F(LayerStackTest, reorderingChangesNonBufferHash) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -467,6 +482,9 @@
}
TEST_F(PredictorTest, getPredictedPlan_recordCandidateAndRetrieveApproximateMatch) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
@@ -504,6 +522,9 @@
}
TEST_F(PredictorTest, recordMissedPlan_skipsApproximateMatch) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::
+ cache_when_source_crop_layer_only_moved,
+ false);
mock::OutputLayer outputLayerOne;
sp<mock::LayerFE> layerFEOne = sp<mock::LayerFE>::make();
OutputLayerCompositionState outputLayerCompositionStateOne{
diff --git a/services/surfaceflinger/Display/DisplayModeController.cpp b/services/surfaceflinger/Display/DisplayModeController.cpp
new file mode 100644
index 0000000..f093384
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "DisplayModeController"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "Display/DisplayModeController.h"
+#include "Display/DisplaySnapshot.h"
+
+#include <log/log.h>
+
+namespace android::display {
+
+void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
+ DisplayModeId activeModeId,
+ scheduler::RefreshRateSelector::Config config) {
+ const auto& snapshot = snapshotRef.get();
+ const auto displayId = snapshot.displayId();
+
+ mDisplays.emplace_or_replace(displayId, snapshotRef, snapshot.displayModes(), activeModeId,
+ config);
+}
+
+void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
+ const bool ok = mDisplays.erase(displayId);
+ ALOGE_IF(!ok, "%s: Unknown display %s", __func__, to_string(displayId).c_str());
+}
+
+auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) -> RefreshRateSelectorPtr {
+ return mDisplays.get(displayId)
+ .transform([](const Display& display) { return display.selectorPtr; })
+ .value_or(nullptr);
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
new file mode 100644
index 0000000..b6a6bee
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -0,0 +1,73 @@
+/*
+ * 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 <memory>
+#include <utility>
+
+#include <android-base/thread_annotations.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
+#include "Display/DisplaySnapshotRef.h"
+#include "DisplayHardware/DisplayMode.h"
+#include "Scheduler/RefreshRateSelector.h"
+#include "ThreadContext.h"
+
+namespace android::display {
+
+// Selects the DisplayMode of each physical display, in accordance with DisplayManager policy and
+// certain heuristic signals.
+class DisplayModeController {
+public:
+ // The referenced DisplaySnapshot must outlive the registration.
+ void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
+ REQUIRES(kMainThreadContext);
+ void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+
+ // TODO(b/241285876): Remove once ownership is no longer shared with DisplayDevice.
+ using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
+
+ // Returns `nullptr` if the display is no longer registered (or never was).
+ RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+
+ // Used by tests to inject an existing RefreshRateSelector.
+ // TODO(b/241285876): Remove this.
+ void registerDisplay(PhysicalDisplayId displayId, DisplaySnapshotRef snapshotRef,
+ RefreshRateSelectorPtr selectorPtr) {
+ mDisplays.emplace_or_replace(displayId, snapshotRef, selectorPtr);
+ }
+
+private:
+ struct Display {
+ Display(DisplaySnapshotRef snapshot, RefreshRateSelectorPtr selectorPtr)
+ : snapshot(snapshot), selectorPtr(std::move(selectorPtr)) {}
+
+ Display(DisplaySnapshotRef snapshot, DisplayModes modes, DisplayModeId activeModeId,
+ scheduler::RefreshRateSelector::Config config)
+ : Display(snapshot,
+ std::make_shared<scheduler::RefreshRateSelector>(std::move(modes),
+ activeModeId, config)) {}
+
+ const DisplaySnapshotRef snapshot;
+ const RefreshRateSelectorPtr selectorPtr;
+ };
+
+ ui::PhysicalDisplayMap<PhysicalDisplayId, Display> mDisplays;
+};
+
+} // namespace android::display
diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/services/surfaceflinger/Display/DisplaySnapshotRef.h
similarity index 75%
copy from libs/gui/aidl/android/gui/LayerDebugInfo.aidl
copy to services/surfaceflinger/Display/DisplaySnapshotRef.h
index faca980..6cc5f7e 100644
--- a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl
+++ b/services/surfaceflinger/Display/DisplaySnapshotRef.h
@@ -14,6 +14,14 @@
* limitations under the License.
*/
-package android.gui;
+#pragma once
-parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h";
+#include <functional>
+
+namespace android::display {
+
+class DisplaySnapshot;
+
+using DisplaySnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/PhysicalDisplay.h b/services/surfaceflinger/Display/PhysicalDisplay.h
index ef36234..9b1f1ed 100644
--- a/services/surfaceflinger/Display/PhysicalDisplay.h
+++ b/services/surfaceflinger/Display/PhysicalDisplay.h
@@ -25,6 +25,7 @@
#include <utils/StrongPointer.h>
#include "DisplaySnapshot.h"
+#include "DisplaySnapshotRef.h"
namespace android::display {
@@ -45,8 +46,7 @@
// Transformers for PhysicalDisplays::get.
- using SnapshotRef = std::reference_wrapper<const DisplaySnapshot>;
- SnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
+ DisplaySnapshotRef snapshotRef() const { return std::cref(mSnapshot); }
bool isInternal() const {
return mSnapshot.connectionType() == ui::DisplayConnectionType::Internal;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 62f8fb1..3bf0eaa 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -137,7 +137,7 @@
auto DisplayDevice::getFrontEndInfo() const -> frontend::DisplayInfo {
gui::DisplayInfo info;
- info.displayId = getLayerStack().id;
+ info.displayId = ui::LogicalDisplayId{static_cast<int32_t>(getLayerStack().id)};
// The physical orientation is set when the orientation of the display panel is
// different than the default orientation of the device. Other services like
@@ -560,10 +560,8 @@
return DesiredModeAction::InitiateRenderRateSwitch;
}
- // Set the render frame rate to the active physical refresh rate to schedule the next
- // frame as soon as possible.
setActiveMode(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
- activeMode.modePtr->getVsyncRate());
+ activeMode.modePtr->getPeakFps());
// Initiate a mode change.
mDesiredModeOpt = std::move(desiredMode);
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 543a16a..c2d09c9 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -364,7 +364,6 @@
hardware::graphics::composer::hal::PowerMode initialPowerMode{
hardware::graphics::composer::hal::PowerMode::OFF};
bool isPrimary{false};
- DisplayModeId activeModeId;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
Fps requestedRefreshRate;
};
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 776bcd3..3cfb9ca 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -77,9 +77,7 @@
using aidl::android::hardware::graphics::common::HdrConversionStrategy;
using aidl::android::hardware::graphics::composer3::Capability;
using aidl::android::hardware::graphics::composer3::DisplayCapability;
-using aidl::android::hardware::graphics::composer3::VrrConfig;
using namespace std::string_literals;
-namespace hal = android::hardware::graphics::composer::hal;
namespace android {
@@ -964,8 +962,45 @@
return mSupportedLayerGenericMetadata;
}
+void HWComposer::dumpOverlayProperties(std::string& result) const {
+ // dump overlay properties
+ result.append("OverlayProperties:\n");
+ base::StringAppendF(&result, "supportMixedColorSpaces: %d\n",
+ mOverlayProperties.supportMixedColorSpaces);
+ base::StringAppendF(&result, "SupportedBufferCombinations(%zu entries)\n",
+ mOverlayProperties.combinations.size());
+ for (const auto& combination : mOverlayProperties.combinations) {
+ result.append(" pixelFormats=\n");
+ for (const auto& pixelFormat : combination.pixelFormats) {
+ base::StringAppendF(&result, " %s (%d)\n",
+ decodePixelFormat(static_cast<PixelFormat>(pixelFormat)).c_str(),
+ static_cast<uint32_t>(pixelFormat));
+ }
+ result.append(" standards=\n");
+ for (const auto& standard : combination.standards) {
+ base::StringAppendF(&result, " %s (%d)\n",
+ decodeStandardOnly(static_cast<uint32_t>(standard)).c_str(),
+ static_cast<uint32_t>(standard));
+ }
+ result.append(" transfers=\n");
+ for (const auto& transfer : combination.transfers) {
+ base::StringAppendF(&result, " %s (%d)\n",
+ decodeTransferOnly(static_cast<uint32_t>(transfer)).c_str(),
+ static_cast<uint32_t>(transfer));
+ }
+ result.append(" ranges=\n");
+ for (const auto& range : combination.ranges) {
+ base::StringAppendF(&result, " %s (%d)\n",
+ decodeRangeOnly(static_cast<uint32_t>(range)).c_str(),
+ static_cast<uint32_t>(range));
+ }
+ result.append("\n");
+ }
+}
+
void HWComposer::dump(std::string& result) const {
result.append(mComposer->dumpDebugInfo());
+ dumpOverlayProperties(result);
}
std::optional<PhysicalDisplayId> HWComposer::toPhysicalDisplayId(
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 7fbbb1a..bc32cda 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -269,6 +269,8 @@
virtual void dump(std::string& out) const = 0;
+ virtual void dumpOverlayProperties(std::string& out) const = 0;
+
virtual Hwc2::Composer* getComposer() const = 0;
// Returns the first display connected at boot. Its connection via HWComposer::onHotplug,
@@ -468,6 +470,7 @@
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
+ void dumpOverlayProperties(std::string& out) const override;
Hwc2::Composer* getComposer() const override { return mComposer.get(); }
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index faa5197..6c1a813 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -46,10 +46,21 @@
namespace impl {
using aidl::android::hardware::power::Boost;
+using aidl::android::hardware::power::ChannelConfig;
using aidl::android::hardware::power::Mode;
using aidl::android::hardware::power::SessionHint;
using aidl::android::hardware::power::SessionTag;
using aidl::android::hardware::power::WorkDuration;
+using aidl::android::hardware::power::WorkDurationFixedV1;
+
+using aidl::android::hardware::common::fmq::MQDescriptor;
+using aidl::android::hardware::common::fmq::SynchronizedReadWrite;
+using aidl::android::hardware::power::ChannelMessage;
+using android::hardware::EventFlag;
+
+using ChannelMessageContents = ChannelMessage::ChannelMessageContents;
+using MsgQueue = android::AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>;
+using FlagQueue = android::AidlMessageQueue<int8_t, SynchronizedReadWrite>;
PowerAdvisor::~PowerAdvisor() = default;
@@ -140,15 +151,7 @@
if (!mBootFinished.load()) {
return;
}
- if (usePowerHintSession()) {
- std::lock_guard lock(mHintSessionMutex);
- if (ensurePowerHintSessionRunning()) {
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_UP);
- if (!ret.isOk()) {
- mHintSession = nullptr;
- }
- }
- }
+ sendHintSessionHint(SessionHint::CPU_LOAD_UP);
}
void PowerAdvisor::notifyDisplayUpdateImminentAndCpuReset() {
@@ -160,15 +163,7 @@
if (mSendUpdateImminent.exchange(false)) {
ALOGV("AIDL notifyDisplayUpdateImminentAndCpuReset");
- if (usePowerHintSession()) {
- std::lock_guard lock(mHintSessionMutex);
- if (ensurePowerHintSessionRunning()) {
- auto ret = mHintSession->sendHint(SessionHint::CPU_LOAD_RESET);
- if (!ret.isOk()) {
- mHintSession = nullptr;
- }
- }
- }
+ sendHintSessionHint(SessionHint::CPU_LOAD_RESET);
if (!mHasDisplayUpdateImminent) {
ALOGV("Skipped sending DISPLAY_UPDATE_IMMINENT because HAL doesn't support it");
@@ -206,7 +201,32 @@
}
bool PowerAdvisor::shouldCreateSessionWithConfig() {
- return mSessionConfigSupported && FlagManager::getInstance().adpf_use_fmq_channel();
+ return mSessionConfigSupported && mBootFinished &&
+ FlagManager::getInstance().adpf_use_fmq_channel();
+}
+
+void PowerAdvisor::sendHintSessionHint(SessionHint hint) {
+ if (!mBootFinished || !usePowerHintSession()) {
+ ALOGV("Power hint session is not enabled, skip sending session hint");
+ return;
+ }
+ ATRACE_CALL();
+ if (sTraceHintSessionData) ATRACE_INT("Session hint", static_cast<int>(hint));
+ {
+ std::scoped_lock lock(mHintSessionMutex);
+ if (!ensurePowerHintSessionRunning()) {
+ ALOGV("Hint session not running and could not be started, skip sending session hint");
+ return;
+ }
+ ALOGV("Sending session hint: %d", static_cast<int>(hint));
+ if (!writeHintSessionMessage<ChannelMessageContents::Tag::hint>(&hint, 1)) {
+ auto ret = mHintSession->sendHint(hint);
+ if (!ret.isOk()) {
+ ALOGW("Failed to send session hint with error: %s", ret.errorMessage());
+ mHintSession = nullptr;
+ }
+ }
+ }
}
bool PowerAdvisor::ensurePowerHintSessionRunning() {
@@ -220,6 +240,9 @@
&mSessionConfig);
if (ret.isOk()) {
mHintSession = ret.value();
+ if (FlagManager::getInstance().adpf_use_fmq_channel_fixed()) {
+ setUpFmq();
+ }
}
// If it fails the first time we try, or ever returns unsupported, assume unsupported
else if (mFirstConfigSupportCheck || ret.isUnsupported()) {
@@ -240,9 +263,36 @@
return mHintSession != nullptr;
}
+void PowerAdvisor::setUpFmq() {
+ auto&& channelRet = getPowerHal().getSessionChannel(getpid(), static_cast<int32_t>(getuid()));
+ if (!channelRet.isOk()) {
+ ALOGE("Failed to get session channel with error: %s", channelRet.errorMessage());
+ return;
+ }
+ auto& channelConfig = channelRet.value();
+ mMsgQueue = std::make_unique<MsgQueue>(std::move(channelConfig.channelDescriptor), true);
+ LOG_ALWAYS_FATAL_IF(!mMsgQueue->isValid(), "Failed to set up hint session msg queue");
+ LOG_ALWAYS_FATAL_IF(channelConfig.writeFlagBitmask <= 0,
+ "Invalid flag bit masks found in channel config: writeBitMask(%d)",
+ channelConfig.writeFlagBitmask);
+ mFmqWriteMask = static_cast<uint32_t>(channelConfig.writeFlagBitmask);
+ if (!channelConfig.eventFlagDescriptor.has_value()) {
+ // For FMQ v1 in Android 15 we will force using shared event flag since the default
+ // no-op FMQ impl in Power HAL v5 will always return a valid channel config with
+ // non-zero masks but no shared flag.
+ mMsgQueue = nullptr;
+ ALOGE("No event flag descriptor found in channel config");
+ return;
+ }
+ mFlagQueue = std::make_unique<FlagQueue>(std::move(*channelConfig.eventFlagDescriptor), true);
+ LOG_ALWAYS_FATAL_IF(!mFlagQueue->isValid(), "Failed to set up hint session flag queue");
+ auto status = EventFlag::createEventFlag(mFlagQueue->getEventFlagWord(), &mEventFlag);
+ LOG_ALWAYS_FATAL_IF(status != OK, "Failed to set up hint session event flag");
+}
+
void PowerAdvisor::updateTargetWorkDuration(Duration targetDuration) {
- if (!usePowerHintSession()) {
- ALOGV("Power hint session target duration cannot be set, skipping");
+ if (!mBootFinished || !usePowerHintSession()) {
+ ALOGV("Power hint session is not enabled, skipping target update");
return;
}
ATRACE_CALL();
@@ -250,10 +300,15 @@
mTargetDuration = targetDuration;
if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration.ns());
if (targetDuration == mLastTargetDurationSent) return;
- std::lock_guard lock(mHintSessionMutex);
- if (ensurePowerHintSessionRunning()) {
- ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
- mLastTargetDurationSent = targetDuration;
+ std::scoped_lock lock(mHintSessionMutex);
+ if (!ensurePowerHintSessionRunning()) {
+ ALOGV("Hint session not running and could not be started, skip updating target");
+ return;
+ }
+ ALOGV("Sending target time: %" PRId64 "ns", targetDuration.ns());
+ mLastTargetDurationSent = targetDuration;
+ auto target = targetDuration.ns();
+ if (!writeHintSessionMessage<ChannelMessageContents::Tag::targetDuration>(&target, 1)) {
auto ret = mHintSession->updateTargetWorkDuration(targetDuration.ns());
if (!ret.isOk()) {
ALOGW("Failed to set power hint target work duration with error: %s",
@@ -270,27 +325,30 @@
return;
}
ATRACE_CALL();
- std::optional<Duration> actualDuration = estimateWorkDuration();
- if (!actualDuration.has_value() || actualDuration < 0ns) {
+ std::optional<WorkDuration> actualDuration = estimateWorkDuration();
+ if (!actualDuration.has_value() || actualDuration->durationNanos < 0) {
ALOGV("Failed to send actual work duration, skipping");
return;
}
- actualDuration = std::make_optional(*actualDuration + sTargetSafetyMargin);
- mActualDuration = actualDuration;
-
+ actualDuration->durationNanos += sTargetSafetyMargin.ns();
if (sTraceHintSessionData) {
- ATRACE_INT64("Measured duration", actualDuration->ns());
- ATRACE_INT64("Target error term", Duration{*actualDuration - mTargetDuration}.ns());
- ATRACE_INT64("Reported duration", actualDuration->ns());
+ ATRACE_INT64("Measured duration", actualDuration->durationNanos);
+ ATRACE_INT64("Target error term", actualDuration->durationNanos - mTargetDuration.ns());
+ ATRACE_INT64("Reported duration", actualDuration->durationNanos);
+ if (supportsGpuReporting()) {
+ ATRACE_INT64("Reported cpu duration", actualDuration->cpuDurationNanos);
+ ATRACE_INT64("Reported gpu duration", actualDuration->gpuDurationNanos);
+ }
ATRACE_INT64("Reported target", mLastTargetDurationSent.ns());
ATRACE_INT64("Reported target error term",
- Duration{*actualDuration - mLastTargetDurationSent}.ns());
+ actualDuration->durationNanos - mLastTargetDurationSent.ns());
}
- ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64
- " with error: %" PRId64,
- actualDuration->ns(), mLastTargetDurationSent.ns(),
- Duration{*actualDuration - mLastTargetDurationSent}.ns());
+ ALOGV("Sending actual work duration of: %" PRId64 " with cpu: %" PRId64 " and gpu: %" PRId64
+ " on reported target: %" PRId64 " with error: %" PRId64,
+ actualDuration->durationNanos, actualDuration->cpuDurationNanos,
+ actualDuration->gpuDurationNanos, mLastTargetDurationSent.ns(),
+ actualDuration->durationNanos - mLastTargetDurationSent.ns());
if (mTimingTestingMode) {
mDelayReportActualMutexAcquisitonPromise.get_future().wait();
@@ -298,33 +356,73 @@
}
{
- std::lock_guard lock(mHintSessionMutex);
+ std::scoped_lock lock(mHintSessionMutex);
if (!ensurePowerHintSessionRunning()) {
- ALOGV("Hint session not running and could not be started, skipping");
+ ALOGV("Hint session not running and could not be started, skip reporting durations");
return;
}
-
- WorkDuration duration{
- .timeStampNanos = TimePoint::now().ns(),
- // TODO(b/284324521): Correctly calculate total duration.
- .durationNanos = actualDuration->ns(),
- .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
- .cpuDurationNanos = actualDuration->ns(),
- // TODO(b/284324521): Calculate RenderEngine GPU time.
- .gpuDurationNanos = 0,
- };
- mHintSessionQueue.push_back(duration);
-
- auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
- if (!ret.isOk()) {
- ALOGW("Failed to report actual work durations with error: %s", ret.errorMessage());
- mHintSession = nullptr;
- return;
+ mHintSessionQueue.push_back(*actualDuration);
+ if (!writeHintSessionMessage<
+ ChannelMessageContents::Tag::workDuration>(mHintSessionQueue.data(),
+ mHintSessionQueue.size())) {
+ auto ret = mHintSession->reportActualWorkDuration(mHintSessionQueue);
+ if (!ret.isOk()) {
+ ALOGW("Failed to report actual work durations with error: %s", ret.errorMessage());
+ mHintSession = nullptr;
+ return;
+ }
}
}
mHintSessionQueue.clear();
}
+template <ChannelMessage::ChannelMessageContents::Tag T, class In>
+bool PowerAdvisor::writeHintSessionMessage(In* contents, size_t count) {
+ if (!mMsgQueue) {
+ ALOGV("Skip using FMQ with message tag %hhd as it's not supported", T);
+ return false;
+ }
+ auto availableSize = mMsgQueue->availableToWrite();
+ if (availableSize < count) {
+ ALOGW("Skip using FMQ with message tag %hhd as there isn't enough space", T);
+ return false;
+ }
+ MsgQueue::MemTransaction tx;
+ if (!mMsgQueue->beginWrite(count, &tx)) {
+ ALOGW("Failed to begin writing message with tag %hhd", T);
+ return false;
+ }
+ for (size_t i = 0; i < count; ++i) {
+ if constexpr (T == ChannelMessageContents::Tag::workDuration) {
+ const WorkDuration& duration = contents[i];
+ new (tx.getSlot(i)) ChannelMessage{
+ .sessionID = static_cast<int32_t>(mSessionConfig.id),
+ .timeStampNanos =
+ (i == count - 1) ? ::android::uptimeNanos() : duration.timeStampNanos,
+ .data = ChannelMessageContents::make<ChannelMessageContents::Tag::workDuration,
+ WorkDurationFixedV1>({
+ .durationNanos = duration.durationNanos,
+ .workPeriodStartTimestampNanos = duration.workPeriodStartTimestampNanos,
+ .cpuDurationNanos = duration.cpuDurationNanos,
+ .gpuDurationNanos = duration.gpuDurationNanos,
+ }),
+ };
+ } else {
+ new (tx.getSlot(i)) ChannelMessage{
+ .sessionID = static_cast<int32_t>(mSessionConfig.id),
+ .timeStampNanos = ::android::uptimeNanos(),
+ .data = ChannelMessageContents::make<T, In>(std::move(contents[i])),
+ };
+ }
+ }
+ if (!mMsgQueue->commitWrite(count)) {
+ ALOGW("Failed to send message with tag %hhd, fall back to binder call", T);
+ return false;
+ }
+ mEventFlag->wake(mFmqWriteMask);
+ return true;
+}
+
void PowerAdvisor::enablePowerHintSession(bool enabled) {
mHintSessionEnabled = enabled;
}
@@ -340,19 +438,50 @@
}
LOG_ALWAYS_FATAL_IF(mHintSessionThreadIds.empty(),
"No thread IDs provided to power hint session!");
- std::lock_guard lock(mHintSessionMutex);
- if (mHintSession != nullptr) {
- ALOGE("Cannot start power hint session: already running");
- return false;
+ {
+ std::scoped_lock lock(mHintSessionMutex);
+ if (mHintSession != nullptr) {
+ ALOGE("Cannot start power hint session: already running");
+ return false;
+ }
+ return ensurePowerHintSessionRunning();
}
- return ensurePowerHintSessionRunning();
}
-void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
+bool PowerAdvisor::supportsGpuReporting() {
+ return mBootFinished && FlagManager::getInstance().adpf_gpu_sf();
+}
+
+void PowerAdvisor::setGpuStartTime(DisplayId displayId, TimePoint startTime) {
DisplayTimingData& displayData = mDisplayTimingData[displayId];
if (displayData.gpuEndFenceTime) {
nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime();
if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) {
+ displayData.lastValidGpuStartTime = displayData.gpuStartTime;
+ displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime);
+ for (auto&& [_, otherDisplayData] : mDisplayTimingData) {
+ if (!otherDisplayData.lastValidGpuStartTime.has_value() ||
+ !otherDisplayData.lastValidGpuEndTime.has_value())
+ continue;
+ if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) &&
+ (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) {
+ displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime;
+ break;
+ }
+ }
+ }
+ displayData.gpuEndFenceTime = nullptr;
+ }
+ displayData.gpuStartTime = startTime;
+}
+
+void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) {
+ DisplayTimingData& displayData = mDisplayTimingData[displayId];
+ if (displayData.gpuEndFenceTime && !supportsGpuReporting()) {
+ nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime();
+ if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) {
+ displayData.lastValidGpuStartTime = displayData.gpuStartTime;
+ displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime);
for (auto&& [_, otherDisplayData] : mDisplayTimingData) {
// If the previous display started before us but ended after we should have
// started, then it likely delayed our start time and we must compensate for that.
@@ -365,12 +494,12 @@
break;
}
}
- displayData.lastValidGpuStartTime = displayData.gpuStartTime;
- displayData.lastValidGpuEndTime = TimePoint::fromNs(signalTime);
}
}
displayData.gpuEndFenceTime = std::move(fenceTime);
- displayData.gpuStartTime = TimePoint::now();
+ if (!supportsGpuReporting()) {
+ displayData.gpuStartTime = TimePoint::now();
+ }
}
void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
@@ -391,9 +520,8 @@
mDisplayTimingData[displayId].skippedValidate = skipped;
}
-void PowerAdvisor::setRequiresClientComposition(DisplayId displayId,
- bool requiresClientComposition) {
- mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition;
+void PowerAdvisor::setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine) {
+ mDisplayTimingData[displayId].requiresRenderEngine = requiresRenderEngine;
}
void PowerAdvisor::setExpectedPresentTime(TimePoint expectedPresentTime) {
@@ -401,8 +529,8 @@
}
void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
- mLastSfPresentEndTime = presentEndTime;
mLastPresentFenceTime = presentFenceTime;
+ mLastSfPresentEndTime = presentEndTime;
}
void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) {
@@ -443,7 +571,7 @@
return sortedDisplays;
}
-std::optional<Duration> PowerAdvisor::estimateWorkDuration() {
+std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() {
if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
return std::nullopt;
}
@@ -462,11 +590,10 @@
// used to accumulate gpu time as we iterate over the active displays
std::optional<TimePoint> estimatedGpuEndTime;
- // The timing info for the previously calculated display, if there was one
- std::optional<DisplayTimeline> previousDisplayTiming;
std::vector<DisplayId>&& displayIds =
getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime);
DisplayTimeline displayTiming;
+ std::optional<GpuTimeline> firstGpuTimeline;
// Iterate over the displays that use hwc in the same order they are presented
for (DisplayId displayId : displayIds) {
@@ -478,14 +605,6 @@
displayTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime);
- // If this is the first display, include the duration before hwc present starts
- if (!previousDisplayTiming.has_value()) {
- estimatedHwcEndTime += displayTiming.hwcPresentStartTime - mCommitStartTimes[0];
- } else { // Otherwise add the time since last display's hwc present finished
- estimatedHwcEndTime +=
- displayTiming.hwcPresentStartTime - previousDisplayTiming->hwcPresentEndTime;
- }
-
// Update predicted present finish time with this display's present time
estimatedHwcEndTime = displayTiming.hwcPresentEndTime;
@@ -500,6 +619,9 @@
// Estimate the reference frame's gpu timing
auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime);
if (gpuTiming.has_value()) {
+ if (!firstGpuTimeline.has_value()) {
+ firstGpuTimeline = gpuTiming;
+ }
previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration;
// Estimate the prediction frame's gpu end time from the reference frame
@@ -507,9 +629,7 @@
estimatedGpuEndTime.value_or(TimePoint{0ns})) +
gpuTiming->duration;
}
- previousDisplayTiming = displayTiming;
}
- ATRACE_INT64("Idle duration", idleDuration.ns());
TimePoint estimatedFlingerEndTime = mLastSfPresentEndTime;
@@ -522,15 +642,33 @@
Duration totalDuration = mFrameDelayDuration +
std::max(estimatedHwcEndTime, estimatedGpuEndTime.value_or(TimePoint{0ns})) -
mCommitStartTimes[0];
+ Duration totalDurationWithoutGpu =
+ mFrameDelayDuration + estimatedHwcEndTime - mCommitStartTimes[0];
// We finish SurfaceFlinger when post-composition finishes, so add that in here
Duration flingerDuration =
estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0];
+ Duration estimatedGpuDuration = firstGpuTimeline.has_value()
+ ? estimatedGpuEndTime.value_or(TimePoint{0ns}) - firstGpuTimeline->startTime
+ : Duration::fromNs(0);
// Combine the two timings into a single normalized one
Duration combinedDuration = combineTimingEstimates(totalDuration, flingerDuration);
+ Duration cpuDuration = combineTimingEstimates(totalDurationWithoutGpu, flingerDuration);
- return std::make_optional(combinedDuration);
+ WorkDuration duration{
+ .timeStampNanos = TimePoint::now().ns(),
+ .durationNanos = combinedDuration.ns(),
+ .workPeriodStartTimestampNanos = mCommitStartTimes[0].ns(),
+ .cpuDurationNanos = supportsGpuReporting() ? cpuDuration.ns() : 0,
+ .gpuDurationNanos = supportsGpuReporting() ? estimatedGpuDuration.ns() : 0,
+ };
+ if (sTraceHintSessionData) {
+ ATRACE_INT64("Idle duration", idleDuration.ns());
+ ATRACE_INT64("Total duration", totalDuration.ns());
+ ATRACE_INT64("Flinger duration", flingerDuration.ns());
+ }
+ return std::make_optional(duration);
}
Duration PowerAdvisor::combineTimingEstimates(Duration totalDuration, Duration flingerDuration) {
@@ -581,7 +719,7 @@
std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming(
std::optional<TimePoint> previousEndTime) {
- if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
+ if (!(requiresRenderEngine && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) {
return std::nullopt;
}
const TimePoint latestGpuStartTime =
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 13e1263..bc4a41b 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -29,6 +29,7 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
#include <aidl/android/hardware/power/IPower.h>
+#include <fmq/AidlMessageQueue.h>
#include <powermanager/PowerHalController.h>
#pragma clang diagnostic pop
@@ -59,6 +60,7 @@
// set before onBootFinished, which gates all methods that run on threads other than SF main
virtual bool usePowerHintSession() = 0;
virtual bool supportsPowerHintSession() = 0;
+ virtual bool supportsGpuReporting() = 0;
// Sends a power hint that updates to the target work duration for the frame
virtual void updateTargetWorkDuration(Duration targetDuration) = 0;
@@ -68,6 +70,8 @@
virtual void enablePowerHintSession(bool enabled) = 0;
// Initializes the power hint session
virtual bool startPowerHintSession(std::vector<int32_t>&& threadIds) = 0;
+ // Provides PowerAdvisor with gpu start time
+ virtual void setGpuStartTime(DisplayId displayId, TimePoint startTime) = 0;
// Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time
virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0;
// Reports the start and end times of a hwc validate call this frame for a given display
@@ -80,9 +84,8 @@
virtual void setExpectedPresentTime(TimePoint expectedPresentTime) = 0;
// Reports the most recent present fence time and end time once known
virtual void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) = 0;
- // Reports whether a display used client composition this frame
- virtual void setRequiresClientComposition(DisplayId displayId,
- bool requiresClientComposition) = 0;
+ // Reports whether a display requires RenderEngine to draw
+ virtual void setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine) = 0;
// Reports whether a given display skipped validation this frame
virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0;
// Reports when a hwc present is delayed, and the time that it will resume
@@ -121,17 +124,19 @@
bool isUsingExpensiveRendering() override { return mNotifiedExpensiveRendering; };
bool usePowerHintSession() override;
bool supportsPowerHintSession() override;
+ bool supportsGpuReporting() override;
void updateTargetWorkDuration(Duration targetDuration) override;
void reportActualWorkDuration() override;
void enablePowerHintSession(bool enabled) override;
bool startPowerHintSession(std::vector<int32_t>&& threadIds) override;
+ void setGpuStartTime(DisplayId displayId, TimePoint startTime) override;
void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) override;
void setHwcValidateTiming(DisplayId displayId, TimePoint validateStartTime,
TimePoint validateEndTime) override;
void setHwcPresentTiming(DisplayId displayId, TimePoint presentStartTime,
TimePoint presentEndTime) override;
void setSkippedValidate(DisplayId displayId, bool skipped) override;
- void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override;
+ void setRequiresRenderEngine(DisplayId displayId, bool requiresRenderEngine);
void setExpectedPresentTime(TimePoint expectedPresentTime) override;
void setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) override;
void setHwcPresentDelayedTime(DisplayId displayId, TimePoint earliestFrameStartTime) override;
@@ -192,7 +197,7 @@
std::optional<TimePoint> hwcValidateStartTime;
std::optional<TimePoint> hwcValidateEndTime;
std::optional<TimePoint> hwcPresentDelayedTime;
- bool usedClientComposition = false;
+ bool requiresRenderEngine = false;
bool skippedValidate = false;
// Calculate high-level timing milestones from more granular display timing data
DisplayTimeline calculateDisplayTimeline(TimePoint fenceTime);
@@ -224,18 +229,17 @@
// Filter and sort the display ids by a given property
std::vector<DisplayId> getOrderedDisplayIds(
std::optional<TimePoint> DisplayTimingData::*sortBy);
- // Estimates a frame's total work duration including gpu time.
- std::optional<Duration> estimateWorkDuration();
+ // Estimates a frame's total work duration including gpu and gpu time.
+ std::optional<aidl::android::hardware::power::WorkDuration> estimateWorkDuration();
// There are two different targets and actual work durations we care about,
// this normalizes them together and takes the max of the two
Duration combineTimingEstimates(Duration totalDuration, Duration flingerDuration);
-
// Whether to use the new "createHintSessionWithConfig" method
bool shouldCreateSessionWithConfig() REQUIRES(mHintSessionMutex);
bool ensurePowerHintSessionRunning() REQUIRES(mHintSessionMutex);
+ void setUpFmq() REQUIRES(mHintSessionMutex);
std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData;
-
// Current frame's delay
Duration mFrameDelayDuration{0ns};
// Last frame's post-composition duration
@@ -270,9 +274,17 @@
bool mHasDisplayUpdateImminent = true;
// Queue of actual durations saved to report
std::vector<aidl::android::hardware::power::WorkDuration> mHintSessionQueue;
+ std::unique_ptr<::android::AidlMessageQueue<
+ aidl::android::hardware::power::ChannelMessage,
+ ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
+ mMsgQueue GUARDED_BY(mHintSessionMutex);
+ std::unique_ptr<::android::AidlMessageQueue<
+ int8_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>>
+ mFlagQueue GUARDED_BY(mHintSessionMutex);
+ android::hardware::EventFlag* mEventFlag;
+ uint32_t mFmqWriteMask;
// The latest values we have received for target and actual
Duration mTargetDuration = kDefaultTargetDuration;
- std::optional<Duration> mActualDuration;
// The list of thread ids, stored so we can restart the session from this class if needed
std::vector<int32_t> mHintSessionThreadIds;
Duration mLastTargetDurationSent = kDefaultTargetDuration;
@@ -305,6 +317,12 @@
// How long we expect hwc to run after the present call until it waits for the fence
static constexpr const Duration kFenceWaitStartDelayValidated{150us};
static constexpr const Duration kFenceWaitStartDelaySkippedValidate{250us};
+
+ void sendHintSessionHint(aidl::android::hardware::power::SessionHint hint);
+
+ template <aidl::android::hardware::power::ChannelMessage::ChannelMessageContents::Tag T,
+ class In>
+ bool writeHintSessionMessage(In* elements, size_t count) REQUIRES(mHintSessionMutex);
};
} // namespace impl
diff --git a/services/surfaceflinger/FrontEnd/DisplayInfo.h b/services/surfaceflinger/FrontEnd/DisplayInfo.h
index 6502f36..ea51e92 100644
--- a/services/surfaceflinger/FrontEnd/DisplayInfo.h
+++ b/services/surfaceflinger/FrontEnd/DisplayInfo.h
@@ -21,6 +21,7 @@
#include <gui/DisplayInfo.h>
#include <ui/DisplayMap.h>
#include <ui/LayerStack.h>
+#include <ui/LogicalDisplayId.h>
#include <ui/Transform.h>
namespace android::surfaceflinger::frontend {
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
index 821ac0c..0dcbb3c 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -153,7 +153,7 @@
out << prefix + (isLastChild ? "└─ " : "├─ ");
if (variant == LayerHierarchy::Variant::Relative) {
out << "(Relative) ";
- } else if (variant == LayerHierarchy::Variant::Mirror) {
+ } else if (LayerHierarchy::isMirror(variant)) {
if (!includeMirroredHierarchy) {
out << "(Mirroring) " << *mLayer << "\n" + prefix + " └─ ...";
return;
@@ -289,6 +289,12 @@
LayerHierarchy* mirror = getHierarchyFromId(mirrorId);
hierarchy->addChild(mirror, LayerHierarchy::Variant::Mirror);
}
+ if (FlagManager::getInstance().detached_mirror()) {
+ if (layer->layerIdToMirror != UNASSIGNED_LAYER_ID) {
+ LayerHierarchy* mirror = getHierarchyFromId(layer->layerIdToMirror);
+ hierarchy->addChild(mirror, LayerHierarchy::Variant::Detached_Mirror);
+ }
+ }
}
void LayerHierarchyBuilder::onLayerDestroyed(RequestedLayerState* layer) {
@@ -325,7 +331,7 @@
LayerHierarchy* hierarchy = getHierarchyFromId(layer->id);
auto it = hierarchy->mChildren.begin();
while (it != hierarchy->mChildren.end()) {
- if (it->second == LayerHierarchy::Variant::Mirror) {
+ if (LayerHierarchy::isMirror(it->second)) {
it = hierarchy->mChildren.erase(it);
} else {
it++;
@@ -335,6 +341,12 @@
for (uint32_t mirrorId : layer->mirrorIds) {
hierarchy->addChild(getHierarchyFromId(mirrorId), LayerHierarchy::Variant::Mirror);
}
+ if (FlagManager::getInstance().detached_mirror()) {
+ if (layer->layerIdToMirror != UNASSIGNED_LAYER_ID) {
+ hierarchy->addChild(getHierarchyFromId(layer->layerIdToMirror),
+ LayerHierarchy::Variant::Detached_Mirror);
+ }
+ }
}
void LayerHierarchyBuilder::doUpdate(
@@ -501,7 +513,7 @@
// stored to reset the id upon destruction.
traversalPath.id = layerId;
traversalPath.variant = variant;
- if (variant == LayerHierarchy::Variant::Mirror) {
+ if (LayerHierarchy::isMirror(variant)) {
traversalPath.mirrorRootIds.emplace_back(mParentPath.id);
} else if (variant == LayerHierarchy::Variant::Relative) {
if (std::find(traversalPath.relativeRootIds.begin(), traversalPath.relativeRootIds.end(),
@@ -516,7 +528,7 @@
LayerHierarchy::ScopedAddToTraversalPath::~ScopedAddToTraversalPath() {
// Reset the traversal id to its original parent state using the state that was saved in
// the constructor.
- if (mTraversalPath.variant == LayerHierarchy::Variant::Mirror) {
+ if (LayerHierarchy::isMirror(mTraversalPath.variant)) {
mTraversalPath.mirrorRootIds.pop_back();
} else if (mTraversalPath.variant == LayerHierarchy::Variant::Relative) {
mTraversalPath.relativeRootIds.pop_back();
diff --git a/services/surfaceflinger/FrontEnd/LayerHierarchy.h b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
index a1c73c3..f62e758 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.h
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.h
@@ -35,6 +35,7 @@
// Detached - child of the parent but currently relative parented to another layer
// Relative - relative child of the parent
// Mirror - mirrored from another layer
+// Detached_Mirror - mirrored from another layer, ignoring local transform
//
// By representing the hierarchy as a graph, we can represent mirrored layer hierarchies without
// cloning the layer requested state. The mirrored hierarchy and its corresponding
@@ -43,13 +44,18 @@
class LayerHierarchy {
public:
enum Variant : uint32_t {
- Attached, // child of the parent
- Detached, // child of the parent but currently relative parented to another layer
- Relative, // relative child of the parent
- Mirror, // mirrored from another layer
+ Attached, // child of the parent
+ Detached, // child of the parent but currently relative parented to another layer
+ Relative, // relative child of the parent
+ Mirror, // mirrored from another layer
+ Detached_Mirror, // mirrored from another layer, ignoring local transform
ftl_first = Attached,
- ftl_last = Mirror,
+ ftl_last = Detached_Mirror,
};
+ static inline bool isMirror(Variant variant) {
+ return ((variant == Mirror) || (variant == Detached_Mirror));
+ }
+
// Represents a unique path to a node.
// The layer hierarchy is represented as a graph. Each node can be visited by multiple parents.
// This allows us to represent mirroring in an efficient way. See the example below:
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index 0983e7c..4b0618e 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -72,8 +72,10 @@
// Check if we are mirroring a single layer, and if so add it to the list of children
// to be mirrored.
layer.layerIdToMirror = linkLayer(layer.layerIdToMirror, layer.id);
- if (layer.layerIdToMirror != UNASSIGNED_LAYER_ID) {
- layer.mirrorIds.emplace_back(layer.layerIdToMirror);
+ if (!FlagManager::getInstance().detached_mirror()) {
+ if (layer.layerIdToMirror != UNASSIGNED_LAYER_ID) {
+ layer.mirrorIds.emplace_back(layer.layerIdToMirror);
+ }
}
}
layer.touchCropId = linkLayer(layer.touchCropId, layer.id);
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
index ea06cf6..70e3c64 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp
@@ -127,9 +127,8 @@
pid = state.ownerPid;
changes = RequestedLayerState::Changes::Created;
clientChanges = 0;
- mirrorRootPath = path.variant == LayerHierarchy::Variant::Mirror
- ? path
- : LayerHierarchy::TraversalPath::ROOT;
+ mirrorRootPath =
+ LayerHierarchy::isMirror(path.variant) ? path : LayerHierarchy::TraversalPath::ROOT;
reachablilty = LayerSnapshot::Reachablilty::Unreachable;
frameRateSelectionPriority = state.frameRateSelectionPriority;
layerMetadata = state.metadata;
@@ -472,13 +471,14 @@
geomContentCrop = requested.getBufferCrop();
}
- if (forceUpdate ||
- requested.what &
- (layer_state_t::eFlagsChanged | layer_state_t::eDestinationFrameChanged |
- layer_state_t::ePositionChanged | layer_state_t::eMatrixChanged |
- layer_state_t::eBufferTransformChanged |
- layer_state_t::eTransformToDisplayInverseChanged) ||
- requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) {
+ if ((forceUpdate ||
+ requested.what &
+ (layer_state_t::eFlagsChanged | layer_state_t::eDestinationFrameChanged |
+ layer_state_t::ePositionChanged | layer_state_t::eMatrixChanged |
+ layer_state_t::eBufferTransformChanged |
+ layer_state_t::eTransformToDisplayInverseChanged) ||
+ requested.changes.test(RequestedLayerState::Changes::BufferSize) || displayChanges) &&
+ !ignoreLocalTransform) {
localTransform = requested.getTransform(displayRotationFlags);
localTransformInverse = localTransform.inverse();
}
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
index 73ee22f..eef8dff 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -80,6 +80,9 @@
ui::Transform localTransformInverse;
gui::WindowInfo inputInfo;
ui::Transform localTransform;
+ // set to true if this snapshot will ignore local transforms. Used when the snapshot
+ // is a mirror root
+ bool ignoreLocalTransform;
gui::DropInputMode dropInputMode;
bool isTrustedOverlay;
gui::GameMode gameMode;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 0966fe0..e40c79c 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -575,9 +575,11 @@
mSnapshots.emplace_back(std::make_unique<LayerSnapshot>(layer, path));
LayerSnapshot* snapshot = mSnapshots.back().get();
snapshot->globalZ = static_cast<size_t>(mSnapshots.size()) - 1;
- if (path.isClone() && path.variant != LayerHierarchy::Variant::Mirror) {
+ if (path.isClone() && !LayerHierarchy::isMirror(path.variant)) {
snapshot->mirrorRootPath = parentSnapshot.mirrorRootPath;
}
+ snapshot->ignoreLocalTransform =
+ path.isClone() && path.variant == LayerHierarchy::Variant::Detached_Mirror;
mPathToSnapshot[path] = snapshot;
mIdToSnapshots.emplace(path.id, snapshot);
@@ -1028,6 +1030,8 @@
const LayerSnapshot& parentSnapshot,
const LayerHierarchy::TraversalPath& path,
const Args& args) {
+ using InputConfig = gui::WindowInfo::InputConfig;
+
if (requested.windowInfoHandle) {
snapshot.inputInfo = *requested.windowInfoHandle->getInfo();
} else {
@@ -1040,7 +1044,8 @@
snapshot.touchCropId = requested.touchCropId;
snapshot.inputInfo.id = static_cast<int32_t>(snapshot.uniqueSequence);
- snapshot.inputInfo.displayId = static_cast<int32_t>(snapshot.outputFilter.layerStack.id);
+ snapshot.inputInfo.displayId =
+ ui::LogicalDisplayId{static_cast<int32_t>(snapshot.outputFilter.layerStack.id)};
snapshot.inputInfo.touchOcclusionMode = requested.hasInputInfo()
? requested.windowInfoHandle->getInfo()->touchOcclusionMode
: parentSnapshot.inputInfo.touchOcclusionMode;
@@ -1056,6 +1061,11 @@
snapshot.dropInputMode = gui::DropInputMode::NONE;
}
+ if (snapshot.isSecure ||
+ parentSnapshot.inputInfo.inputConfig.test(InputConfig::SENSITIVE_FOR_PRIVACY)) {
+ snapshot.inputInfo.inputConfig |= InputConfig::SENSITIVE_FOR_PRIVACY;
+ }
+
updateVisibility(snapshot, snapshot.isVisible);
if (!needsInputInfo(snapshot, requested)) {
return;
@@ -1068,14 +1078,14 @@
auto displayInfo = displayInfoOpt.value_or(sDefaultInfo);
if (!requested.windowInfoHandle) {
- snapshot.inputInfo.inputConfig = gui::WindowInfo::InputConfig::NO_INPUT_CHANNEL;
+ snapshot.inputInfo.inputConfig = InputConfig::NO_INPUT_CHANNEL;
}
fillInputFrameInfo(snapshot.inputInfo, displayInfo.transform, snapshot);
if (noValidDisplay) {
// Do not let the window receive touches if it is not associated with a valid display
// transform. We still allow the window to receive keys and prevent ANRs.
- snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_TOUCHABLE;
+ snapshot.inputInfo.inputConfig |= InputConfig::NOT_TOUCHABLE;
}
snapshot.inputInfo.alpha = snapshot.color.a;
@@ -1085,7 +1095,7 @@
// If the window will be blacked out on a display because the display does not have the secure
// flag and the layer has the secure flag set, then drop input.
if (!displayInfo.isSecure && snapshot.isSecure) {
- snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::DROP_INPUT;
+ snapshot.inputInfo.inputConfig |= InputConfig::DROP_INPUT;
}
if (requested.touchCropId != UNASSIGNED_LAYER_ID || path.isClone()) {
@@ -1102,7 +1112,7 @@
// Inherit the trusted state from the parent hierarchy, but don't clobber the trusted state
// if it was set by WM for a known system overlay
if (snapshot.isTrustedOverlay) {
- snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::TRUSTED_OVERLAY;
+ snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
}
snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
@@ -1110,10 +1120,10 @@
// If the layer is a clone, we need to crop the input region to cloned root to prevent
// touches from going outside the cloned area.
if (path.isClone()) {
- snapshot.inputInfo.inputConfig |= gui::WindowInfo::InputConfig::CLONE;
+ snapshot.inputInfo.inputConfig |= InputConfig::CLONE;
// Cloned layers shouldn't handle watch outside since their z order is not determined by
// WM or the client.
- snapshot.inputInfo.inputConfig.clear(gui::WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH);
+ snapshot.inputInfo.inputConfig.clear(InputConfig::WATCH_OUTSIDE_TOUCH);
}
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
index 867f3af..028bd19 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -585,11 +585,13 @@
return false;
}
- static constexpr uint64_t deniedFlags = layer_state_t::eProducerDisconnect |
- layer_state_t::eLayerChanged | layer_state_t::eRelativeLayerChanged |
- layer_state_t::eTransparentRegionChanged | layer_state_t::eFlagsChanged |
- layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
- layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent;
+ const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
+ layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
+ layer_state_t::eLayerStackChanged | layer_state_t::eReparent |
+ (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
+ ? 0
+ : layer_state_t::eAutoRefreshChanged);
if (s.what & deniedFlags) {
ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
s.what & deniedFlags);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 7e8ed48..363b35c 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -15,7 +15,7 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#include "TransactionCallbackInvoker.h"
+
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -24,8 +24,6 @@
#define LOG_TAG "Layer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include "Layer.h"
-
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <binder/IPCThreadState.h>
@@ -40,7 +38,6 @@
#include <ftl/enum.h>
#include <ftl/fake_guard.h>
#include <gui/BufferItem.h>
-#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/TraceUtils.h>
#include <math.h>
@@ -74,10 +71,12 @@
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
+#include "Layer.h"
#include "LayerProtoHelper.h"
#include "MutexUtils.h"
#include "SurfaceFlinger.h"
#include "TimeStats/TimeStats.h"
+#include "TransactionCallbackInvoker.h"
#include "TunnelModeEnabledReporter.h"
#include "Utils/FenceUtils.h"
@@ -91,6 +90,10 @@
const ui::Transform kIdentityTransform;
+ui::LogicalDisplayId toLogicalDisplayId(const ui::LayerStack& layerStack) {
+ return ui::LogicalDisplayId{static_cast<int32_t>(layerStack.id)};
+}
+
bool assignTransform(ui::Transform* dst, ui::Transform& from) {
if (*dst == from) {
return false;
@@ -151,8 +154,7 @@
mWindowType(static_cast<WindowInfo::Type>(
args.metadata.getInt32(gui::METADATA_WINDOW_TYPE, 0))),
mLayerCreationFlags(args.flags),
- mBorderEnabled(false),
- mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName)) {
+ mLegacyLayerFE(args.flinger->getFactory().createLayerFE(mName, this)) {
ALOGV("Creating Layer %s", getDebugName());
uint32_t layerFlags = 0;
@@ -1236,28 +1238,6 @@
return StretchEffect{};
}
-bool Layer::enableBorder(bool shouldEnable, float width, const half4& color) {
- if (mBorderEnabled == shouldEnable && mBorderWidth == width && mBorderColor == color) {
- return false;
- }
- mBorderEnabled = shouldEnable;
- mBorderWidth = width;
- mBorderColor = color;
- return true;
-}
-
-bool Layer::isBorderEnabled() {
- return mBorderEnabled;
-}
-
-float Layer::getBorderWidth() {
- return mBorderWidth;
-}
-
-const half4& Layer::getBorderColor() {
- return mBorderColor;
-}
-
bool Layer::propagateFrameRateForLayerTree(FrameRate parentFrameRate, bool overrideChildren,
bool* transactionNeeded) {
// Gets the frame rate to propagate to children.
@@ -1573,10 +1553,6 @@
return usage;
}
-void Layer::skipReportingTransformHint() {
- mSkipReportingTransformHint = true;
-}
-
void Layer::updateTransformHint(ui::Transform::RotationFlags transformHint) {
if (mFlinger->mDebugDisableTransformHint || transformHint & ui::Transform::ROT_INVALID) {
transformHint = ui::Transform::ROT_0;
@@ -1589,53 +1565,6 @@
// debugging
// ----------------------------------------------------------------------------
-// TODO(marissaw): add new layer state info to layer debugging
-gui::LayerDebugInfo Layer::getLayerDebugInfo(const DisplayDevice* display) const {
- using namespace std::string_literals;
-
- gui::LayerDebugInfo info;
- const State& ds = getDrawingState();
- info.mName = getName();
- sp<Layer> parent = mDrawingParent.promote();
- info.mParentName = parent ? parent->getName() : "none"s;
- info.mType = getType();
-
- info.mVisibleRegion = getVisibleRegion(display);
- info.mSurfaceDamageRegion = surfaceDamageRegion;
- info.mLayerStack = getLayerStack().id;
- info.mX = ds.transform.tx();
- info.mY = ds.transform.ty();
- info.mZ = ds.z;
- info.mCrop = ds.crop;
- info.mColor = ds.color;
- info.mFlags = ds.flags;
- info.mPixelFormat = getPixelFormat();
- info.mDataSpace = static_cast<android_dataspace>(getDataSpace());
- info.mMatrix[0][0] = ds.transform[0][0];
- info.mMatrix[0][1] = ds.transform[0][1];
- info.mMatrix[1][0] = ds.transform[1][0];
- info.mMatrix[1][1] = ds.transform[1][1];
- {
- sp<const GraphicBuffer> buffer = getBuffer();
- if (buffer != 0) {
- info.mActiveBufferWidth = buffer->getWidth();
- info.mActiveBufferHeight = buffer->getHeight();
- info.mActiveBufferStride = buffer->getStride();
- info.mActiveBufferFormat = buffer->format;
- } else {
- info.mActiveBufferWidth = 0;
- info.mActiveBufferHeight = 0;
- info.mActiveBufferStride = 0;
- info.mActiveBufferFormat = 0;
- }
- }
- info.mNumQueuedFrames = getQueuedFrameCount();
- info.mIsOpaque = isOpaque(ds);
- info.mContentDirty = contentDirty;
- info.mStretchEffect = getStretchEffect();
- return info;
-}
-
void Layer::miniDumpHeader(std::string& result) {
result.append(kDumpTableRowLength, '-');
result.append("\n");
@@ -2540,7 +2469,7 @@
mDrawingState.inputInfo.ownerUid = gui::Uid{mOwnerUid};
mDrawingState.inputInfo.ownerPid = gui::Pid{mOwnerPid};
mDrawingState.inputInfo.inputConfig |= WindowInfo::InputConfig::NO_INPUT_CHANNEL;
- mDrawingState.inputInfo.displayId = getLayerStack().id;
+ mDrawingState.inputInfo.displayId = toLogicalDisplayId(getLayerStack());
}
const ui::Transform& displayTransform =
@@ -2548,7 +2477,7 @@
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
- info.displayId = getLayerStack().id;
+ info.displayId = toLogicalDisplayId(getLayerStack());
fillInputFrameInfo(info, displayTransform);
@@ -2689,19 +2618,6 @@
return outputLayer ? outputLayer->getState().visibleRegion : Region();
}
-void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId) {
- if (mFlinger->mLayerLifecycleManagerEnabled) return;
- mSnapshot->path.id = clonedFrom->getSequence();
- mSnapshot->path.mirrorRootIds.emplace_back(mirrorRootId);
-
- cloneDrawingState(clonedFrom.get());
- mClonedFrom = clonedFrom;
- mPremultipliedAlpha = clonedFrom->mPremultipliedAlpha;
- mPotentialCursor = clonedFrom->mPotentialCursor;
- mProtectedByApp = clonedFrom->mProtectedByApp;
- updateCloneBufferInfo();
-}
-
void Layer::updateCloneBufferInfo() {
if (!isClone() || !isClonedFromAlive()) {
return;
@@ -2801,7 +2717,7 @@
}
sp<Layer> clonedChild = clonedLayersMap[child];
if (clonedChild == nullptr) {
- clonedChild = child->createClone(mirrorRoot->getSequence());
+ clonedChild = child->createClone();
clonedLayersMap[child] = clonedChild;
}
addChildToDrawing(clonedChild);
@@ -2960,26 +2876,13 @@
ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
} else {
- // If we didn't get a release callback yet, e.g. some scenarios when capturing
- // screenshots asynchronously, then make sure we don't drop the fence.
- mAdditionalPreviousReleaseFences.emplace_back(std::move(futureFenceResult));
- std::vector<ftl::Future<FenceResult>> mergedFences;
- sp<Fence> prevFence = nullptr;
- // For a layer that's frequently screenshotted, try to merge fences to make sure we
- // don't grow unbounded.
- for (auto& futureReleaseFence : mAdditionalPreviousReleaseFences) {
- auto result = futureReleaseFence.wait_for(0s);
- if (result != std::future_status::ready) {
- mergedFences.emplace_back(std::move(futureReleaseFence));
- continue;
- }
- mergeFence(getDebugName(), futureReleaseFence.get().value_or(Fence::NO_FENCE),
- prevFence);
- }
- if (prevFence != nullptr) {
- mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))));
- }
- mAdditionalPreviousReleaseFences.swap(mergedFences);
+ // If we didn't get a release callback yet (e.g. some scenarios when capturing
+ // screenshots asynchronously) then make sure we don't drop the fence.
+ // Older fences for the same layer stack can be dropped when a new fence arrives.
+ // An assumption here is that RenderEngine performs work sequentially, so an
+ // incoming fence will not fire before an existing fence.
+ mAdditionalPreviousReleaseFences.emplace_or_replace(layerStack,
+ std::move(futureFenceResult));
}
if (mBufferInfo.mBuffer) {
@@ -3060,13 +2963,7 @@
void Layer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
for (const auto& handle : mDrawingState.callbackHandles) {
- if (mFlinger->mLayerLifecycleManagerEnabled) {
- handle->transformHint = mTransformHint;
- } else {
- handle->transformHint = mSkipReportingTransformHint
- ? std::nullopt
- : std::make_optional<uint32_t>(mTransformHintLegacy);
- }
+ handle->transformHint = mTransformHint;
handle->dequeueReadyTime = dequeueReadyTime;
handle->currentMaxAcquiredBufferCount =
mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
@@ -3325,10 +3222,6 @@
mFlinger->mTimeStats->setPostTime(layerId, mDrawingState.frameNumber, getName().c_str(),
mOwnerUid, postTime, getGameMode());
- if (mFlinger->mLegacyFrontEndEnabled) {
- recordLayerHistoryBufferUpdate(getLayerProps(), systemTime());
- }
-
setFrameTimelineVsyncForBufferTransaction(info, postTime);
if (dequeueTime && *dequeueTime != 0) {
@@ -3529,10 +3422,10 @@
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
if (FlagManager::getInstance().ce_fence_promise() &&
mPreviousReleaseBufferEndpoint == handle->listener) {
- // Add fences from previous screenshots now so that they can be dispatched to the
+ // Add fence from previous screenshot now so that it can be dispatched to the
// client.
- for (auto& futureReleaseFence : mAdditionalPreviousReleaseFences) {
- handle->previousReleaseFences.emplace_back(std::move(futureReleaseFence));
+ for (auto& [_, future] : mAdditionalPreviousReleaseFences) {
+ handle->previousReleaseFences.emplace_back(std::move(future));
}
mAdditionalPreviousReleaseFences.clear();
} else if (FlagManager::getInstance().screenshot_fence_preservation() &&
@@ -3790,11 +3683,10 @@
}
}
-sp<Layer> Layer::createClone(uint32_t mirrorRootId) {
+sp<Layer> Layer::createClone() {
surfaceflinger::LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0,
LayerMetadata());
sp<Layer> layer = mFlinger->getFactory().createBufferStateLayer(args);
- layer->setInitialValuesForClone(sp<Layer>::fromExisting(this), mirrorRootId);
return layer;
}
@@ -3876,8 +3768,10 @@
const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
layer_state_t::eFlagsChanged | layer_state_t::eBlurRegionsChanged |
- layer_state_t::eLayerStackChanged | layer_state_t::eAutoRefreshChanged |
- layer_state_t::eReparent;
+ layer_state_t::eLayerStackChanged | layer_state_t::eReparent |
+ (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
+ ? 0
+ : layer_state_t::eAutoRefreshChanged);
if ((s.what & requiredFlags) != requiredFlags) {
ATRACE_FORMAT_INSTANT("%s: false [missing required flags 0x%" PRIx64 "]", __func__,
@@ -4088,7 +3982,7 @@
}
sp<LayerFE> Layer::copyCompositionEngineLayerFE() const {
- auto result = mFlinger->getFactory().createLayerFE(mName);
+ auto result = mFlinger->getFactory().createLayerFE(mName, this);
result->mSnapshot = std::make_unique<LayerSnapshot>(*mSnapshot);
return result;
}
@@ -4100,7 +3994,7 @@
return layerFE;
}
}
- auto layerFE = mFlinger->getFactory().createLayerFE(mName);
+ auto layerFE = mFlinger->getFactory().createLayerFE(mName, this);
mLayerFEs.emplace_back(path, layerFE);
return layerFE;
}
@@ -4408,7 +4302,6 @@
if (mTransformHintLegacy == ui::Transform::ROT_INVALID) {
mTransformHintLegacy = displayTransformHint;
}
- mSkipReportingTransformHint = false;
}
const std::shared_ptr<renderengine::ExternalTexture>& Layer::getExternalTexture() const {
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 9108869..9db7664 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,6 +18,7 @@
#include <android/gui/DropInputMode.h>
#include <android/gui/ISurfaceComposerClient.h>
+#include <ftl/small_map.h>
#include <gui/BufferQueue.h>
#include <gui/LayerState.h>
#include <gui/WindowInfo.h>
@@ -25,9 +26,11 @@
#include <math/vec4.h>
#include <sys/types.h>
#include <ui/BlurRegion.h>
+#include <ui/DisplayMap.h>
#include <ui/FloatRect.h>
#include <ui/FrameStats.h>
#include <ui/GraphicBuffer.h>
+#include <ui/LayerStack.h>
#include <ui/PixelFormat.h>
#include <ui/Region.h>
#include <ui/StretchEffect.h>
@@ -71,10 +74,6 @@
struct LayerFECompositionState;
}
-namespace gui {
-class LayerDebugInfo;
-}
-
namespace frametimeline {
class SurfaceFrame;
} // namespace frametimeline
@@ -251,7 +250,7 @@
// true if this layer is visible, false otherwise
virtual bool isVisible() const;
- virtual sp<Layer> createClone(uint32_t mirrorRoot);
+ virtual sp<Layer> createClone();
// Set a 2x2 transformation matrix on the layer. This transform
// will be applied after parent transforms, but before any final
@@ -699,12 +698,9 @@
* Sets display transform hint on BufferLayerConsumer.
*/
void updateTransformHint(ui::Transform::RotationFlags);
- void skipReportingTransformHint();
inline const State& getDrawingState() const { return mDrawingState; }
inline State& getDrawingState() { return mDrawingState; }
- gui::LayerDebugInfo getLayerDebugInfo(const DisplayDevice*) const;
-
void miniDumpLegacy(std::string& result, const DisplayDevice&) const;
void miniDump(std::string& result, const frontend::LayerSnapshot&, const DisplayDevice&) const;
void dumpFrameStats(std::string& result) const;
@@ -882,10 +878,6 @@
bool setStretchEffect(const StretchEffect& effect);
StretchEffect getStretchEffect() const;
- bool enableBorder(bool shouldEnable, float width, const half4& color);
- bool isBorderEnabled();
- float getBorderWidth();
- const half4& getBorderColor();
bool setBufferCrop(const Rect& /* bufferCrop */);
bool setDestinationFrame(const Rect& /* destinationFrame */);
@@ -962,8 +954,11 @@
// screenshots asynchronously. There may be no buffer update for the
// layer, but the layer will still be composited on the screen in every
// frame. Kepping track of these fences ensures that they are not dropped
- // and can be dispatched to the client at a later time.
- std::vector<ftl::Future<FenceResult>> mAdditionalPreviousReleaseFences;
+ // and can be dispatched to the client at a later time. Older fences are
+ // dropped when a layer stack receives a new fence.
+ // TODO(b/300533018): Track fence per multi-instance RenderEngine
+ ftl::SmallMap<ui::LayerStack, ftl::Future<FenceResult>, ui::kDisplayCapacity>
+ mAdditionalPreviousReleaseFences;
// Exposed so SurfaceFlinger can assert that it's held
const sp<SurfaceFlinger> mFlinger;
@@ -981,7 +976,6 @@
friend class TransactionFrameTracerTest;
friend class TransactionSurfaceFrameTest;
- virtual void setInitialValuesForClone(const sp<Layer>& clonedFrom, uint32_t mirrorRootId);
void preparePerFrameCompositionState();
void preparePerFrameBufferCompositionState();
void preparePerFrameEffectsCompositionState();
@@ -1256,10 +1250,6 @@
bool findInHierarchy(const sp<Layer>&);
- bool mBorderEnabled = false;
- float mBorderWidth;
- half4 mBorderColor;
-
void setTransformHintLegacy(ui::Transform::RotationFlags);
void releasePreviousBuffer();
void resetDrawingStateBufferInfo();
@@ -1267,7 +1257,6 @@
// Transform hint provided to the producer. This must be accessed holding
// the mStateLock.
ui::Transform::RotationFlags mTransformHintLegacy = ui::Transform::ROT_0;
- bool mSkipReportingTransformHint = true;
std::optional<ui::Transform::RotationFlags> mTransformHint = std::nullopt;
ReleaseCallbackId mPreviousReleaseCallbackId = ReleaseCallbackId::INVALID_ID;
diff --git a/services/surfaceflinger/LayerFE.cpp b/services/surfaceflinger/LayerFE.cpp
index 620edca..c2251a8 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,6 +27,7 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
+#include "common/FlagManager.h"
#include "ui/FenceResult.h"
#include "ui/LayerStack.h"
@@ -80,6 +81,16 @@
LayerFE::LayerFE(const std::string& name) : mName(name) {}
+LayerFE::~LayerFE() {
+ // Ensures that no promise is left unfulfilled before the LayerFE is destroyed.
+ // An unfulfilled promise could occur when a screenshot is attempted, but the
+ // render area is invalid and there is no memory for the capture result.
+ if (FlagManager::getInstance().ce_fence_promise() &&
+ mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+ setReleaseFence(Fence::NO_FENCE);
+ }
+}
+
const compositionengine::LayerFECompositionState* LayerFE::getCompositionState() const {
return mSnapshot.get();
}
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index 019fa22..658f949 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -36,6 +36,7 @@
class LayerFE : public virtual RefBase, public virtual compositionengine::LayerFE {
public:
LayerFE(const std::string& name);
+ virtual ~LayerFE();
// compositionengine::LayerFE overrides
const compositionengine::LayerFECompositionState* getCompositionState() const override;
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index aa6026e..753886a 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -334,7 +334,7 @@
variant);
frontend::LayerSnapshot* childSnapshot = getSnapshot(path, layer);
if (variant == Variant::Attached || variant == Variant::Detached ||
- variant == Variant::Mirror) {
+ frontend::LayerHierarchy::isMirror(variant)) {
mChildToParent[childSnapshot->uniqueSequence] = snapshot->uniqueSequence;
layerProto->add_children(childSnapshot->uniqueSequence);
} else if (variant == Variant::Relative) {
@@ -465,7 +465,7 @@
displays.Reserve(displayInfos.size());
for (const auto& [layerStack, displayInfo] : displayInfos) {
auto displayProto = displays.Add();
- displayProto->set_id(displayInfo.info.displayId);
+ displayProto->set_id(displayInfo.info.displayId.val());
displayProto->set_layer_stack(layerStack.id);
displayProto->mutable_size()->set_w(displayInfo.info.logicalWidth);
displayProto->mutable_size()->set_h(displayInfo.info.logicalHeight);
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index 51d4ff8..c25ddb6 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -24,31 +24,17 @@
#include "SurfaceFlinger.h"
namespace android {
-namespace {
-void reparentForDrawing(const sp<Layer>& oldParent, const sp<Layer>& newParent,
- const Rect& drawingBounds) {
- // Compute and cache the bounds for the new parent layer.
- newParent->computeBounds(drawingBounds.toFloatRect(), ui::Transform(),
- 0.f /* shadowRadius */);
- newParent->updateSnapshot(true /* updateGeometry */);
- oldParent->setChildrenDrawingParent(newParent);
-};
-
-} // namespace
-
-LayerRenderArea::LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop,
- ui::Size reqSize, ui::Dataspace reqDataSpace, bool childrenOnly,
- bool allowSecureLayers, const ui::Transform& layerTransform,
- const Rect& layerBufferSize, bool hintForSeamlessTransition)
+LayerRenderArea::LayerRenderArea(sp<Layer> layer, const Rect& crop, ui::Size reqSize,
+ ui::Dataspace reqDataSpace, bool allowSecureLayers,
+ const ui::Transform& layerTransform, const Rect& layerBufferSize,
+ bool hintForSeamlessTransition)
: RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, hintForSeamlessTransition,
allowSecureLayers),
mLayer(std::move(layer)),
- mLayerTransform(layerTransform),
mLayerBufferSize(layerBufferSize),
mCrop(crop),
- mFlinger(flinger),
- mChildrenOnly(childrenOnly) {}
+ mTransform(layerTransform) {}
const ui::Transform& LayerRenderArea::getTransform() const {
return mTransform;
@@ -71,53 +57,4 @@
}
}
-void LayerRenderArea::render(std::function<void()> drawLayers) {
- using namespace std::string_literals;
-
- if (!mChildrenOnly) {
- mTransform = mLayerTransform.inverse();
- }
-
- if (mFlinger.mLayerLifecycleManagerEnabled) {
- drawLayers();
- return;
- }
- // If layer is offscreen, update mirroring info if it exists
- if (mLayer->isRemovedFromCurrentState()) {
- mLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->updateMirrorInfo({}); });
- mLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) { layer->updateCloneBufferInfo(); });
- }
-
- if (!mChildrenOnly) {
- // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
- // layers in a regular cycles.
- if (mLayer->isRemovedFromCurrentState()) {
- FloatRect maxBounds = mFlinger.getMaxDisplayBounds();
- mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
- }
- drawLayers();
- } else {
- // In the "childrenOnly" case we reparent the children to a screenshot
- // layer which has no properties set and which does not draw.
- // We hold the statelock as the reparent-for-drawing operation modifies the
- // hierarchy and there could be readers on Binder threads, like dump.
- auto screenshotParentLayer = mFlinger.getFactory().createEffectLayer(
- {&mFlinger, nullptr, "Screenshot Parent"s, ISurfaceComposerClient::eNoColorFill,
- LayerMetadata()});
- {
- Mutex::Autolock _l(mFlinger.mStateLock);
- reparentForDrawing(mLayer, screenshotParentLayer, getSourceCrop());
- }
- drawLayers();
- {
- Mutex::Autolock _l(mFlinger.mStateLock);
- mLayer->setChildrenDrawingParent(mLayer);
- }
- }
- mLayer->updateSnapshot(/*updateGeometry=*/true);
- mLayer->updateChildrenSnapshots(/*updateGeometry=*/true);
-}
-
} // namespace android
diff --git a/services/surfaceflinger/LayerRenderArea.h b/services/surfaceflinger/LayerRenderArea.h
index aa609ee..b12afe8 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -32,29 +32,23 @@
class LayerRenderArea : public RenderArea {
public:
- LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
- ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
- const ui::Transform& layerTransform, const Rect& layerBufferSize,
- bool hintForSeamlessTransition);
+ LayerRenderArea(sp<Layer> layer, const Rect& crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool allowSecureLayers, const ui::Transform& layerTransform,
+ const Rect& layerBufferSize, bool hintForSeamlessTransition);
const ui::Transform& getTransform() const override;
bool isSecure() const override;
sp<const DisplayDevice> getDisplayDevice() const override;
Rect getSourceCrop() const override;
- void render(std::function<void()> drawLayers) override;
virtual sp<Layer> getParentLayer() const { return mLayer; }
private:
const sp<Layer> mLayer;
- const ui::Transform mLayerTransform;
const Rect mLayerBufferSize;
const Rect mCrop;
ui::Transform mTransform;
-
- SurfaceFlinger& mFlinger;
- const bool mChildrenOnly;
};
} // namespace android
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 0aee7d4..ffc1dd7 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -2,7 +2,6 @@
adyabr@google.com
alecmouri@google.com
-chaviw@google.com
domlaskowski@google.com
jreck@google.com
lpy@google.com
@@ -10,5 +9,6 @@
racarr@google.com
ramindani@google.com
rnlee@google.com
+sallyqi@google.com
scroggo@google.com
vishnun@google.com
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 77e045d..2b4e234 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -42,6 +42,7 @@
#include "DisplayRenderArea.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "Layer.h"
+#include "RenderAreaBuilder.h"
#include "Scheduler/VsyncController.h"
#include "SurfaceFlinger.h"
@@ -278,11 +279,6 @@
const Rect sampledBounds = sampleRegion.bounds();
constexpr bool kHintForSeamlessTransition = false;
- SurfaceFlinger::RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, sampledBounds, sampledBounds.getSize(),
- ui::Dataspace::V0_SRGB, kHintForSeamlessTransition);
- });
-
std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners;
auto layerFilterFn = [&](const char* layerName, uint32_t layerId, const Rect& bounds,
@@ -377,8 +373,14 @@
constexpr bool kIsProtected = false;
if (const auto fenceResult =
- mFlinger.captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, buffer,
- kRegionSampling, kGrayscale, kIsProtected, nullptr)
+ mFlinger.captureScreenshot(SurfaceFlinger::RenderAreaBuilderVariant(
+ std::in_place_type<DisplayRenderAreaBuilder>,
+ sampledBounds, sampledBounds.getSize(),
+ ui::Dataspace::V0_SRGB,
+ kHintForSeamlessTransition,
+ true /* captureSecureLayers */, displayWeak),
+ getLayerSnapshots, buffer, kRegionSampling, kGrayscale,
+ kIsProtected, nullptr)
.get();
fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 5de148e..18a5304 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -47,9 +47,6 @@
virtual ~RenderArea() = default;
- // Invoke drawLayers to render layers into the render area.
- virtual void render(std::function<void()> drawLayers) { drawLayers(); }
-
// Returns true if the render area is secure. A secure layer should be
// blacked out / skipped when rendered to an insecure render area.
virtual bool isSecure() const = 0;
diff --git a/services/surfaceflinger/RenderAreaBuilder.h b/services/surfaceflinger/RenderAreaBuilder.h
new file mode 100644
index 0000000..012acd2
--- /dev/null
+++ b/services/surfaceflinger/RenderAreaBuilder.h
@@ -0,0 +1,114 @@
+/*
+ * 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 "DisplayDevice.h"
+#include "DisplayRenderArea.h"
+#include "LayerRenderArea.h"
+#include "ui/Size.h"
+#include "ui/Transform.h"
+
+namespace android {
+/**
+ * A parameter object for creating a render area
+ */
+struct RenderAreaBuilder {
+ // Source crop of the render area
+ Rect crop;
+
+ // Size of the physical render area
+ ui::Size reqSize;
+
+ // Composition data space of the render area
+ ui::Dataspace reqDataSpace;
+
+ // If true, the secure layer would be blacked out or skipped
+ // when rendered to an insecure render area
+ bool allowSecureLayers;
+
+ // If true, the render result may be used for system animations
+ // that must preserve the exact colors of the display
+ bool hintForSeamlessTransition;
+
+ virtual std::unique_ptr<RenderArea> build() const = 0;
+
+ RenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool allowSecureLayers, bool hintForSeamlessTransition)
+ : crop(crop),
+ reqSize(reqSize),
+ reqDataSpace(reqDataSpace),
+ allowSecureLayers(allowSecureLayers),
+ hintForSeamlessTransition(hintForSeamlessTransition) {}
+
+ virtual ~RenderAreaBuilder() = default;
+};
+
+struct DisplayRenderAreaBuilder : RenderAreaBuilder {
+ DisplayRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool allowSecureLayers, bool hintForSeamlessTransition,
+ wp<const DisplayDevice> displayWeak)
+ : RenderAreaBuilder(crop, reqSize, reqDataSpace, allowSecureLayers,
+ hintForSeamlessTransition),
+ displayWeak(displayWeak) {}
+
+ // Display that render area will be on
+ wp<const DisplayDevice> displayWeak;
+
+ std::unique_ptr<RenderArea> build() const override {
+ return DisplayRenderArea::create(displayWeak, crop, reqSize, reqDataSpace,
+ hintForSeamlessTransition, allowSecureLayers);
+ }
+};
+
+struct LayerRenderAreaBuilder : RenderAreaBuilder {
+ LayerRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ bool allowSecureLayers, bool hintForSeamlessTransition, sp<Layer> layer,
+ bool childrenOnly)
+ : RenderAreaBuilder(crop, reqSize, reqDataSpace, allowSecureLayers,
+ hintForSeamlessTransition),
+ layer(layer),
+ childrenOnly(childrenOnly) {}
+
+ // Layer that the render area will be on
+ sp<Layer> layer;
+
+ // Transform to be applied on the layers to transform them
+ // into the logical render area
+ ui::Transform layerTransform{ui::Transform()};
+
+ // Buffer bounds
+ Rect layerBufferSize{Rect()};
+
+ // If false, transform is inverted from the parent snapshot
+ bool childrenOnly;
+
+ // Uses parent snapshot to determine layer transform and buffer size
+ void setLayerInfo(const frontend::LayerSnapshot* parentSnapshot) {
+ if (!childrenOnly) {
+ layerTransform = parentSnapshot->localTransform.inverse();
+ }
+ layerBufferSize = parentSnapshot->bufferSize;
+ }
+
+ std::unique_ptr<RenderArea> build() const override {
+ return std::make_unique<LayerRenderArea>(layer, crop, reqSize, reqDataSpace,
+ allowSecureLayers, layerTransform, layerBufferSize,
+ hintForSeamlessTransition);
+ }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 96eccf2..6b65449 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -235,7 +235,8 @@
ParcelableVsyncEventData* outVsyncEventData) {
ATRACE_CALL();
outVsyncEventData->vsync =
- mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this));
+ mEventThread->getLatestVsyncEventData(sp<EventThreadConnection>::fromExisting(this),
+ systemTime());
return binder::Status::ok();
}
@@ -387,8 +388,8 @@
}
}
-VsyncEventData EventThread::getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const {
+VsyncEventData EventThread::getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const {
// Resync so that the vsync is accurate with hardware. getLatestVsyncEventData is an alternate
// way to get vsync data (instead of posting callbacks to Choreographer).
mCallback.resync();
@@ -399,11 +400,10 @@
const auto [presentTime, deadline] = [&]() -> std::pair<nsecs_t, nsecs_t> {
std::lock_guard<std::mutex> lock(mMutex);
const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(
- systemTime() + mWorkDuration.get().count() + mReadyDuration.count());
+ now + mWorkDuration.get().count() + mReadyDuration.count());
return {vsyncTime, vsyncTime - mReadyDuration.count()};
}();
- generateFrameTimeline(vsyncEventData, frameInterval.ns(), systemTime(SYSTEM_TIME_MONOTONIC),
- presentTime, deadline);
+ generateFrameTimeline(vsyncEventData, frameInterval.ns(), now, presentTime, deadline);
if (FlagManager::getInstance().vrr_config()) {
mCallback.onExpectedPresentTimePosted(TimePoint::fromNs(presentTime));
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 90e61a9..f772126 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -127,8 +127,8 @@
virtual void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) = 0;
// Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer.
virtual void requestNextVsync(const sp<EventThreadConnection>& connection) = 0;
- virtual VsyncEventData getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const = 0;
+ virtual VsyncEventData getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const = 0;
virtual void onNewVsyncSchedule(std::shared_ptr<scheduler::VsyncSchedule>) = 0;
@@ -160,8 +160,8 @@
status_t registerDisplayEventConnection(const sp<EventThreadConnection>& connection) override;
void setVsyncRate(uint32_t rate, const sp<EventThreadConnection>& connection) override;
void requestNextVsync(const sp<EventThreadConnection>& connection) override;
- VsyncEventData getLatestVsyncEventData(
- const sp<EventThreadConnection>& connection) const override;
+ VsyncEventData getLatestVsyncEventData(const sp<EventThreadConnection>& connection,
+ nsecs_t now) const override;
void enableSyntheticVsync(bool) override;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 974c837..a819b79 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -280,9 +280,18 @@
case Layer::FrameRateCompatibility::Exact:
return LayerVoteType::ExplicitExact;
case Layer::FrameRateCompatibility::Gte:
- return LayerVoteType::ExplicitGte;
+ if (isVrrDevice) {
+ return LayerVoteType::ExplicitGte;
+ } else {
+ // For MRR, treat GTE votes as Max because it is used for animations and
+ // scroll. MRR cannot change frame rate without jank, so it should
+ // prefer smoothness.
+ return LayerVoteType::Max;
+ }
}
}();
+ const bool isValuelessVote = voteType == LayerVoteType::NoVote ||
+ voteType == LayerVoteType::Min || voteType == LayerVoteType::Max;
if (FlagManager::getInstance().game_default_frame_rate()) {
// Determine the layer frame rate considering the following priorities:
@@ -307,7 +316,8 @@
gameModeFrameRateOverride.getIntValue());
}
} else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
- info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate,
+ info->setLayerVote({setFrameRateVoteType,
+ isValuelessVote ? 0_Hz : frameRate.vote.rate,
frameRate.vote.seamlessness, frameRate.category});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, gameFrameRateOverrideVoteType,
@@ -335,8 +345,8 @@
} else {
if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
const auto type = info->isVisible() ? voteType : LayerVoteType::NoVote;
- info->setLayerVote({type, frameRate.vote.rate, frameRate.vote.seamlessness,
- frameRate.category});
+ info->setLayerVote({type, isValuelessVote ? 0_Hz : frameRate.vote.rate,
+ frameRate.vote.seamlessness, frameRate.category});
} else {
if (!frameRate.isVoteValidForMrr(isVrrDevice)) {
ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 1bc4ac2..632f42a 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -55,10 +55,10 @@
bool pendingModeChange, const LayerProps& props) {
lastPresentTime = std::max(lastPresentTime, static_cast<nsecs_t>(0));
- mLastUpdatedTime = std::max(lastPresentTime, now);
*mLayerProps = props;
switch (updateType) {
case LayerUpdateType::AnimationTX:
+ mLastUpdatedTime = std::max(lastPresentTime, now);
mLastAnimationTime = std::max(lastPresentTime, now);
break;
case LayerUpdateType::SetFrameRate:
@@ -67,6 +67,7 @@
}
FALLTHROUGH_INTENDED;
case LayerUpdateType::Buffer:
+ mLastUpdatedTime = std::max(lastPresentTime, now);
FrameTimeData frameTime = {.presentTime = lastPresentTime,
.queueTime = mLastUpdatedTime,
.pendingModeChange = pendingModeChange,
@@ -180,19 +181,19 @@
bool LayerInfo::hasEnoughDataForHeuristic() const {
// The layer had to publish at least HISTORY_SIZE or HISTORY_DURATION of updates
if (mFrameTimes.size() < 2) {
- ALOGV("fewer than 2 frames recorded: %zu", mFrameTimes.size());
+ ALOGV("%s fewer than 2 frames recorded: %zu", mName.c_str(), mFrameTimes.size());
return false;
}
if (!isFrameTimeValid(mFrameTimes.front())) {
- ALOGV("stale frames still captured");
+ ALOGV("%s stale frames still captured", mName.c_str());
return false;
}
const auto totalDuration = mFrameTimes.back().queueTime - mFrameTimes.front().queueTime;
if (mFrameTimes.size() < HISTORY_SIZE && totalDuration < HISTORY_DURATION.count()) {
- ALOGV("not enough frames captured: %zu | %.2f seconds", mFrameTimes.size(),
- totalDuration / 1e9f);
+ ALOGV("%s not enough frames captured: %zu | %.2f seconds", mName.c_str(),
+ mFrameTimes.size(), totalDuration / 1e9f);
return false;
}
@@ -364,6 +365,8 @@
}
if (frequent.clearHistory) {
+ ATRACE_FORMAT_INSTANT("frequent.clearHistory");
+ ALOGV("%s frequent.clearHistory", mName.c_str());
clearHistory(now);
}
@@ -562,8 +565,25 @@
return vote.type == FrameRateCompatibility::NoVote;
}
+bool LayerInfo::FrameRate::isValuelessType() const {
+ // For a valueless frame rate compatibility (type), the frame rate should be unspecified (0 Hz).
+ if (!isApproxEqual(vote.rate, 0_Hz)) {
+ return false;
+ }
+ switch (vote.type) {
+ case FrameRateCompatibility::Min:
+ case FrameRateCompatibility::NoVote:
+ return true;
+ case FrameRateCompatibility::Default:
+ case FrameRateCompatibility::ExactOrMultiple:
+ case FrameRateCompatibility::Exact:
+ case FrameRateCompatibility::Gte:
+ return false;
+ }
+}
+
bool LayerInfo::FrameRate::isValid() const {
- return isNoVote() || vote.rate.isValid() || category != FrameRateCategory::Default;
+ return isValuelessType() || vote.rate.isValid() || category != FrameRateCategory::Default;
}
bool LayerInfo::FrameRate::isVoteValidForMrr(bool isVrrDevice) const {
@@ -571,7 +591,7 @@
return true;
}
- if (category == FrameRateCategory::Default && vote.type != FrameRateCompatibility::Gte) {
+ if (category == FrameRateCategory::Default) {
return true;
}
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 40903ed..a7847bc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -146,6 +146,9 @@
// selection.
bool isNoVote() const;
+ // Returns true if the FrameRate has a valid valueless (0 Hz) frame rate type.
+ bool isValuelessType() const;
+
// Checks whether the given FrameRate's vote specifications is valid for MRR devices
// given the current flagging.
bool isVoteValidForMrr(bool isVrrDevice) const;
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.cpp b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
index cd45bfd..7e61dc0 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.cpp
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.cpp
@@ -115,9 +115,24 @@
break;
}
- auto triggerTime = mClock->now() + mInterval;
+ auto triggerTime = mClock->now() + mInterval.load();
state = TimerState::WAITING;
while (true) {
+ if (mPaused) {
+ mWaiting = true;
+ int result = sem_wait(&mSemaphore);
+ if (result && errno != EINTR) {
+ std::stringstream ss;
+ ss << "sem_wait failed (" << errno << ")";
+ LOG_ALWAYS_FATAL("%s", ss.str().c_str());
+ }
+
+ mWaiting = false;
+ state = checkForResetAndStop(state);
+ if (state == TimerState::STOPPED) {
+ break;
+ }
+ }
// Wait until triggerTime time to check if we need to reset or drop into the idle state.
if (const auto triggerInterval = triggerTime - mClock->now(); triggerInterval > 0ns) {
mWaiting = true;
@@ -137,14 +152,14 @@
break;
}
- if (state == TimerState::WAITING && (triggerTime - mClock->now()) <= 0ns) {
+ if (!mPaused && state == TimerState::WAITING && (triggerTime - mClock->now()) <= 0ns) {
triggerTimeout = true;
state = TimerState::IDLE;
break;
}
if (state == TimerState::RESET) {
- triggerTime = mLastResetTime.load() + mInterval;
+ triggerTime = mLastResetTime.load() + mInterval.load();
state = TimerState::WAITING;
}
}
@@ -179,5 +194,15 @@
}
}
+void OneShotTimer::pause() {
+ mPaused = true;
+}
+
+void OneShotTimer::resume() {
+ if (mPaused.exchange(false)) {
+ LOG_ALWAYS_FATAL_IF(sem_post(&mSemaphore), "sem_post failed");
+ }
+}
+
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/OneShotTimer.h b/services/surfaceflinger/Scheduler/OneShotTimer.h
index 02e8719..4e1e2ee 100644
--- a/services/surfaceflinger/Scheduler/OneShotTimer.h
+++ b/services/surfaceflinger/Scheduler/OneShotTimer.h
@@ -43,7 +43,8 @@
std::unique_ptr<android::Clock> clock = std::make_unique<SteadyClock>());
~OneShotTimer();
- Duration interval() const { return mInterval; }
+ Duration interval() const { return mInterval.load(); }
+ void setInterval(Interval value) { mInterval = value; }
// Initializes and turns on the idle timer.
void start();
@@ -51,6 +52,10 @@
void stop();
// Resets the wakeup time and fires the reset callback.
void reset();
+ // Pauses the timer. reset calls will get ignored.
+ void pause();
+ // Resumes the timer.
+ void resume();
private:
// Enum to track in what state is the timer.
@@ -91,7 +96,7 @@
std::string mName;
// Interval after which timer expires.
- const Interval mInterval;
+ std::atomic<Interval> mInterval;
// Callback that happens when timer resets.
const ResetCallback mResetCallback;
@@ -105,6 +110,7 @@
std::atomic<bool> mResetTriggered = false;
std::atomic<bool> mStopTriggered = false;
std::atomic<bool> mWaiting = false;
+ std::atomic<bool> mPaused = false;
std::atomic<std::chrono::steady_clock::time_point> mLastResetTime;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
index 97279c3..a37fb96 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -285,11 +285,12 @@
std::string RefreshRateSelector::Policy::toString() const {
return base::StringPrintf("{defaultModeId=%d, allowGroupSwitching=%s"
- ", primaryRanges=%s, appRequestRanges=%s}",
+ ", primaryRanges=%s, appRequestRanges=%s idleScreenConfig=%s}",
ftl::to_underlying(defaultMode),
allowGroupSwitching ? "true" : "false",
- to_string(primaryRanges).c_str(),
- to_string(appRequestRanges).c_str());
+ to_string(primaryRanges).c_str(), to_string(appRequestRanges).c_str(),
+ idleScreenConfigOpt ? idleScreenConfigOpt->toString().c_str()
+ : "nullptr");
}
std::pair<nsecs_t, nsecs_t> RefreshRateSelector::getDisplayFrames(nsecs_t layerPeriod,
@@ -861,7 +862,7 @@
// interactive (as opposed to ExplicitExactOrMultiple) and therefore if those posted an explicit
// vote we should not change it if we get a touch event. Only apply touch boost if it will
// actually increase the refresh rate over the normal selection.
- const bool touchBoostForExplicitExact = [&] {
+ const auto isTouchBoostForExplicitExact = [&]() -> bool {
if (supportsAppFrameRateOverrideByContent()) {
// Enable touch boost if there are other layers besides exact
return explicitExact + noVoteLayers + explicitGteLayers != layers.size();
@@ -869,13 +870,11 @@
// Enable touch boost if there are no exact layers
return explicitExact == 0;
}
- }();
+ };
- const bool touchBoostForCategory =
- explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
-
- const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
- using fps_approx_ops::operator<;
+ const auto isTouchBoostForCategory = [&]() -> bool {
+ return explicitCategoryVoteLayers + noVoteLayers + explicitGteLayers != layers.size();
+ };
// A method for UI Toolkit to send the touch signal via "HighHint" category vote,
// which will touch boost when there are no ExplicitDefault layer votes. This is an
@@ -883,13 +882,17 @@
// compatibility to limit the frame rate, which should not have touch boost.
const bool hasInteraction = signals.touch || interactiveLayers > 0;
- if (hasInteraction && explicitDefaultVoteLayers == 0 && touchBoostForExplicitExact &&
- touchBoostForCategory &&
- scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
- ALOGV("Touch Boost");
- ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
- to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
- return {touchRefreshRates, GlobalSignals{.touch = true}};
+ if (hasInteraction && explicitDefaultVoteLayers == 0 && isTouchBoostForExplicitExact() &&
+ isTouchBoostForCategory()) {
+ const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending);
+ using fps_approx_ops::operator<;
+
+ if (scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) {
+ ALOGV("Touch Boost");
+ ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])",
+ to_string(touchRefreshRates.front().frameRateMode.fps).c_str());
+ return {touchRefreshRates, GlobalSignals{.touch = true}};
+ }
}
// If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the
@@ -903,8 +906,8 @@
return {ascendingWithPreferred, kNoSignals};
}
- ALOGV("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
- ATRACE_FORMAT_INSTANT("%s (scored))", to_string(ranking.front().frameRateMode.fps).c_str());
+ ALOGV("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str());
+ ATRACE_FORMAT_INSTANT("%s (scored)", to_string(ranking.front().frameRateMode.fps).c_str());
return {ranking, kNoSignals};
}
@@ -1253,14 +1256,14 @@
RefreshRateSelector::RefreshRateSelector(DisplayModes modes, DisplayModeId activeModeId,
Config config)
: mKnownFrameRates(constructKnownFrameRates(modes)), mConfig(config) {
- initializeIdleTimer();
+ initializeIdleTimer(mConfig.legacyIdleTimerTimeout);
FTL_FAKE_GUARD(kMainThreadContext, updateDisplayModes(std::move(modes), activeModeId));
}
-void RefreshRateSelector::initializeIdleTimer() {
- if (mConfig.idleTimerTimeout > 0ms) {
+void RefreshRateSelector::initializeIdleTimer(std::chrono::milliseconds timeout) {
+ if (timeout > 0ms) {
mIdleTimer.emplace(
- "IdleTimer", mConfig.idleTimerTimeout,
+ "IdleTimer", timeout,
[this] {
std::scoped_lock lock(mIdleTimerCallbacksMutex);
if (const auto callbacks = getIdleTimerCallbacks()) {
@@ -1383,9 +1386,40 @@
mGetRankedFrameRatesCache.reset();
- if (*getCurrentPolicyLocked() == oldPolicy) {
+ const auto& idleScreenConfigOpt = getCurrentPolicyLocked()->idleScreenConfigOpt;
+ if (idleScreenConfigOpt != oldPolicy.idleScreenConfigOpt) {
+ if (!idleScreenConfigOpt.has_value()) {
+ // fallback to legacy timer if existed, otherwise pause the old timer
+ LOG_ALWAYS_FATAL_IF(!mIdleTimer);
+ if (mConfig.legacyIdleTimerTimeout > 0ms) {
+ mIdleTimer->setInterval(mConfig.legacyIdleTimerTimeout);
+ mIdleTimer->resume();
+ } else {
+ mIdleTimer->pause();
+ }
+ } else if (idleScreenConfigOpt->timeoutMillis > 0) {
+ // create a new timer or reconfigure
+ const auto timeout = std::chrono::milliseconds{idleScreenConfigOpt->timeoutMillis};
+ if (!mIdleTimer) {
+ initializeIdleTimer(timeout);
+ if (mIdleTimerStarted) {
+ mIdleTimer->start();
+ }
+ } else {
+ mIdleTimer->setInterval(timeout);
+ mIdleTimer->resume();
+ }
+ } else {
+ if (mIdleTimer) {
+ mIdleTimer->pause();
+ }
+ }
+ }
+
+ if (getCurrentPolicyLocked()->similarExceptIdleConfig(oldPolicy)) {
return SetPolicyResult::Unchanged;
}
+
constructAvailableRefreshRates();
displayId = getActiveModeLocked().modePtr->getPhysicalDisplayId();
@@ -1587,7 +1621,10 @@
}
std::chrono::milliseconds RefreshRateSelector::getIdleTimerTimeout() {
- return mConfig.idleTimerTimeout;
+ if (FlagManager::getInstance().idle_screen_refresh_rate_timeout() && mIdleTimer) {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(mIdleTimer->interval());
+ }
+ return mConfig.legacyIdleTimerTimeout;
}
// TODO(b/293651105): Extract category FpsRange mapping to OEM-configurable config.
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index a0e2785..4f491d9 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
@@ -67,26 +67,32 @@
FpsRanges primaryRanges;
// The app request refresh rate ranges. @see DisplayModeSpecs.aidl for details.
FpsRanges appRequestRanges;
+ // The idle timer configuration, if provided.
+ std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig> idleScreenConfigOpt;
Policy() = default;
Policy(DisplayModeId defaultMode, FpsRange range,
- bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault,
+ const std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig>&
+ idleScreenConfigOpt = std::nullopt)
: Policy(defaultMode, FpsRanges{range, range}, FpsRanges{range, range},
- allowGroupSwitching) {}
+ allowGroupSwitching, idleScreenConfigOpt) {}
Policy(DisplayModeId defaultMode, FpsRanges primaryRanges, FpsRanges appRequestRanges,
- bool allowGroupSwitching = kAllowGroupSwitchingDefault)
+ bool allowGroupSwitching = kAllowGroupSwitchingDefault,
+ const std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig>&
+ idleScreenConfigOpt = std::nullopt)
: defaultMode(defaultMode),
allowGroupSwitching(allowGroupSwitching),
primaryRanges(primaryRanges),
- appRequestRanges(appRequestRanges) {}
+ appRequestRanges(appRequestRanges),
+ idleScreenConfigOpt(idleScreenConfigOpt) {}
bool operator==(const Policy& other) const {
using namespace fps_approx_ops;
- return defaultMode == other.defaultMode && primaryRanges == other.primaryRanges &&
- appRequestRanges == other.appRequestRanges &&
- allowGroupSwitching == other.allowGroupSwitching;
+ return similarExceptIdleConfig(other) &&
+ idleScreenConfigOpt == other.idleScreenConfigOpt;
}
bool operator!=(const Policy& other) const { return !(*this == other); }
@@ -95,6 +101,13 @@
return isApproxEqual(primaryRanges.physical.min, primaryRanges.physical.max);
}
+ bool similarExceptIdleConfig(const Policy& updated) const {
+ using namespace fps_approx_ops;
+ return defaultMode == updated.defaultMode && primaryRanges == updated.primaryRanges &&
+ appRequestRanges == updated.appRequestRanges &&
+ allowGroupSwitching == updated.allowGroupSwitching;
+ }
+
std::string toString() const;
};
@@ -291,7 +304,7 @@
int frameRateMultipleThreshold = 0;
// The Idle Timer timeout. 0 timeout means no idle timer.
- std::chrono::milliseconds idleTimerTimeout = 0ms;
+ std::chrono::milliseconds legacyIdleTimerTimeout = 0ms;
// The controller representing how the kernel idle timer will be configured
// either on the HWC api or sysprop.
@@ -302,7 +315,7 @@
DisplayModes, DisplayModeId activeModeId,
Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
.frameRateMultipleThreshold = 0,
- .idleTimerTimeout = 0ms,
+ .legacyIdleTimerTimeout = 0ms,
.kernelIdleTimerController = {}});
RefreshRateSelector(const RefreshRateSelector&) = delete;
@@ -383,12 +396,14 @@
}
void startIdleTimer() {
+ mIdleTimerStarted = true;
if (mIdleTimer) {
mIdleTimer->start();
}
}
void stopIdleTimer() {
+ mIdleTimerStarted = false;
if (mIdleTimer) {
mIdleTimer->stop();
}
@@ -481,7 +496,7 @@
void updateDisplayModes(DisplayModes, DisplayModeId activeModeId) EXCLUDES(mLock)
REQUIRES(kMainThreadContext);
- void initializeIdleTimer();
+ void initializeIdleTimer(std::chrono::milliseconds timeout);
std::optional<IdleTimerCallbacks::Callbacks> getIdleTimerCallbacks() const
REQUIRES(mIdleTimerCallbacksMutex) {
@@ -562,6 +577,7 @@
std::optional<IdleTimerCallbacks> mIdleTimerCallbacks GUARDED_BY(mIdleTimerCallbacksMutex);
// Used to detect (lack of) frame activity.
ftl::Optional<scheduler::OneShotTimer> mIdleTimer;
+ std::atomic<bool> mIdleTimerStarted = false;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 7968096..005ec05 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -250,18 +250,6 @@
mPacesetterFrameDurationFractionToSkip = 0.f;
}
- if (FlagManager::getInstance().vrr_config()) {
- const auto minFramePeriod = pacesetterPtr->schedulePtr->minFramePeriod();
- const auto presentFenceForPastVsync =
- pacesetterPtr->targeterPtr->target().presentFenceForPastVsync(minFramePeriod);
- const auto lastConfirmedPresentTime = presentFenceForPastVsync->getSignalTime();
- if (lastConfirmedPresentTime != Fence::SIGNAL_TIME_PENDING &&
- lastConfirmedPresentTime != Fence::SIGNAL_TIME_INVALID) {
- pacesetterPtr->schedulePtr->getTracker()
- .onFrameBegin(expectedVsyncTime, TimePoint::fromNs(lastConfirmedPresentTime));
- }
- }
-
const auto resultsPerDisplay = compositor.composite(pacesetterPtr->displayId, targeters);
if (FlagManager::getInstance().vrr_config()) {
compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId);
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index ccb3aa7..ccaa05f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -346,7 +346,9 @@
// Used to skip event dispatch before EventThread creation during boot.
// TODO: b/241285191 - Reorder Scheduler initialization to avoid this.
bool hasEventThreads() const {
- return CC_LIKELY(mRenderEventThread && mLastCompositeEventThread);
+ return CC_LIKELY(
+ mRenderEventThread &&
+ (FlagManager::getInstance().deprecate_vsync_sf() || mLastCompositeEventThread));
}
EventThread& eventThreadFor(Cycle cycle) const {
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index b3c1f6b..0b47924 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -427,6 +427,7 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
+ mTimelines.clear();
clearTimestamps();
}
@@ -553,10 +554,23 @@
mLastTimestampIndex = 0;
}
- mTimelines.clear();
- mLastCommittedVsync = TimePoint::fromNs(0);
mIdealPeriod = Period::fromNs(idealPeriod());
- mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
+ if (mTimelines.empty()) {
+ mLastCommittedVsync = TimePoint::fromNs(0);
+ mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
+ } else {
+ while (mTimelines.size() > 1) {
+ mTimelines.pop_front();
+ }
+ mTimelines.front().setRenderRate(mRenderRateOpt);
+ // set mLastCommittedVsync to a valid vsync but don't commit too much in the future
+ const auto vsyncOpt = mTimelines.front().nextAnticipatedVSyncTimeFrom(
+ getVSyncPredictionModelLocked(),
+ /* minFramePeriodOpt */ std::nullopt,
+ snapToVsync(mClock->now()), MissedVsync{},
+ /* lastVsyncOpt */ std::nullopt);
+ mLastCommittedVsync = *vsyncOpt;
+ }
}
bool VSyncPredictor::needsMoreSamples() const {
@@ -588,6 +602,7 @@
if (mRenderRateOpt &&
mLastCommittedVsync.ns() + mRenderRateOpt->getPeriodNsecs() * kEnoughFramesToBreakPhase <
mClock->now()) {
+ ATRACE_FORMAT_INSTANT("kEnoughFramesToBreakPhase");
mTimelines.clear();
mLastCommittedVsync = TimePoint::fromNs(0);
mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, mRenderRateOpt);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 3ed1d41..8ce61d8 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -68,6 +68,13 @@
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
+ bool isCurrentMode(const ftl::NonNull<DisplayModePtr>& modePtr) const EXCLUDES(mMutex) {
+ std::lock_guard lock(mMutex);
+ return mDisplayModePtr->getId() == modePtr->getId() &&
+ mDisplayModePtr->getVsyncRate().getPeriodNsecs() ==
+ mRateMap.find(idealPeriod())->second.slope;
+ }
+
void setRenderRate(Fps, bool applyImmediately) final EXCLUDES(mMutex);
void onFrameBegin(TimePoint expectedPresentTime, TimePoint lastConfirmedPresentTime) final
@@ -83,7 +90,7 @@
};
struct MissedVsync {
- TimePoint vsync;
+ TimePoint vsync = TimePoint::fromNs(0);
Duration fixup = Duration::fromNs(0);
};
@@ -97,7 +104,7 @@
std::optional<TimePoint> validUntil() const { return mValidUntil; }
bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
void shiftVsyncSequence(Duration phase);
- void setRenderRate(Fps renderRate) { mRenderRateOpt = renderRate; }
+ void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
private:
nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 186a2d6..8038364 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -141,8 +141,7 @@
std::lock_guard lock(mMutex);
mLastHwVsync.reset();
- if (!mSupportKernelIdleTimer &&
- modePtr->getVsyncRate().getPeriodNsecs() == mTracker.currentPeriod() && !force) {
+ if (!mSupportKernelIdleTimer && mTracker.isCurrentMode(modePtr) && !force) {
endPeriodTransition();
setIgnorePresentFencesInternal(false);
mMoreSamplesNeeded = false;
@@ -217,6 +216,11 @@
mMoreSamplesNeeded = mTracker.needsMoreSamples();
}
+ if (mExternalIgnoreFences) {
+ // keep HWVSync on as long as we ignore present fences.
+ mMoreSamplesNeeded = true;
+ }
+
if (!mMoreSamplesNeeded) {
setIgnorePresentFencesInternal(false);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 8787cdb..134d28e 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -71,6 +71,11 @@
*/
virtual Period minFramePeriod() const = 0;
+ /**
+ * Checks if the sourced mode is equal to the mode in the tracker.
+ */
+ virtual bool isCurrentMode(const ftl::NonNull<DisplayModePtr>& modePtr) const = 0;
+
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
index a5bb6c2..d37d2dc 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameTargeter.h
@@ -33,6 +33,7 @@
// TODO(b/185536303): Pull to FTL.
#include "../../../TracedOrdinal.h"
#include "../../../Utils/Dumper.h"
+#include "../../../Utils/RingBuffer.h"
namespace android::scheduler {
@@ -61,7 +62,7 @@
// VSYNC of at least one previous frame has not yet passed. In other words, this is NOT the
// `presentFenceForPreviousFrame` if running N VSYNCs ahead, but the one that should have been
// signaled by now (unless that frame missed).
- const FenceTimePtr& presentFenceForPastVsync(Period minFramePeriod) const;
+ FenceTimePtr presentFenceForPastVsync(Period minFramePeriod) const;
// Equivalent to `presentFenceForPastVsync` unless running N VSYNCs ahead.
const FenceTimePtr& presentFenceForPreviousFrame() const {
@@ -71,6 +72,7 @@
bool isFramePending() const { return mFramePending; }
bool didMissFrame() const { return mFrameMissed; }
bool didMissHwcFrame() const { return mHwcFrameMissed && !mGpuFrameMissed; }
+ TimePoint lastSignaledFrameTime() const { return mLastSignaledFrameTime; };
protected:
explicit FrameTarget(const std::string& displayLabel);
@@ -83,6 +85,12 @@
return mExpectedPresentTime - minFramePeriod;
}
+ void addFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime,
+ TimePoint expectedPresentTime) {
+ mFenceWithFenceTimes.next() = {std::move(presentFence), presentFenceTime,
+ expectedPresentTime};
+ }
+
VsyncId mVsyncId;
TimePoint mFrameBeginTime;
TimePoint mExpectedPresentTime;
@@ -96,8 +104,12 @@
struct FenceWithFenceTime {
sp<Fence> fence = Fence::NO_FENCE;
FenceTimePtr fenceTime = FenceTime::NO_FENCE;
+ TimePoint expectedPresentTime = TimePoint();
};
std::array<FenceWithFenceTime, 2> mPresentFences;
+ utils::RingBuffer<FenceWithFenceTime, 5> mFenceWithFenceTimes;
+
+ TimePoint mLastSignaledFrameTime;
private:
friend class FrameTargeterTestBase;
@@ -107,6 +119,18 @@
static_assert(N > 1);
return expectedFrameDuration() > (N - 1) * minFramePeriod;
}
+
+ const FenceTimePtr pastVsyncTimePtr() const {
+ auto pastFenceTimePtr = FenceTime::NO_FENCE;
+ for (size_t i = 0; i < mFenceWithFenceTimes.size(); i++) {
+ const auto& [_, fenceTimePtr, expectedPresentTime] = mFenceWithFenceTimes[i];
+ if (expectedPresentTime > mFrameBeginTime) {
+ return pastFenceTimePtr;
+ }
+ pastFenceTimePtr = fenceTimePtr;
+ }
+ return pastFenceTimePtr;
+ }
};
// Computes a display's per-frame metrics about past/upcoming targeting of present deadlines.
diff --git a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
index 68c277d..badd21e 100644
--- a/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
+++ b/services/surfaceflinger/Scheduler/src/FrameTargeter.cpp
@@ -16,6 +16,7 @@
#include <gui/TraceUtils.h>
+#include <common/FlagManager.h>
#include <scheduler/FrameTargeter.h>
#include <scheduler/IVsyncSource.h>
@@ -33,8 +34,10 @@
return mExpectedPresentTime - Period::fromNs(minFramePeriod.ns() << shift);
}
-const FenceTimePtr& FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
- // TODO(b/267315508): Generalize to N VSYNCs.
+FenceTimePtr FrameTarget::presentFenceForPastVsync(Period minFramePeriod) const {
+ if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ return pastVsyncTimePtr();
+ }
const size_t i = static_cast<size_t>(targetsVsyncsAhead<2>(minFramePeriod));
return mPresentFences[i].fenceTime;
}
@@ -44,7 +47,8 @@
// should use `TimePoint::now()` in case of delays since `mFrameBeginTime`.
// TODO(b/267315508): Generalize to N VSYNCs.
- if (targetsVsyncsAhead<3>(minFramePeriod)) {
+ const bool allowNVsyncs = FlagManager::getInstance().allow_n_vsyncs_in_targeter();
+ if (!allowNVsyncs && targetsVsyncsAhead<3>(minFramePeriod)) {
return true;
}
@@ -113,6 +117,7 @@
mFrameMissed = mFramePending || [&] {
const nsecs_t pastPresentTime = pastPresentFence->getSignalTime();
if (pastPresentTime < 0) return false;
+ mLastSignaledFrameTime = TimePoint::fromNs(pastPresentTime);
const nsecs_t frameMissedSlop = vsyncPeriod.ns() / 2;
return lastScheduledPresentTime.ns() < pastPresentTime - frameMissedSlop;
}();
@@ -143,8 +148,12 @@
}
FenceTimePtr FrameTargeter::setPresentFence(sp<Fence> presentFence, FenceTimePtr presentFenceTime) {
- mPresentFences[1] = mPresentFences[0];
- mPresentFences[0] = {std::move(presentFence), presentFenceTime};
+ if (FlagManager::getInstance().allow_n_vsyncs_in_targeter()) {
+ addFence(std::move(presentFence), presentFenceTime, mExpectedPresentTime);
+ } else {
+ mPresentFences[1] = mPresentFences[0];
+ mPresentFences[0] = {std::move(presentFence), presentFenceTime, mExpectedPresentTime};
+ }
return presentFenceTime;
}
diff --git a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
index 29711af..5448eec 100644
--- a/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
+++ b/services/surfaceflinger/Scheduler/tests/FrameTargeterTest.cpp
@@ -24,6 +24,7 @@
#include <com_android_graphics_surfaceflinger_flags.h>
+using namespace com::android::graphics::surfaceflinger;
using namespace std::chrono_literals;
namespace android::scheduler {
@@ -168,6 +169,7 @@
}
TEST_F(FrameTargeterTest, recallsPastVsync) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{111};
TimePoint frameBeginTime(1000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -184,6 +186,7 @@
}
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAhead) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -203,8 +206,32 @@
}
}
+TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAhead) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+ VsyncId vsyncId{222};
+ TimePoint frameBeginTime(2000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 10ms;
+
+ FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate, kRefreshRate);
+ const auto fence = frame.end();
+
+ const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
+ EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+
+ frameBeginTime += kPeriod;
+ previousFence = fence;
+ }
+}
+
TEST_F(FrameTargeterTest, recallsPastVsyncTwoVsyncsAheadVrr) {
- SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::vrr_config, true);
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{222};
TimePoint frameBeginTime(2000ms);
@@ -227,6 +254,33 @@
}
}
+TEST_F(FrameTargeterTest, recallsPastNVsyncTwoVsyncsAheadVrr) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+
+ VsyncId vsyncId{222};
+ TimePoint frameBeginTime(2000ms);
+ constexpr Fps kRefreshRate = 120_Hz;
+ constexpr Fps kPeakRefreshRate = 240_Hz;
+ constexpr Period kPeriod = kRefreshRate.getPeriod();
+ constexpr Duration kFrameDuration = 10ms;
+
+ FenceTimePtr previousFence = FenceTime::NO_FENCE;
+
+ for (int n = 5; n-- > 0;) {
+ Frame frame(this, vsyncId++, frameBeginTime, kFrameDuration, kRefreshRate,
+ kPeakRefreshRate);
+ const auto fence = frame.end();
+
+ const auto pastVsyncTime = frameBeginTime + kFrameDuration - 2 * kPeriod;
+ EXPECT_EQ(target().pastVsyncTime(kPeriod), pastVsyncTime);
+ EXPECT_EQ(target().presentFenceForPastVsync(kFrameDuration), previousFence);
+
+ frameBeginTime += kPeriod;
+ previousFence = fence;
+ }
+}
+
TEST_F(FrameTargeterTest, doesNotDetectEarlyPresentIfNoFence) {
constexpr Period kPeriod = (60_Hz).getPeriod();
EXPECT_EQ(target().presentFenceForPastVsync(kPeriod), FenceTime::NO_FENCE);
@@ -234,6 +288,7 @@
}
TEST_F(FrameTargeterTest, detectsEarlyPresent) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -263,6 +318,7 @@
// Same as `detectsEarlyPresent`, above, but verifies that we do not set an earliest present time
// when there is expected present time support.
TEST_F(FrameTargeterWithExpectedPresentSupportTest, detectsEarlyPresent) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{333};
TimePoint frameBeginTime(3000ms);
constexpr Fps kRefreshRate = 60_Hz;
@@ -289,6 +345,7 @@
}
TEST_F(FrameTargeterTest, detectsEarlyPresentTwoVsyncsAhead) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
VsyncId vsyncId{444};
TimePoint frameBeginTime(4000ms);
constexpr Fps kRefreshRate = 120_Hz;
@@ -320,7 +377,52 @@
target().expectedPresentTime() - kPeriod - kHwcMinWorkDuration);
}
+TEST_F(FrameTargeterTest, detectsEarlyPresentNVsyncsAhead) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, true);
+ VsyncId vsyncId{444};
+ TimePoint frameBeginTime(4000ms);
+ Fps refreshRate = 120_Hz;
+ Period period = refreshRate.getPeriod();
+
+ // The target is not early while past present fences are pending.
+ for (int n = 5; n-- > 0;) {
+ const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+ EXPECT_FALSE(wouldPresentEarly(period));
+ EXPECT_FALSE(target().earliestPresentTime());
+ }
+
+ Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+ auto fence = frame.end();
+ frameBeginTime += period;
+ fence->signalForTest(frameBeginTime.ns());
+
+ // The target is two VSYNCs ahead, so the past present fence is still pending.
+ EXPECT_FALSE(wouldPresentEarly(period));
+ EXPECT_FALSE(target().earliestPresentTime());
+
+ { const Frame frame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate); }
+
+ Frame oneEarlyPresentFrame(this, vsyncId++, frameBeginTime, 10ms, refreshRate, refreshRate);
+ // The target is early if the past present fence was signaled.
+ EXPECT_TRUE(wouldPresentEarly(period));
+ ASSERT_NE(std::nullopt, target().earliestPresentTime());
+ EXPECT_EQ(*target().earliestPresentTime(),
+ target().expectedPresentTime() - period - kHwcMinWorkDuration);
+
+ fence = oneEarlyPresentFrame.end();
+ frameBeginTime += period;
+ fence->signalForTest(frameBeginTime.ns());
+
+ // Change rate to track frame more than 2 vsyncs ahead
+ refreshRate = 144_Hz;
+ period = refreshRate.getPeriod();
+ Frame onePresentEarlyFrame(this, vsyncId++, frameBeginTime, 16ms, refreshRate, refreshRate);
+ // The target is not early as last frame as the past frame is tracked for pending.
+ EXPECT_FALSE(wouldPresentEarly(period));
+}
+
TEST_F(FrameTargeterTest, detectsEarlyPresentThreeVsyncsAhead) {
+ SET_FLAG_FOR_TEST(flags::allow_n_vsyncs_in_targeter, false);
TimePoint frameBeginTime(5000ms);
constexpr Fps kRefreshRate = 144_Hz;
constexpr Period kPeriod = kRefreshRate.getPeriod();
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 6b117b8..5181fb8 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -65,7 +65,6 @@
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
#include <gui/IProducerListener.h>
-#include <gui/LayerDebugInfo.h>
#include <gui/LayerMetadata.h>
#include <gui/LayerState.h>
#include <gui/Surface.h>
@@ -73,6 +72,7 @@
#include <gui/TraceUtils.h>
#include <hidl/ServiceManagement.h>
#include <layerproto/LayerProtoParser.h>
+#include <linux/sched/types.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
#include <private/gui/SyncFeatures.h>
@@ -148,6 +148,7 @@
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RegionSamplingThread.h"
+#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
@@ -168,10 +169,6 @@
#define NO_THREAD_SAFETY_ANALYSIS \
_Pragma("GCC error \"Prefer <ftl/fake_guard.h> or MutexUtils.h helpers.\"")
-// To enable layer borders in the system, change the below flag to true.
-#undef DOES_CONTAIN_BORDER
-#define DOES_CONTAIN_BORDER false
-
namespace android {
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -232,7 +229,7 @@
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
}
-std::chrono::milliseconds getIdleTimerTimeout(DisplayId displayId) {
+std::chrono::milliseconds getIdleTimerTimeout(PhysicalDisplayId displayId) {
if (const int32_t displayIdleTimerMs =
base::GetIntProperty("debug.sf.set_idle_timer_ms_"s +
std::to_string(displayId.value),
@@ -246,7 +243,7 @@
return std::chrono::milliseconds(millis);
}
-bool getKernelIdleTimerSyspropConfig(DisplayId displayId) {
+bool getKernelIdleTimerSyspropConfig(PhysicalDisplayId displayId) {
const bool displaySupportKernelIdleTimer =
base::GetBoolProperty("debug.sf.support_kernel_idle_timer_"s +
std::to_string(displayId.value),
@@ -428,7 +425,8 @@
mInternalDisplayDensity(
getDensityFromProperty("ro.sf.lcd_density", !mEmulatedDisplayDensity)),
mPowerAdvisor(std::make_unique<Hwc2::impl::PowerAdvisor>(*this)),
- mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()) {
+ mWindowInfosListenerInvoker(sp<WindowInfosListenerInvoker>::make()),
+ mSkipPowerOnForQuiescent(base::GetBoolProperty("ro.boot.quiescent"s, false)) {
ALOGI("Using HWComposer service: %s", mHwcServiceName.c_str());
}
@@ -535,8 +533,6 @@
mLayerLifecycleManagerEnabled =
base::GetBoolProperty("persist.debug.sf.enable_layer_lifecycle_manager"s, true);
- mLegacyFrontEndEnabled = !mLayerLifecycleManagerEnabled ||
- base::GetBoolProperty("persist.debug.sf.enable_legacy_frontend"s, false);
// These are set by the HWC implementation to indicate that they will use the workarounds.
mIsHotplugErrViaNegVsync =
@@ -798,6 +794,8 @@
char prop[PROPERTY_VALUE_MAX];
property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+ // TODO: b/293371537 - Once GraphiteVk is deemed relatively stable, log a warning that
+ // PROPERTY_DEBUG_RENDERENGINE_BACKEND is deprecated
if (strcmp(prop, "skiagl") == 0) {
builder.setThreaded(renderengine::RenderEngine::Threaded::NO)
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::GL);
@@ -812,12 +810,35 @@
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else {
const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK;
- const bool useVulkan = FlagManager::getInstance().vulkan_renderengine() &&
+ const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
renderengine::RenderEngine::canSupport(kVulkan);
+ const bool useVulkan = useGraphite ||
+ (FlagManager::getInstance().vulkan_renderengine() &&
+ renderengine::RenderEngine::canSupport(kVulkan));
+
+ builder.setSkiaBackend(useGraphite ? renderengine::RenderEngine::SkiaBackend::GRAPHITE
+ : renderengine::RenderEngine::SkiaBackend::GANESH);
builder.setGraphicsApi(useVulkan ? kVulkan : renderengine::RenderEngine::GraphicsApi::GL);
}
}
+/**
+ * Choose a suggested blurring algorithm if supportsBlur is true. By default Kawase will be
+ * suggested as it's faster than a full Gaussian blur and looks close enough.
+ */
+renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) {
+ if (!supportsBlur) {
+ return renderengine::RenderEngine::BlurAlgorithm::NONE;
+ }
+
+ auto const algorithm = base::GetProperty(PROPERTY_DEBUG_RENDERENGINE_BLUR_ALGORITHM, "");
+ if (algorithm == "gaussian") {
+ return renderengine::RenderEngine::BlurAlgorithm::GAUSSIAN;
+ } else {
+ return renderengine::RenderEngine::BlurAlgorithm::KAWASE;
+ }
+}
+
void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) {
ATRACE_CALL();
ALOGI( "SurfaceFlinger's main thread ready to run. "
@@ -833,7 +854,7 @@
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
+ .setBlurAlgorithm(chooseBlurAlgorithm(mSupportsBlur))
.setContextPriority(
useContextPriority
? renderengine::RenderEngine::ContextPriority::REALTIME
@@ -1839,19 +1860,6 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
- outLayers->clear();
- auto future = mScheduler->schedule([=, this] {
- const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
- mDrawingState.traverseInZOrder([&](Layer* layer) {
- outLayers->push_back(layer->getLayerDebugInfo(display.get()));
- });
- });
-
- future.wait();
- return NO_ERROR;
-}
-
status_t SurfaceFlinger::getCompositionPreference(
Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
Dataspace* outWideColorGamutDataspace,
@@ -2084,10 +2092,17 @@
sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection(
gui::ISurfaceComposer::VsyncSource vsyncSource, EventRegistrationFlags eventRegistration,
const sp<IBinder>& layerHandle) {
- const auto cycle = vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
- ? scheduler::Cycle::LastComposite
- : scheduler::Cycle::Render;
+ const auto cycle = [&] {
+ if (FlagManager::getInstance().deprecate_vsync_sf()) {
+ ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger,
+ "requested unsupported config eVsyncSourceSurfaceFlinger");
+ return scheduler::Cycle::Render;
+ }
+ return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger
+ ? scheduler::Cycle::LastComposite
+ : scheduler::Cycle::Render;
+ }();
return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle);
}
@@ -2211,19 +2226,20 @@
void SurfaceFlinger::onRefreshRateChangedDebug(const RefreshRateChangedDebugData& data) {
ATRACE_CALL();
- if (const auto displayId = getHwComposer().toPhysicalDisplayId(data.display); displayId) {
- const char* const whence = __func__;
- static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) {
- const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported()
- ? data.refreshPeriodNanos
- : data.vsyncPeriodNanos);
- ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue());
- const auto display = getDisplayDeviceLocked(*displayId);
- FTL_FAKE_GUARD(kMainThreadContext,
- display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps,
- /* setByHwc */ true));
- }));
- }
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
+ kMainThreadContext) {
+ if (const auto displayIdOpt = getHwComposer().toPhysicalDisplayId(data.display)) {
+ if (const auto display = getDisplayDeviceLocked(*displayIdOpt)) {
+ const Fps fps = Fps::fromPeriodNsecs(getHwComposer().getComposer()->isVrrSupported()
+ ? data.refreshPeriodNanos
+ : data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s Fps %d", whence, fps.getIntValue());
+ display->updateRefreshRateOverlayRate(fps, display->getActiveMode().fps,
+ /* setByHwc */ true);
+ }
+ }
+ }));
}
void SurfaceFlinger::configure() {
@@ -2256,7 +2272,7 @@
outTransactionsAreEmpty = !needsTraversal;
const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
if (shouldCommit) {
- commitTransactions();
+ commitTransactionsLegacy();
}
bool mustComposite = latchBuffers() || shouldCommit;
@@ -2380,14 +2396,22 @@
mLayerHierarchyBuilder.update(mLayerLifecycleManager);
}
+ // Keep a copy of the drawing state (that is going to be overwritten
+ // by commitTransactionsLocked) outside of mStateLock so that the side
+ // effects of the State assignment don't happen with mStateLock held,
+ // which can cause deadlocks.
+ State drawingState(mDrawingState);
+ Mutex::Autolock lock(mStateLock);
bool mustComposite = false;
- mustComposite |= applyAndCommitDisplayTransactionStates(update.transactions);
+ mustComposite |= applyAndCommitDisplayTransactionStatesLocked(update.transactions);
{
ATRACE_NAME("LayerSnapshotBuilder:update");
frontend::LayerSnapshotBuilder::Args
args{.root = mLayerHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLayerLifecycleManager,
+ .includeMetadata = mCompositionEngine->getFeatureFlags().test(
+ compositionengine::Feature::kSnapshotLayerMetadata),
.displays = mFrontEndDisplayInfos,
.displayChanges = mFrontEndDisplayInfosChanged,
.globalShadowSettings = mDrawingState.globalShadowSettings,
@@ -2418,86 +2442,84 @@
mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
bool newDataLatched = false;
- if (!mLegacyFrontEndEnabled) {
- ATRACE_NAME("DisplayCallbackAndStatsUpdates");
- mustComposite |= applyTransactions(update.transactions, vsyncId);
- traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
- const nsecs_t latchTime = systemTime();
- bool unused = false;
+ ATRACE_NAME("DisplayCallbackAndStatsUpdates");
+ mustComposite |= applyTransactionsLocked(update.transactions, vsyncId);
+ traverseLegacyLayers([&](Layer* layer) { layer->commitTransaction(); });
+ const nsecs_t latchTime = systemTime();
+ bool unused = false;
- for (auto& layer : mLayerLifecycleManager.getLayers()) {
- if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
- layer->bgColorLayer) {
- sp<Layer> bgColorLayer = getFactory().createEffectLayer(
- LayerCreationArgs(this, nullptr, layer->name,
- ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
- std::make_optional(layer->id), true));
- mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
- }
- const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
+ for (auto& layer : mLayerLifecycleManager.getLayers()) {
+ if (layer->changes.test(frontend::RequestedLayerState::Changes::Created) &&
+ layer->bgColorLayer) {
+ sp<Layer> bgColorLayer = getFactory().createEffectLayer(
+ LayerCreationArgs(this, nullptr, layer->name,
+ ISurfaceComposerClient::eFXSurfaceEffect, LayerMetadata(),
+ std::make_optional(layer->id), true));
+ mLegacyLayers[bgColorLayer->sequence] = bgColorLayer;
+ }
+ const bool willReleaseBufferOnLatch = layer->willReleaseBufferOnLatch();
- auto it = mLegacyLayers.find(layer->id);
- if (it == mLegacyLayers.end() &&
- layer->changes.test(frontend::RequestedLayerState::Changes::Destroyed)) {
- // Layer handle was created and immediately destroyed. It was destroyed before it
- // was added to the map.
- continue;
- }
-
- LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- layer->getDebugString().c_str());
- if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
- if (!it->second->hasBuffer()) {
- // The last latch time is used to classify a missed frame as buffer stuffing
- // instead of a missed frame. This is used to identify scenarios where we
- // could not latch a buffer or apply a transaction due to backpressure.
- // We only update the latch time for buffer less layers here, the latch time
- // is updated for buffer layers when the buffer is latched.
- it->second->updateLastLatchTime(latchTime);
- }
- continue;
- }
-
- const bool bgColorOnly =
- !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
- if (willReleaseBufferOnLatch) {
- mLayersWithBuffersRemoved.emplace(it->second);
- }
- it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
- newDataLatched = true;
-
- mLayersWithQueuedFrames.emplace(it->second);
- mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
+ auto it = mLegacyLayers.find(layer->id);
+ if (it == mLegacyLayers.end() &&
+ layer->changes.test(frontend::RequestedLayerState::Changes::Destroyed)) {
+ // Layer handle was created and immediately destroyed. It was destroyed before it
+ // was added to the map.
+ continue;
}
- updateLayerHistory(latchTime);
- mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) ==
- mLayersIdsWithQueuedFrames.end())
- return;
- Region visibleReg;
- visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
- invalidateLayerStack(snapshot.outputFilter, visibleReg);
- });
-
- for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
- mLegacyLayers.erase(destroyedLayer->id);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ layer->getDebugString().c_str());
+ if (!layer->hasReadyFrame() && !willReleaseBufferOnLatch) {
+ if (!it->second->hasBuffer()) {
+ // The last latch time is used to classify a missed frame as buffer stuffing
+ // instead of a missed frame. This is used to identify scenarios where we
+ // could not latch a buffer or apply a transaction due to backpressure.
+ // We only update the latch time for buffer less layers here, the latch time
+ // is updated for buffer layers when the buffer is latched.
+ it->second->updateLastLatchTime(latchTime);
+ }
+ continue;
}
- {
- ATRACE_NAME("LLM:commitChanges");
- mLayerLifecycleManager.commitChanges();
+ const bool bgColorOnly =
+ !layer->externalTexture && (layer->bgColorLayerId != UNASSIGNED_LAYER_ID);
+ if (willReleaseBufferOnLatch) {
+ mLayersWithBuffersRemoved.emplace(it->second);
}
+ it->second->latchBufferImpl(unused, latchTime, bgColorOnly);
+ newDataLatched = true;
- // enter boot animation on first buffer latch
- if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
- ALOGI("Enter boot animation");
- mBootStage = BootStage::BOOTANIMATION;
- }
+ mLayersWithQueuedFrames.emplace(it->second);
+ mLayersIdsWithQueuedFrames.emplace(it->second->sequence);
}
+
+ updateLayerHistory(latchTime);
+ mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
+ if (mLayersIdsWithQueuedFrames.find(snapshot.path.id) == mLayersIdsWithQueuedFrames.end())
+ return;
+ Region visibleReg;
+ visibleReg.set(snapshot.transformedBoundsWithoutTransparentRegion);
+ invalidateLayerStack(snapshot.outputFilter, visibleReg);
+ });
+
+ for (auto& destroyedLayer : mLayerLifecycleManager.getDestroyedLayers()) {
+ mLegacyLayers.erase(destroyedLayer->id);
+ }
+
+ {
+ ATRACE_NAME("LLM:commitChanges");
+ mLayerLifecycleManager.commitChanges();
+ }
+
+ // enter boot animation on first buffer latch
+ if (CC_UNLIKELY(mBootStage == BootStage::BOOTLOADER && newDataLatched)) {
+ ALOGI("Enter boot animation");
+ mBootStage = BootStage::BOOTANIMATION;
+ }
+
mustComposite |= (getTransactionFlags() & ~eTransactionFlushNeeded) || newDataLatched;
- if (mustComposite && !mLegacyFrontEndEnabled) {
+ if (mustComposite) {
commitTransactions();
}
@@ -2599,18 +2621,21 @@
mScheduler->getPacesetterRefreshRate());
const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
- bool transactionsAreEmpty;
- if (mLegacyFrontEndEnabled) {
- mustComposite |=
- updateLayerSnapshotsLegacy(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
- flushTransactions, transactionsAreEmpty);
- }
+ bool transactionsAreEmpty = false;
if (mLayerLifecycleManagerEnabled) {
mustComposite |=
updateLayerSnapshots(vsyncId, pacesetterFrameTarget.frameBeginTime().ns(),
flushTransactions, transactionsAreEmpty);
}
+ // Tell VsyncTracker that we are going to present this frame before scheduling
+ // setTransactionFlags which will schedule another SF frame. This was if the tracker
+ // needs to adjust the vsync timeline, it will be done before the next frame.
+ if (FlagManager::getInstance().vrr_config() && mustComposite) {
+ mScheduler->getVsyncSchedule()->getTracker().onFrameBegin(
+ pacesetterFrameTarget.expectedPresentTime(),
+ pacesetterFrameTarget.lastSignaledFrameTime());
+ }
if (transactionFlushNeeded()) {
setTransactionFlags(eTransactionFlushNeeded);
}
@@ -2704,21 +2729,6 @@
mLayerMetadataSnapshotNeeded = false;
}
- if (DOES_CONTAIN_BORDER) {
- refreshArgs.borderInfoList.clear();
- mDrawingState.traverse([&refreshArgs](Layer* layer) {
- if (layer->isBorderEnabled()) {
- compositionengine::BorderRenderInfo info;
- info.width = layer->getBorderWidth();
- info.color = layer->getBorderColor();
- layer->traverse(LayerVector::StateSet::Drawing, [&info](Layer* ilayer) {
- info.layerIds.push_back(ilayer->getSequence());
- });
- refreshArgs.borderInfoList.emplace_back(std::move(info));
- }
- });
- }
-
refreshArgs.bufferIdsToUncache = std::move(mBufferIdsToUncache);
if (!FlagManager::getInstance().ce_fence_promise()) {
@@ -3183,7 +3193,8 @@
if (mLayerLifecycleManagerEnabled) {
mLayerSnapshotBuilder.forEachVisibleSnapshot(
[&, compositionDisplay = compositionDisplay](
- std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ std::unique_ptr<frontend::LayerSnapshot>&
+ snapshot) FTL_FAKE_GUARD(kMainThreadContext) {
auto it = mLegacyLayers.find(snapshot->sequence);
LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
"Couldnt find layer object for %s",
@@ -3242,7 +3253,7 @@
if (mNumTrustedPresentationListeners > 0) {
// We avoid any reverse traversal upwards so this shouldn't be too expensive
- traverseLegacyLayers([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
if (!layer->hasTrustedPresentationListener()) {
return;
}
@@ -3302,6 +3313,19 @@
void SurfaceFlinger::commitTransactions() {
ATRACE_CALL();
+ mDebugInTransaction = systemTime();
+
+ // Here we're guaranteed that some transaction flags are set
+ // so we can call commitTransactionsLocked unconditionally.
+ // We clear the flags with mStateLock held to guarantee that
+ // mCurrentState won't change until the transaction is committed.
+ mScheduler->modulateVsync({}, &VsyncModulator::onTransactionCommit);
+ commitTransactionsLocked(clearTransactionFlags(eTransactionMask));
+ mDebugInTransaction = 0;
+}
+
+void SurfaceFlinger::commitTransactionsLegacy() {
+ ATRACE_CALL();
// Keep a copy of the drawing state (that is going to be overwritten
// by commitTransactionsLocked) outside of mStateLock so that the side
@@ -3486,12 +3510,45 @@
for (const auto [hwcDisplayId, connection] : events) {
if (auto info = getHwComposer().onHotplug(hwcDisplayId, connection)) {
const auto displayId = info->id;
- const bool connected = connection == hal::Connection::CONNECTED;
+ const ftl::Concat displayString("display ", displayId.value, "(HAL ID ", hwcDisplayId,
+ ')');
- if (const char* const log =
- processHotplug(displayId, hwcDisplayId, connected, std::move(*info))) {
- ALOGI("%s display %s (HAL ID %" PRIu64 ")", log, to_string(displayId).c_str(),
- hwcDisplayId);
+ if (connection == hal::Connection::CONNECTED) {
+ const auto activeModeIdOpt =
+ processHotplugConnect(displayId, hwcDisplayId, std::move(*info),
+ displayString.c_str());
+ if (!activeModeIdOpt) {
+ if (FlagManager::getInstance().hotplug2()) {
+ mScheduler->dispatchHotplugError(
+ static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
+ }
+ getHwComposer().disconnectDisplay(displayId);
+ continue;
+ }
+
+ const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
+ getKernelIdleTimerProperties(displayId);
+
+ using Config = scheduler::RefreshRateSelector::Config;
+ const Config config =
+ {.enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
+ ? Config::FrameRateOverride::Enabled
+ : Config::FrameRateOverride::Disabled,
+ .frameRateMultipleThreshold =
+ base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0),
+ .legacyIdleTimerTimeout = idleTimerTimeoutMs,
+ .kernelIdleTimerController = kernelIdleTimerController};
+
+ const auto snapshotOpt =
+ mPhysicalDisplays.get(displayId).transform(&PhysicalDisplay::snapshotRef);
+ LOG_ALWAYS_FATAL_IF(!snapshotOpt);
+
+ mDisplayModeController.registerDisplay(*snapshotOpt, *activeModeIdOpt, config);
+ } else {
+ // Unregister before destroying the DisplaySnapshot below.
+ mDisplayModeController.unregisterDisplay(displayId);
+
+ processHotplugDisconnect(displayId, displayString.c_str());
}
}
}
@@ -3499,36 +3556,20 @@
return !events.empty();
}
-const char* SurfaceFlinger::processHotplug(PhysicalDisplayId displayId,
- hal::HWDisplayId hwcDisplayId, bool connected,
- DisplayIdentificationInfo&& info) {
- const auto displayOpt = mPhysicalDisplays.get(displayId);
- if (!connected) {
- LOG_ALWAYS_FATAL_IF(!displayOpt);
- const auto& display = displayOpt->get();
-
- if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
- mCurrentState.displays.removeItemsAt(index);
- }
-
- mPhysicalDisplays.erase(displayId);
- return "Disconnecting";
- }
-
+std::optional<DisplayModeId> SurfaceFlinger::processHotplugConnect(PhysicalDisplayId displayId,
+ hal::HWDisplayId hwcDisplayId,
+ DisplayIdentificationInfo&& info,
+ const char* displayString) {
auto [displayModes, activeMode] = loadDisplayModes(displayId);
if (!activeMode) {
- ALOGE("Failed to hotplug display %s", to_string(displayId).c_str());
- if (FlagManager::getInstance().hotplug2()) {
- mScheduler->dispatchHotplugError(
- static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN));
- }
- getHwComposer().disconnectDisplay(displayId);
- return nullptr;
+ ALOGE("Failed to hotplug %s", displayString);
+ return std::nullopt;
}
+ const DisplayModeId activeModeId = activeMode->getId();
ui::ColorModes colorModes = getHwComposer().getColorModes(displayId);
- if (displayOpt) {
+ if (const auto displayOpt = mPhysicalDisplays.get(displayId)) {
const auto& display = displayOpt->get();
const auto& snapshot = display.snapshot();
@@ -3547,7 +3588,8 @@
auto& state = mCurrentState.displays.editValueFor(it->second.token());
state.sequenceId = DisplayDeviceState{}.sequenceId; // Generate new sequenceId.
state.physical->activeMode = std::move(activeMode);
- return "Reconnecting";
+ ALOGI("Reconnecting %s", displayString);
+ return activeModeId;
}
const sp<IBinder> token = sp<BBinder>::make();
@@ -3567,7 +3609,23 @@
state.displayName = std::move(info.name);
mCurrentState.displays.add(token, state);
- return "Connecting";
+ ALOGI("Connecting %s", displayString);
+ return activeModeId;
+}
+
+void SurfaceFlinger::processHotplugDisconnect(PhysicalDisplayId displayId,
+ const char* displayString) {
+ ALOGI("Disconnecting %s", displayString);
+
+ const auto displayOpt = mPhysicalDisplays.get(displayId);
+ LOG_ALWAYS_FATAL_IF(!displayOpt);
+ const auto& display = displayOpt->get();
+
+ if (const ssize_t index = mCurrentState.displays.indexOfKey(display.token()); index >= 0) {
+ mCurrentState.displays.removeItemsAt(index);
+ }
+
+ mPhysicalDisplays.erase(displayId);
}
void SurfaceFlinger::dispatchDisplayModeChangeEvent(PhysicalDisplayId displayId,
@@ -3595,43 +3653,21 @@
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
- if (const auto& physical = state.physical) {
- creationArgs.activeModeId = physical->activeMode->getId();
- const auto [kernelIdleTimerController, idleTimerTimeoutMs] =
- getKernelIdleTimerProperties(compositionDisplay->getId());
+ if (const auto physicalIdOpt = PhysicalDisplayId::tryCast(compositionDisplay->getId())) {
+ const auto physicalId = *physicalIdOpt;
- using Config = scheduler::RefreshRateSelector::Config;
- const auto enableFrameRateOverride = sysprop::enable_frame_rate_override(true)
- ? Config::FrameRateOverride::Enabled
- : Config::FrameRateOverride::Disabled;
- const Config config =
- {.enableFrameRateOverride = enableFrameRateOverride,
- .frameRateMultipleThreshold =
- base::GetIntProperty("debug.sf.frame_rate_multiple_threshold"s, 0),
- .idleTimerTimeout = idleTimerTimeoutMs,
- .kernelIdleTimerController = kernelIdleTimerController};
-
+ creationArgs.isPrimary = physicalId == getPrimaryDisplayIdLocked();
creationArgs.refreshRateSelector =
- mPhysicalDisplays.get(physical->id)
- .transform(&PhysicalDisplay::snapshotRef)
- .transform([&](const display::DisplaySnapshot& snapshot) {
- return std::make_shared<
- scheduler::RefreshRateSelector>(snapshot.displayModes(),
- creationArgs.activeModeId,
- config);
- })
- .value_or(nullptr);
+ FTL_FAKE_GUARD(kMainThreadContext,
+ mDisplayModeController.selectorPtrFor(physicalId));
- creationArgs.isPrimary = physical->id == getPrimaryDisplayIdLocked();
-
- mPhysicalDisplays.get(physical->id)
+ mPhysicalDisplays.get(physicalId)
.transform(&PhysicalDisplay::snapshotRef)
.transform(ftl::unit_fn([&](const display::DisplaySnapshot& snapshot) {
for (const auto mode : snapshot.colorModes()) {
creationArgs.hasWideColorGamut |= ui::isWideColorMode(mode);
creationArgs.hwcColorModes
- .emplace(mode,
- getHwComposer().getRenderIntents(physical->id, mode));
+ .emplace(mode, getHwComposer().getRenderIntents(physicalId, mode));
}
}));
}
@@ -3849,7 +3885,9 @@
if (currentState.physical) {
const auto display = getDisplayDeviceLocked(displayToken);
- setPowerModeInternal(display, hal::PowerMode::ON);
+ if (!mSkipPowerOnForQuiescent) {
+ setPowerModeInternal(display, hal::PowerMode::ON);
+ }
// TODO(b/175678251) Call a listener instead.
if (currentState.physical->hwcDisplayId == getHwComposer().getPrimaryHwcDisplayId()) {
@@ -3991,19 +4029,7 @@
}
}
- if (!hintDisplay) {
- // NOTE: TEMPORARY FIX ONLY. Real fix should cause layers to
- // redraw after transform hint changes. See bug 8508397.
- // could be null when this layer is using a layerStack
- // that is not visible on any display. Also can occur at
- // screen off/on times.
- // U Update: Don't provide stale hints to the clients. For
- // special cases where we want the app to draw its
- // first frame before the display is available, we rely
- // on WMS and DMS to provide the right information
- // so the client can calculate the hint.
- layer->skipReportingTransformHint();
- } else {
+ if (hintDisplay) {
layer->updateTransformHint(hintDisplay->getTransformHint());
}
});
@@ -4142,7 +4168,7 @@
outWindowInfos.push_back(snapshot.inputInfo);
});
} else {
- mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
+ mDrawingState.traverseInReverseZOrder([&](Layer* layer) FTL_FAKE_GUARD(kMainThreadContext) {
if (!layer->needsInputInfo()) return;
const auto opt =
mFrontEndDisplayInfos.get(layer->getLayerStack())
@@ -4847,6 +4873,8 @@
if (listener &&
(flushState.queueProcessTime - transaction.postTime) >
std::chrono::nanoseconds(4s).count()) {
+ // Used to add a stalled transaction which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
mTransactionHandler
.onTransactionQueueStalled(transaction.id,
{.pid = layer->getOwnerPid(),
@@ -4869,97 +4897,107 @@
const TransactionHandler::TransactionFlushState& flushState) {
using TransactionReadiness = TransactionHandler::TransactionReadiness;
auto ready = TransactionReadiness::Ready;
- flushState.transaction->traverseStatesWithBuffersWhileTrue([&](const ResolvedComposerState&
- resolvedState) -> bool {
- const frontend::RequestedLayerState* layer =
- mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
- const auto& transaction = *flushState.transaction;
- const auto& s = resolvedState.state;
- // check for barrier frames
- if (s.bufferData->hasBarrier) {
- // The current producerId is already a newer producer than the buffer that has a
- // barrier. This means the incoming buffer is older and we can release it here. We
- // don't wait on the barrier since we know that's stale information.
- if (layer->barrierProducerId > s.bufferData->producerId) {
- if (s.bufferData->releaseBufferListener) {
- uint32_t currentMaxAcquiredBufferCount =
- getMaxAcquiredBufferCountForCurrentRefreshRate(layer->ownerUid.val());
- ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
- layer->name.c_str(), s.bufferData->frameNumber);
- s.bufferData->releaseBufferListener
- ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()->getId(),
- s.bufferData->frameNumber},
- s.bufferData->acquireFence
- ? s.bufferData->acquireFence
- : Fence::NO_FENCE,
- currentMaxAcquiredBufferCount);
+ flushState.transaction->traverseStatesWithBuffersWhileTrue(
+ [&](const ResolvedComposerState& resolvedState) FTL_FAKE_GUARD(
+ kMainThreadContext) -> bool {
+ const frontend::RequestedLayerState* layer =
+ mLayerLifecycleManager.getLayerFromId(resolvedState.layerId);
+ const auto& transaction = *flushState.transaction;
+ const auto& s = resolvedState.state;
+ // check for barrier frames
+ if (s.bufferData->hasBarrier) {
+ // The current producerId is already a newer producer than the buffer that has a
+ // barrier. This means the incoming buffer is older and we can release it here.
+ // We don't wait on the barrier since we know that's stale information.
+ if (layer->barrierProducerId > s.bufferData->producerId) {
+ if (s.bufferData->releaseBufferListener) {
+ uint32_t currentMaxAcquiredBufferCount =
+ getMaxAcquiredBufferCountForCurrentRefreshRate(
+ layer->ownerUid.val());
+ ATRACE_FORMAT_INSTANT("callReleaseBufferCallback %s - %" PRIu64,
+ layer->name.c_str(), s.bufferData->frameNumber);
+ s.bufferData->releaseBufferListener
+ ->onReleaseBuffer({resolvedState.externalTexture->getBuffer()
+ ->getId(),
+ s.bufferData->frameNumber},
+ s.bufferData->acquireFence
+ ? s.bufferData->acquireFence
+ : Fence::NO_FENCE,
+ currentMaxAcquiredBufferCount);
+ }
+
+ // Delete the entire state at this point and not just release the buffer
+ // because everything associated with the Layer in this Transaction is now
+ // out of date.
+ ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d",
+ layer->name.c_str(), layer->barrierProducerId,
+ s.bufferData->producerId);
+ return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
+ }
+
+ if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
+ const bool willApplyBarrierFrame =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
+ ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
+ s.bufferData->barrierFrameNumber));
+ if (!willApplyBarrierFrame) {
+ ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64
+ " > %" PRId64,
+ layer->name.c_str(), layer->barrierFrameNumber,
+ s.bufferData->barrierFrameNumber);
+ ready = TransactionReadiness::NotReadyBarrier;
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
+ }
}
- // Delete the entire state at this point and not just release the buffer because
- // everything associated with the Layer in this Transaction is now out of date.
- ATRACE_FORMAT("DeleteStaleBuffer %s barrierProducerId:%d > %d", layer->name.c_str(),
- layer->barrierProducerId, s.bufferData->producerId);
- return TraverseBuffersReturnValues::DELETE_AND_CONTINUE_TRAVERSAL;
- }
-
- if (layer->barrierFrameNumber < s.bufferData->barrierFrameNumber) {
- const bool willApplyBarrierFrame =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get()) &&
- ((flushState.bufferLayersReadyToPresent.get(s.surface.get()) >=
- s.bufferData->barrierFrameNumber));
- if (!willApplyBarrierFrame) {
- ATRACE_FORMAT("NotReadyBarrier %s barrierFrameNumber:%" PRId64 " > %" PRId64,
- layer->name.c_str(), layer->barrierFrameNumber,
- s.bufferData->barrierFrameNumber);
- ready = TransactionReadiness::NotReadyBarrier;
+ // If backpressure is enabled and we already have a buffer to commit, keep
+ // the transaction in the queue.
+ const bool hasPendingBuffer =
+ flushState.bufferLayersReadyToPresent.contains(s.surface.get());
+ if (layer->backpressureEnabled() && hasPendingBuffer &&
+ transaction.isAutoTimestamp) {
+ ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
+ ready = TransactionReadiness::NotReady;
return TraverseBuffersReturnValues::STOP_TRAVERSAL;
}
- }
- }
- // If backpressure is enabled and we already have a buffer to commit, keep
- // the transaction in the queue.
- const bool hasPendingBuffer =
- flushState.bufferLayersReadyToPresent.contains(s.surface.get());
- if (layer->backpressureEnabled() && hasPendingBuffer && transaction.isAutoTimestamp) {
- ATRACE_FORMAT("hasPendingBuffer %s", layer->name.c_str());
- ready = TransactionReadiness::NotReady;
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
-
- const bool acquireFenceAvailable = s.bufferData &&
- s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
- s.bufferData->acquireFence;
- const bool fenceSignaled = !acquireFenceAvailable ||
- s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
- if (!fenceSignaled) {
- // check fence status
- const bool allowLatchUnsignaled = shouldLatchUnsignaled(s, transaction.states.size(),
- flushState.firstTransaction) &&
- layer->isSimpleBufferUpdate(s);
- if (allowLatchUnsignaled) {
- ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s", layer->name.c_str());
- ready = TransactionReadiness::NotReadyUnsignaled;
- } else {
- ready = TransactionReadiness::NotReady;
- auto& listener = s.bufferData->releaseBufferListener;
- if (listener &&
- (flushState.queueProcessTime - transaction.postTime) >
- std::chrono::nanoseconds(4s).count()) {
- mTransactionHandler
- .onTransactionQueueStalled(transaction.id,
- {.pid = layer->ownerPid.val(),
- .layerId = layer->id,
- .layerName = layer->name,
- .bufferId = s.bufferData->getId(),
- .frameNumber = s.bufferData->frameNumber});
+ const bool acquireFenceAvailable = s.bufferData &&
+ s.bufferData->flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ s.bufferData->acquireFence;
+ const bool fenceSignaled = !acquireFenceAvailable ||
+ s.bufferData->acquireFence->getStatus() != Fence::Status::Unsignaled;
+ if (!fenceSignaled) {
+ // check fence status
+ const bool allowLatchUnsignaled =
+ shouldLatchUnsignaled(s, transaction.states.size(),
+ flushState.firstTransaction) &&
+ layer->isSimpleBufferUpdate(s);
+ if (allowLatchUnsignaled) {
+ ATRACE_FORMAT("fence unsignaled try allowLatchUnsignaled %s",
+ layer->name.c_str());
+ ready = TransactionReadiness::NotReadyUnsignaled;
+ } else {
+ ready = TransactionReadiness::NotReady;
+ auto& listener = s.bufferData->releaseBufferListener;
+ if (listener &&
+ (flushState.queueProcessTime - transaction.postTime) >
+ std::chrono::nanoseconds(4s).count()) {
+ mTransactionHandler
+ .onTransactionQueueStalled(transaction.id,
+ {.pid = layer->ownerPid.val(),
+ .layerId = layer->id,
+ .layerName = layer->name,
+ .bufferId = s.bufferData->getId(),
+ .frameNumber =
+ s.bufferData->frameNumber});
+ }
+ ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
+ return TraverseBuffersReturnValues::STOP_TRAVERSAL;
+ }
}
- ATRACE_FORMAT("fence unsignaled %s", layer->name.c_str());
- return TraverseBuffersReturnValues::STOP_TRAVERSAL;
- }
- }
- return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
- });
+ return TraverseBuffersReturnValues::CONTINUE_TRAVERSAL;
+ });
return ready;
}
@@ -5081,7 +5119,7 @@
const int originPid = ipc->getCallingPid();
const int originUid = ipc->getCallingUid();
uint32_t permissions = LayerStatePermissions::getTransactionPermissions(originPid, originUid);
- for (auto composerState : states) {
+ for (auto& composerState : states) {
composerState.state.sanitize(permissions);
}
@@ -5180,7 +5218,13 @@
}(state.flags);
const auto frameHint = state.isFrameActive() ? FrameHint::kActive : FrameHint::kNone;
- mTransactionHandler.queueTransaction(std::move(state));
+ {
+ // Transactions are added via a lockless queue and does not need to be added from the main
+ // thread.
+ ftl::FakeGuard guard(kMainThreadContext);
+ mTransactionHandler.queueTransaction(std::move(state));
+ }
+
for (const auto& [displayId, data] : mNotifyExpectedPresentMap) {
if (data.hintStatus.load() == NotifyExpectedPresentHintStatus::ScheduleOnTx) {
scheduleNotifyExpectedPresentHint(displayId, VsyncId{frameTimelineInfo.vsyncId});
@@ -5215,27 +5259,22 @@
nsecs_t now = systemTime();
uint32_t clientStateFlags = 0;
for (auto& resolvedState : states) {
- if (mLegacyFrontEndEnabled) {
- clientStateFlags |=
- setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
- isAutoTimestamp, postTime, transactionId);
-
- } else /*mLayerLifecycleManagerEnabled*/ {
- clientStateFlags |= updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState,
- desiredPresentTime, isAutoTimestamp,
- postTime, transactionId);
- }
- if ((flags & eAnimation) && resolvedState.state.surface) {
- if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
- const auto layerProps = scheduler::LayerProps{
- .visible = layer->isVisible(),
- .bounds = layer->getBounds(),
- .transform = layer->getTransform(),
- .setFrameRateVote = layer->getFrameRateForLayerTree(),
- .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
- .isFrontBuffered = layer->isFrontBuffered(),
- };
- layer->recordLayerHistoryAnimationTx(layerProps, now);
+ clientStateFlags |=
+ updateLayerCallbacksAndStats(frameTimelineInfo, resolvedState, desiredPresentTime,
+ isAutoTimestamp, postTime, transactionId);
+ if (!mLayerLifecycleManagerEnabled) {
+ if ((flags & eAnimation) && resolvedState.state.surface) {
+ if (const auto layer = LayerHandle::getLayer(resolvedState.state.surface)) {
+ const auto layerProps = scheduler::LayerProps{
+ .visible = layer->isVisible(),
+ .bounds = layer->getBounds(),
+ .transform = layer->getTransform(),
+ .setFrameRateVote = layer->getFrameRateForLayerTree(),
+ .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(),
+ .isFrontBuffered = layer->isFrontBuffered(),
+ };
+ layer->recordLayerHistoryAnimationTx(layerProps, now);
+ }
}
}
}
@@ -5271,9 +5310,8 @@
return needsTraversal;
}
-bool SurfaceFlinger::applyAndCommitDisplayTransactionStates(
+bool SurfaceFlinger::applyAndCommitDisplayTransactionStatesLocked(
std::vector<TransactionState>& transactions) {
- Mutex::Autolock lock(mStateLock);
bool needsTraversal = false;
uint32_t transactionFlags = 0;
for (auto& transaction : transactions) {
@@ -5295,7 +5333,7 @@
}
mFrontEndDisplayInfosChanged = mTransactionFlags & eDisplayTransactionNeeded;
- if (mFrontEndDisplayInfosChanged && !mLegacyFrontEndEnabled) {
+ if (mFrontEndDisplayInfosChanged) {
processDisplayChangesLocked();
mFrontEndDisplayInfos.clear();
for (const auto& [_, display] : mDisplays) {
@@ -5504,11 +5542,6 @@
if (what & layer_state_t::eBlurRegionsChanged) {
if (layer->setBlurRegions(s.blurRegions)) flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eRenderBorderChanged) {
- if (layer->enableBorder(s.borderEnabled, s.borderWidth, s.borderColor)) {
- flags |= eTraversalNeeded;
- }
- }
if (what & layer_state_t::eLayerStackChanged) {
ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
// We only allow setting layer stacks for top level layers,
@@ -5897,7 +5930,7 @@
return result;
}
- mirrorLayer->setClonedChild(mirrorFrom->createClone(mirrorLayer->getSequence()));
+ mirrorLayer->setClonedChild(mirrorFrom->createClone());
}
outResult.layerId = mirrorLayer->sequence;
@@ -5943,11 +5976,6 @@
return result;
}
- if (mLegacyFrontEndEnabled) {
- std::scoped_lock<std::mutex> lock(mMirrorDisplayLock);
- mMirrorDisplays.emplace_back(layerStack, outResult.handle, args.client);
- }
-
setTransactionFlags(eTransactionFlushNeeded);
return NO_ERROR;
}
@@ -6028,7 +6056,11 @@
mDestroyedHandles.emplace_back(layerId, layer->getDebugName());
}
- mTransactionHandler.onLayerDestroyed(layerId);
+ {
+ // Used to remove stalled transactions which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
+ mTransactionHandler.onLayerDestroyed(layerId);
+ }
Mutex::Autolock lock(mStateLock);
markLayerPendingRemovalLocked(layer);
@@ -6058,10 +6090,9 @@
std::vector<TransactionState> transactions;
transactions.emplace_back(state);
- if (mLegacyFrontEndEnabled) {
- applyTransactions(transactions, VsyncId{0});
- } else {
- applyAndCommitDisplayTransactionStates(transactions);
+ {
+ Mutex::Autolock lock(mStateLock);
+ applyAndCommitDisplayTransactionStatesLocked(transactions);
}
{
@@ -6075,8 +6106,11 @@
// Power on all displays. The primary display is first, so becomes the active display. Also,
// the DisplayCapability set of a display is populated on its first powering on. Do this now
// before responding to any Binder query from DisplayManager about display capabilities.
- for (const auto& [id, display] : mPhysicalDisplays) {
- setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+ // Additionally, do not turn on displays if the boot should be quiescent.
+ if (!mSkipPowerOnForQuiescent) {
+ for (const auto& [id, display] : mPhysicalDisplays) {
+ setPowerModeInternal(getDisplayDeviceLocked(id), hal::PowerMode::ON);
+ }
}
}
}
@@ -6235,6 +6269,7 @@
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
auto future = mScheduler->schedule([=, this]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(
kMainThreadContext) {
+ mSkipPowerOnForQuiescent = false;
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -6611,17 +6646,6 @@
}
}
- if (mLegacyFrontEndEnabled) {
- perfetto::protos::LayersProto layersProto;
- for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- if (stackIdsToSkip.find(layer->getLayerStack().id) != stackIdsToSkip.end()) {
- continue;
- }
- layer->writeToProto(layersProto, traceFlags);
- }
- return layersProto;
- }
-
return LayerProtoFromSnapshotGenerator(mLayerSnapshotBuilder, mFrontEndDisplayInfos,
mLegacyLayers, traceFlags)
.generate(mLayerHierarchyBuilder.getHierarchy());
@@ -6679,7 +6703,11 @@
}
perfetto::protos::LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return mScheduler->schedule([=, this] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler
+ ->schedule([=, this]() FTL_FAKE_GUARD(kMainThreadContext) {
+ return dumpDrawingStateProto(traceFlags);
+ })
+ .get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
@@ -6728,17 +6756,18 @@
Layer::miniDumpHeader(result);
const DisplayDevice& ref = *display;
- mLayerSnapshotBuilder.forEachVisibleSnapshot([&](const frontend::LayerSnapshot& snapshot) {
- if (!snapshot.hasSomethingToDraw() ||
- ref.getLayerStack() != snapshot.outputFilter.layerStack) {
- return;
- }
- auto it = mLegacyLayers.find(snapshot.sequence);
- LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
- "Couldnt find layer object for %s",
- snapshot.getDebugString().c_str());
- it->second->miniDump(result, snapshot, ref);
- });
+ mLayerSnapshotBuilder.forEachVisibleSnapshot(
+ [&](const frontend::LayerSnapshot& snapshot) FTL_FAKE_GUARD(kMainThreadContext) {
+ if (!snapshot.hasSomethingToDraw() ||
+ ref.getLayerStack() != snapshot.outputFilter.layerStack) {
+ return;
+ }
+ auto it = mLegacyLayers.find(snapshot.sequence);
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(it == mLegacyLayers.end(),
+ "Couldnt find layer object for %s",
+ snapshot.getDebugString().c_str());
+ it->second->miniDump(result, snapshot, ref);
+ });
result.append("\n");
}
}
@@ -6835,24 +6864,23 @@
StringAppendF(&result, " transaction-flags : %08x\n", mTransactionFlags.load());
if (const auto display = getDefaultDisplayDeviceLocked()) {
- std::string fps, xDpi, yDpi;
- if (const auto activeModePtr =
- display->refreshRateSelector().getActiveMode().modePtr.get()) {
- fps = to_string(activeModePtr->getVsyncRate());
-
+ std::string peakFps, xDpi, yDpi;
+ const auto activeMode = display->refreshRateSelector().getActiveMode();
+ if (const auto activeModePtr = activeMode.modePtr.get()) {
+ peakFps = to_string(activeMode.modePtr->getPeakFps());
const auto dpi = activeModePtr->getDpi();
xDpi = base::StringPrintf("%.2f", dpi.x);
yDpi = base::StringPrintf("%.2f", dpi.y);
} else {
- fps = "unknown";
+ peakFps = "unknown";
xDpi = "unknown";
yDpi = "unknown";
}
StringAppendF(&result,
- " refresh-rate : %s\n"
+ " peak-refresh-rate : %s\n"
" x-dpi : %s\n"
" y-dpi : %s\n",
- fps.c_str(), xDpi.c_str(), yDpi.c_str());
+ peakFps.c_str(), xDpi.c_str(), yDpi.c_str());
}
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -6866,10 +6894,6 @@
}
result.push_back('\n');
- if (mLegacyFrontEndEnabled) {
- dumpHwcLayersMinidumpLockedLegacy(result);
- }
-
{
DumpArgs plannerArgs;
plannerArgs.add(); // first argument is ignored
@@ -7623,13 +7647,12 @@
}
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
-SurfaceFlinger::getKernelIdleTimerProperties(DisplayId displayId) {
+SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) {
const bool isKernelIdleTimerHwcSupported = getHwComposer().getComposer()->isSupported(
android::Hwc2::Composer::OptionalFeature::KernelIdleTimer);
const auto timeout = getIdleTimerTimeout(displayId);
if (isKernelIdleTimerHwcSupported) {
- if (const auto id = PhysicalDisplayId::tryCast(displayId);
- getHwComposer().hasDisplayIdleTimerCapability(*id)) {
+ if (getHwComposer().hasDisplayIdleTimerCapability(displayId)) {
// In order to decide if we can use the HWC api for idle timer
// we query DisplayCapability::DISPLAY_IDLE_TIMER directly on the composer
// without relying on hasDisplayCapability.
@@ -7774,20 +7797,6 @@
return NO_ERROR;
}
- // Currently, there is no wrapper in bionic: b/183240349.
- struct sched_attr {
- uint32_t size;
- uint32_t sched_policy;
- uint64_t sched_flags;
- int32_t sched_nice;
- uint32_t sched_priority;
- uint64_t sched_runtime;
- uint64_t sched_deadline;
- uint64_t sched_period;
- uint32_t sched_util_min;
- uint32_t sched_util_max;
- };
-
sched_attr attr = {};
attr.size = sizeof(attr);
@@ -7842,17 +7851,19 @@
status_t validate = validateScreenshotPermissions(args);
if (validate != OK) {
+ ALOGD("Permission denied to captureDisplay");
invokeScreenCaptureError(validate, captureListener);
return;
}
if (!args.displayToken) {
+ ALOGD("Invalid display token to captureDisplay");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
- ALOGE("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
+ ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
}
@@ -7865,6 +7876,7 @@
Mutex::Autolock lock(mStateLock);
sp<DisplayDevice> display = getDisplayDeviceLocked(args.displayToken);
if (!display) {
+ ALOGD("Unable to find display device for captureDisplay");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
@@ -7881,18 +7893,13 @@
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
} else {
- ALOGW("Invalid layer handle passed as excludeLayer to captureDisplay");
+ ALOGD("Invalid layer handle passed as excludeLayer to captureDisplay");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
}
}
- RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, args.sourceCrop, reqSize, args.dataspace,
- args.hintForSeamlessTransition, args.captureSecureLayers);
- });
-
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
getLayerSnapshots =
@@ -7905,8 +7912,12 @@
getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
}
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
- args.allowProtected, args.grayscale, captureListener);
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+ args.sourceCrop, reqSize, args.dataspace,
+ args.hintForSeamlessTransition,
+ args.captureSecureLayers, displayWeak),
+ getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7919,6 +7930,7 @@
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
+ ALOGD("Unable to find display device for captureDisplay");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
@@ -7936,17 +7948,11 @@
constexpr auto kMaxTextureSize = 16384;
if (size.width <= 0 || size.height <= 0 || size.width >= kMaxTextureSize ||
size.height >= kMaxTextureSize) {
- ALOGE("capture display resolved to invalid size %d x %d", size.width, size.height);
+ ALOGD("captureDisplay resolved to invalid size %d x %d", size.width, size.height);
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
- RenderAreaFuture renderAreaFuture = ftl::defer([=] {
- return DisplayRenderArea::create(displayWeak, Rect(), size, args.dataspace,
- args.hintForSeamlessTransition,
- false /* captureSecureLayers */);
- });
-
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
getLayerSnapshots = getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
@@ -7967,8 +7973,12 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat,
- kAllowProtected, kGrayscale, captureListener);
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+ Rect(), size, args.dataspace,
+ args.hintForSeamlessTransition,
+ false /* captureSecureLayers */, displayWeak),
+ getLayerSnapshots, size, args.pixelFormat, kAllowProtected, kGrayscale,
+ captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7983,6 +7993,7 @@
status_t validate = validateScreenshotPermissions(args);
if (validate != OK) {
+ ALOGD("Permission denied to captureLayers");
invokeScreenCaptureError(validate, captureListener);
return;
}
@@ -7994,7 +8005,7 @@
ui::Dataspace dataspace = args.dataspace;
if (args.captureSecureLayers && !hasCaptureBlackoutContentPermission()) {
- ALOGE("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
+ ALOGD("Attempting to capture secure layers without CAPTURE_BLACKOUT_CONTENT");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
return;
}
@@ -8004,7 +8015,7 @@
parent = LayerHandle::getLayer(args.layerHandle);
if (parent == nullptr) {
- ALOGE("captureLayers called with an invalid or removed parent");
+ ALOGD("captureLayers called with an invalid or removed parent");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
@@ -8023,6 +8034,7 @@
if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
// Error out if the layer has no source bounds (i.e. they are boundless) and a source
// crop was not specified, or an invalid frame scale was provided.
+ ALOGD("Boundless layer, unspecified crop, or invalid frame scale to captureLayers");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
@@ -8033,7 +8045,7 @@
if (excludeLayer != UNASSIGNED_LAYER_ID) {
excludeLayerIds.emplace(excludeLayer);
} else {
- ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
+ ALOGD("Invalid layer handle passed as excludeLayer to captureLayers");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
@@ -8042,34 +8054,11 @@
// really small crop or frameScale
if (reqSize.width <= 0 || reqSize.height <= 0) {
- ALOGW("Failed to captureLayers: crop or scale too small");
+ ALOGD("Failed to captureLayers: crop or scale too small");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
- bool childrenOnly = args.childrenOnly;
- RenderAreaFuture renderAreaFuture = ftl::defer([=, this]() -> std::unique_ptr<RenderArea> {
- ui::Transform layerTransform;
- Rect layerBufferSize;
- if (mLayerLifecycleManagerEnabled) {
- frontend::LayerSnapshot* snapshot =
- mLayerSnapshotBuilder.getSnapshot(parent->getSequence());
- if (!snapshot) {
- ALOGW("Couldn't find layer snapshot for %d", parent->getSequence());
- } else {
- layerTransform = snapshot->localTransform;
- layerBufferSize = snapshot->bufferSize;
- }
- } else {
- layerTransform = parent->getTransform();
- layerBufferSize = parent->getBufferSize(parent->getDrawingState());
- }
-
- return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
- childrenOnly, args.captureSecureLayers,
- layerTransform, layerBufferSize,
- args.hintForSeamlessTransition);
- });
GetLayerSnapshotsFunction getLayerSnapshots;
if (mLayerLifecycleManagerEnabled) {
std::optional<FloatRect> parentCrop = std::nullopt;
@@ -8107,13 +8096,17 @@
}
if (captureListener == nullptr) {
- ALOGE("capture screen must provide a capture listener callback");
+ ALOGD("capture screen must provide a capture listener callback");
invokeScreenCaptureError(BAD_VALUE, captureListener);
return;
}
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
- args.allowProtected, args.grayscale, captureListener);
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
+ reqSize, dataspace, args.captureSecureLayers,
+ args.hintForSeamlessTransition, parent,
+ args.childrenOnly),
+ getLayerSnapshots, reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
}
// Creates a Future release fence for a layer and keeps track of it in a list to
@@ -8140,7 +8133,7 @@
return protectedLayerFound;
}
-void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
+void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
GetLayerSnapshotsFunction getLayerSnapshots,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
bool allowProtected, bool grayscale,
@@ -8189,21 +8182,35 @@
renderengine::impl::ExternalTexture::Usage::
WRITEABLE);
auto futureFence =
- captureScreenshot(std::move(renderAreaFuture), getLayerSnapshots, texture,
+ captureScreenshot(renderAreaBuilder, getLayerSnapshots, texture,
false /* regionSampling */, grayscale, isProtected, captureListener);
futureFence.get();
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
- RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
+ RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- auto takeScreenshotFn = [=, this, renderAreaFuture = std::move(renderAreaFuture)]() REQUIRES(
+ auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
// LayerSnapshots must be obtained from the main thread.
auto layers = getLayerSnapshots();
+
+ if (auto* layerRenderAreaBuilder =
+ std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) {
+ // LayerSnapshotBuilder should only be accessed from the main thread.
+ frontend::LayerSnapshot* snapshot =
+ mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence());
+ if (!snapshot) {
+ ALOGW("Couldn't find layer snapshot for %d",
+ layerRenderAreaBuilder->layer->getSequence());
+ } else {
+ layerRenderAreaBuilder->setLayerInfo(snapshot);
+ }
+ }
+
if (FlagManager::getInstance().ce_fence_promise()) {
for (auto& [layer, layerFE] : layers) {
attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
@@ -8211,7 +8218,10 @@
}
ScreenCaptureResults captureResults;
- std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
+ std::unique_ptr<const RenderArea> renderArea =
+ std::visit([](auto&& arg) -> std::unique_ptr<RenderArea> { return arg.build(); },
+ renderAreaBuilder);
+
if (!renderArea) {
ALOGW("Skipping screen capture because of invalid render area.");
if (captureListener) {
@@ -8221,11 +8231,9 @@
return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
}
- ftl::SharedFuture<FenceResult> renderFuture;
- renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- renderFuture = renderScreenImpl(renderArea, buffer, regionSampling, grayscale,
- isProtected, captureResults, layers);
- });
+ ftl::SharedFuture<FenceResult> renderFuture =
+ renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
+ isProtected, captureResults, layers);
if (captureListener) {
// Defer blocking on renderFuture back to the Binder thread.
@@ -8242,9 +8250,7 @@
};
// TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number
- // of calls on the main thread. renderAreaFuture runs on the main thread and should
- // no longer be a future, so that it does not need to make an additional jump on the
- // main thread whenever get() is called.
+ // of calls on the main thread.
auto future =
mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
@@ -8257,16 +8263,16 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
+ std::unique_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) {
auto layers = getLayerSnapshots();
- return renderScreenImpl(renderArea, buffer, regionSampling, grayscale, isProtected,
+ return renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
captureResults, layers);
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::shared_ptr<const RenderArea> renderArea,
+ std::unique_ptr<const RenderArea> renderArea,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) {
@@ -8296,9 +8302,15 @@
Mutex::Autolock lock(mStateLock);
const DisplayDevice* display = nullptr;
if (parent) {
- display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
- return display.getLayerStack() == layerStack;
- }).get();
+ const frontend::LayerSnapshot* snapshot = mLayerLifecycleManagerEnabled
+ ? mLayerSnapshotBuilder.getSnapshot(parent->sequence)
+ : parent->getLayerSnapshot();
+ if (snapshot) {
+ display = findDisplay([layerStack = snapshot->outputFilter.layerStack](
+ const auto& display) {
+ return display.getLayerStack() == layerStack;
+ }).get();
+ }
}
if (display == nullptr) {
@@ -8664,8 +8676,13 @@
return INVALID_OPERATION;
} else {
using Policy = scheduler::RefreshRateSelector::DisplayManagerPolicy;
+ const auto idleScreenConfigOpt =
+ FlagManager::getInstance().idle_screen_refresh_rate_timeout()
+ ? specs.idleScreenRefreshRateConfig
+ : std::nullopt;
const Policy policy{DisplayModeId(specs.defaultMode), translate(specs.primaryRanges),
- translate(specs.appRequestRanges), specs.allowGroupSwitching};
+ translate(specs.appRequestRanges), specs.allowGroupSwitching,
+ idleScreenConfigOpt};
return setDesiredDisplayModeSpecsInternal(display, policy);
}
@@ -9009,6 +9026,8 @@
status_t SurfaceFlinger::getStalledTransactionInfo(
int pid, std::optional<TransactionHandler::StalledTransactionInfo>& result) {
+ // Used to add a stalled transaction which uses an internal lock.
+ ftl::FakeGuard guard(kMainThreadContext);
result = mTransactionHandler.getStalledTransactionInfo(pid);
return NO_ERROR;
}
@@ -9136,7 +9155,7 @@
Mutex::Autolock lock(mStateLock);
createEffectLayer(mirrorArgs, &unused, &childMirror);
MUTEX_ALIAS(mStateLock, childMirror->mFlinger->mStateLock);
- childMirror->setClonedChild(layer->createClone(childMirror->getSequence()));
+ childMirror->setClonedChild(layer->createClone());
childMirror->reparent(mirrorDisplay.rootHandle);
}
// lock on mStateLock needs to be released before binder handle gets destroyed
@@ -9196,7 +9215,7 @@
snapshots[i] = std::move(layerFE->mSnapshot);
}
}
- if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+ if (!mLayerLifecycleManagerEnabled) {
for (auto [layer, layerFE] : layers) {
layer->updateLayerSnapshot(std::move(layerFE->mSnapshot));
}
@@ -9209,7 +9228,8 @@
if (mLayerLifecycleManagerEnabled) {
nsecs_t currentTime = systemTime();
mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
+ kMainThreadContext) {
if (cursorOnly &&
snapshot->compositionType !=
aidl::android::hardware::graphics::composer3::Composition::CURSOR) {
@@ -9232,7 +9252,7 @@
layers.emplace_back(legacyLayer.get(), layerFE.get());
});
}
- if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+ if (!mLayerLifecycleManagerEnabled) {
auto moveSnapshots = [&layers, &refreshArgs, cursorOnly](Layer* layer) {
if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
if (cursorOnly &&
@@ -9270,11 +9290,12 @@
std::optional<ui::LayerStack> layerStack, uint32_t uid,
std::function<bool(const frontend::LayerSnapshot&, bool& outStopTraversal)>
snapshotFilterFn) {
- return [&, layerStack, uid]() {
+ return [&, layerStack, uid]() FTL_FAKE_GUARD(kMainThreadContext) {
std::vector<std::pair<Layer*, sp<LayerFE>>> layers;
bool stopTraversal = false;
mLayerSnapshotBuilder.forEachVisibleSnapshot(
- [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) {
+ [&](std::unique_ptr<frontend::LayerSnapshot>& snapshot) FTL_FAKE_GUARD(
+ kMainThreadContext) {
if (stopTraversal) {
return;
}
@@ -9296,7 +9317,7 @@
"Couldnt find layer object for %s",
snapshot->getDebugString().c_str());
Layer* legacyLayer = (it == mLegacyLayers.end()) ? nullptr : it->second.get();
- sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name);
+ sp<LayerFE> layerFE = getFactory().createLayerFE(snapshot->name, legacyLayer);
layerFE->mSnapshot = std::make_unique<frontend::LayerSnapshot>(*snapshot);
layers.emplace_back(legacyLayer, std::move(layerFE));
});
@@ -9309,7 +9330,8 @@
SurfaceFlinger::getLayerSnapshotsForScreenshots(std::optional<ui::LayerStack> layerStack,
uint32_t uid,
std::unordered_set<uint32_t> excludeLayerIds) {
- return [&, layerStack, uid, excludeLayerIds = std::move(excludeLayerIds)]() {
+ return [&, layerStack, uid,
+ excludeLayerIds = std::move(excludeLayerIds)]() FTL_FAKE_GUARD(kMainThreadContext) {
if (excludeLayerIds.empty()) {
auto getLayerSnapshotsFn =
getLayerSnapshotsForScreenshots(layerStack, uid, /*snapshotFilterFn=*/nullptr);
@@ -9351,7 +9373,7 @@
bool childrenOnly,
const std::optional<FloatRect>& parentCrop) {
return [&, rootLayerId, uid, excludeLayerIds = std::move(excludeLayerIds), childrenOnly,
- parentCrop]() {
+ parentCrop]() FTL_FAKE_GUARD(kMainThreadContext) {
auto root = mLayerHierarchyBuilder.getPartialHierarchy(rootLayerId, childrenOnly);
frontend::LayerSnapshotBuilder::Args
args{.root = root,
@@ -9863,6 +9885,7 @@
std::optional<DisplayId> id = DisplayId::fromValue(static_cast<uint64_t>(displayId));
mFlinger->captureDisplay(*id, args, captureListener);
} else {
+ ALOGD("Permission denied to captureDisplayById");
invokeScreenCaptureError(PERMISSION_DENIED, captureListener);
}
return binderStatusFromStatusT(NO_ERROR);
@@ -9908,22 +9931,6 @@
return binderStatusFromStatusT(status);
}
-binder::Status SurfaceComposerAIDL::getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) {
- if (!outLayers) {
- return binderStatusFromStatusT(UNEXPECTED_NULL);
- }
-
- IPCThreadState* ipc = IPCThreadState::self();
- const int pid = ipc->getCallingPid();
- const int uid = ipc->getCallingUid();
- if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) {
- ALOGE("Layer debug info permission denied for pid=%d, uid=%d", pid, uid);
- return binderStatusFromStatusT(PERMISSION_DENIED);
- }
- status_t status = mFlinger->getLayerDebugInfo(outLayers);
- return binderStatusFromStatusT(status);
-}
-
binder::Status SurfaceComposerAIDL::getCompositionPreference(gui::CompositionPreference* outPref) {
ui::Dataspace dataspace;
ui::PixelFormat pixelFormat;
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 4c53fae..8474515 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -38,7 +38,6 @@
#include <gui/FrameTimestamps.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ITransactionCompletedListener.h>
-#include <gui/LayerDebugInfo.h>
#include <gui/LayerState.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
@@ -66,6 +65,7 @@
#include <ui/FenceResult.h>
#include <common/FlagManager.h>
+#include "Display/DisplayModeController.h"
#include "Display/PhysicalDisplay.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
@@ -192,6 +192,9 @@
Always,
};
+struct DisplayRenderAreaBuilder;
+struct LayerRenderAreaBuilder;
+
using DisplayColorSetting = compositionengine::OutputColorSetting;
class SurfaceFlinger : public BnSurfaceComposer,
@@ -383,7 +386,7 @@
using TransactionSchedule = scheduler::TransactionSchedule;
using GetLayerSnapshotsFunction = std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()>;
- using RenderAreaFuture = ftl::Future<std::unique_ptr<RenderArea>>;
+ using RenderAreaBuilderVariant = std::variant<DisplayRenderAreaBuilder, LayerRenderAreaBuilder>;
using DumpArgs = Vector<String16>;
using Dumper = std::function<void(const DumpArgs&, bool asProto, std::string&)>;
@@ -600,7 +603,6 @@
status_t overrideHdrTypes(const sp<IBinder>& displayToken,
const std::vector<ui::Hdr>& hdrTypes);
status_t onPullAtom(const int32_t atomId, std::vector<uint8_t>* pulledData, bool* success);
- status_t getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers);
status_t getCompositionPreference(ui::Dataspace* outDataspace, ui::PixelFormat* outPixelFormat,
ui::Dataspace* outWideColorGamutDataspace,
ui::PixelFormat* outWideColorGamutPixelFormat) const;
@@ -710,7 +712,7 @@
// Get the controller and timeout that will help decide how the kernel idle timer will be
// configured and what value to use as the timeout.
std::pair<std::optional<KernelIdleTimerController>, std::chrono::milliseconds>
- getKernelIdleTimerProperties(DisplayId) REQUIRES(mStateLock);
+ getKernelIdleTimerProperties(PhysicalDisplayId) REQUIRES(mStateLock);
// Updates the kernel idle timer either through HWC or through sysprop
// depending on which controller is provided
void updateKernelIdleTimer(std::chrono::milliseconds timeoutMs, KernelIdleTimerController,
@@ -761,7 +763,8 @@
bool force = false)
REQUIRES(mStateLock, kMainThreadContext);
- void commitTransactions() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+ void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
+ void commitTransactions() REQUIRES(kMainThreadContext, mStateLock);
void commitTransactionsLocked(uint32_t transactionFlags)
REQUIRES(mStateLock, kMainThreadContext);
void doCommitTransactions() REQUIRES(mStateLock);
@@ -772,24 +775,27 @@
void updateLayerGeometry();
void updateLayerMetadataSnapshot();
std::vector<std::pair<Layer*, LayerFE*>> moveSnapshotsToCompositionArgs(
- compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly);
+ compositionengine::CompositionRefreshArgs& refreshArgs, bool cursorOnly)
+ REQUIRES(kMainThreadContext);
void moveSnapshotsFromCompositionArgs(compositionengine::CompositionRefreshArgs& refreshArgs,
- const std::vector<std::pair<Layer*, LayerFE*>>& layers);
+ const std::vector<std::pair<Layer*, LayerFE*>>& layers)
+ REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
bool updateLayerSnapshotsLegacy(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
// Return true if we must composite this frame
bool updateLayerSnapshots(VsyncId vsyncId, nsecs_t frameTimeNs, bool transactionsFlushed,
bool& out) REQUIRES(kMainThreadContext);
- void updateLayerHistory(nsecs_t now);
+ void updateLayerHistory(nsecs_t now) REQUIRES(kMainThreadContext);
frontend::Update flushLifecycleUpdates() REQUIRES(kMainThreadContext);
- void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime);
+ void updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) REQUIRES(kMainThreadContext);
void persistDisplayBrightness(bool needsComposite) REQUIRES(kMainThreadContext);
void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
- std::vector<gui::DisplayInfo>& outDisplayInfos);
+ std::vector<gui::DisplayInfo>& outDisplayInfos)
+ REQUIRES(kMainThreadContext);
void commitInputWindowCommands() REQUIRES(mStateLock);
- void updateCursorAsync();
+ void updateCursorAsync() REQUIRES(kMainThreadContext);
void initScheduler(const sp<const DisplayDevice>&) REQUIRES(kMainThreadContext, mStateLock);
@@ -805,18 +811,18 @@
const int64_t postTime, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks,
int originPid, int originUid, uint64_t transactionId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
// Flush pending transactions that were presented after desiredPresentTime.
// For test only
bool flushTransactionQueues(VsyncId) REQUIRES(kMainThreadContext);
bool applyTransactions(std::vector<TransactionState>&, VsyncId) REQUIRES(kMainThreadContext);
- bool applyAndCommitDisplayTransactionStates(std::vector<TransactionState>& transactions)
- REQUIRES(kMainThreadContext);
+ bool applyAndCommitDisplayTransactionStatesLocked(std::vector<TransactionState>& transactions)
+ REQUIRES(kMainThreadContext, mStateLock);
// Returns true if there is at least one transaction that needs to be flushed
- bool transactionFlushNeeded();
- void addTransactionReadyFilters();
+ bool transactionFlushNeeded() REQUIRES(kMainThreadContext);
+ void addTransactionReadyFilters() REQUIRES(kMainThreadContext);
TransactionHandler::TransactionReadiness transactionReadyTimelineCheck(
const TransactionHandler::TransactionFlushState& flushState)
REQUIRES(kMainThreadContext);
@@ -833,7 +839,7 @@
uint32_t updateLayerCallbacksAndStats(const FrameTimelineInfo&, ResolvedComposerState&,
int64_t desiredPresentTime, bool isAutoTimestamp,
int64_t postTime, uint64_t transactionId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
uint32_t getTransactionFlags() const;
// Sets the masked bits, and schedules a commit if needed.
@@ -849,7 +855,7 @@
static LatchUnsignaledConfig getLatchUnsignaledConfig();
bool shouldLatchUnsignaled(const layer_state_t&, size_t numStates, bool firstTransaction) const;
bool applyTransactionsLocked(std::vector<TransactionState>& transactions, VsyncId)
- REQUIRES(mStateLock);
+ REQUIRES(mStateLock, kMainThreadContext);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
@@ -889,12 +895,12 @@
// Checks if a protected layer exists in a list of layers.
bool layersHasProtectedLayer(const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
- void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize,
- ui::PixelFormat, bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>&);
+ void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
+ ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
+ bool grayscale, const sp<IScreenCaptureListener>&);
ftl::SharedFuture<FenceResult> captureScreenshot(
- RenderAreaFuture, GetLayerSnapshotsFunction,
+ RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
@@ -902,13 +908,13 @@
// not yet been captured, and thus cannot yet be passed in as a parameter.
// Needed for TestableSurfaceFlinger.
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
+ std::unique_ptr<const RenderArea>, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock)
REQUIRES(kMainThreadContext);
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::shared_ptr<const RenderArea>,
+ std::unique_ptr<const RenderArea>,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, bool isProtected, ScreenCaptureResults&,
std::vector<std::pair<Layer*, sp<android::LayerFE>>>& layers) EXCLUDES(mStateLock)
@@ -988,8 +994,7 @@
return getDefaultDisplayDeviceLocked();
}
- using DisplayDeviceAndSnapshot =
- std::pair<sp<DisplayDevice>, display::PhysicalDisplay::SnapshotRef>;
+ using DisplayDeviceAndSnapshot = std::pair<sp<DisplayDevice>, display::DisplaySnapshotRef>;
// Combinator for ftl::Optional<PhysicalDisplay>::and_then.
auto getDisplayDeviceAndSnapshot() REQUIRES(mStateLock) {
@@ -1058,10 +1063,13 @@
bool configureLocked() REQUIRES(mStateLock) REQUIRES(kMainThreadContext)
EXCLUDES(mHotplugMutex);
- // Returns a string describing the hotplug, or nullptr if it was rejected.
- const char* processHotplug(PhysicalDisplayId, hal::HWDisplayId, bool connected,
- DisplayIdentificationInfo&&) REQUIRES(mStateLock)
- REQUIRES(kMainThreadContext);
+ // Returns the active mode ID, or nullopt on hotplug failure.
+ std::optional<DisplayModeId> processHotplugConnect(PhysicalDisplayId, hal::HWDisplayId,
+ DisplayIdentificationInfo&&,
+ const char* displayString)
+ REQUIRES(mStateLock, kMainThreadContext);
+ void processHotplugDisconnect(PhysicalDisplayId, const char* displayString)
+ REQUIRES(mStateLock, kMainThreadContext);
sp<DisplayDevice> setupNewDisplayDeviceInternal(
const wp<IBinder>& displayToken,
@@ -1138,9 +1146,10 @@
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
- void listLayers(std::string& result) const;
- void dumpStats(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
- void clearStats(const DumpArgs& args, std::string& result);
+ void listLayers(std::string& result) const REQUIRES(kMainThreadContext);
+ void dumpStats(const DumpArgs& args, std::string& result) const
+ REQUIRES(mStateLock, kMainThreadContext);
+ void clearStats(const DumpArgs& args, std::string& result) REQUIRES(kMainThreadContext);
void dumpTimeStats(const DumpArgs& args, bool asProto, std::string& result) const;
void dumpFrameTimeline(const DumpArgs& args, std::string& result) const;
void logFrameStats(TimePoint now) REQUIRES(kMainThreadContext);
@@ -1158,7 +1167,8 @@
void dumpFrontEnd(std::string& result) REQUIRES(kMainThreadContext);
void dumpVisibleFrontEnd(std::string& result) REQUIRES(mStateLock, kMainThreadContext);
- perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
+ perfetto::protos::LayersProto dumpDrawingStateProto(uint32_t traceFlags) const
+ REQUIRES(kMainThreadContext);
void dumpOffscreenLayersProto(perfetto::protos::LayersProto& layersProto,
uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
google::protobuf::RepeatedPtrField<perfetto::protos::DisplayProto> dumpDisplayProto() const;
@@ -1206,7 +1216,8 @@
ui::Rotation getPhysicalDisplayOrientation(DisplayId, bool isPrimary) const
REQUIRES(mStateLock);
- void traverseLegacyLayers(const LayerVector::Visitor& visitor) const;
+ void traverseLegacyLayers(const LayerVector::Visitor& visitor) const
+ REQUIRES(kMainThreadContext);
void initBootProperties();
void initTransactionTraceWriter();
@@ -1319,6 +1330,8 @@
// reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
+ display::DisplayModeController mDisplayModeController;
+
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
std::optional<DisplayIdGenerator<HalVirtualDisplayId>> hal;
@@ -1482,25 +1495,28 @@
bool mPowerHintSessionEnabled;
bool mLayerLifecycleManagerEnabled = false;
- bool mLegacyFrontEndEnabled = true;
+ // Whether a display should be turned on when initialized
+ bool mSkipPowerOnForQuiescent;
- frontend::LayerLifecycleManager mLayerLifecycleManager;
- frontend::LayerHierarchyBuilder mLayerHierarchyBuilder;
- frontend::LayerSnapshotBuilder mLayerSnapshotBuilder;
+ frontend::LayerLifecycleManager mLayerLifecycleManager GUARDED_BY(kMainThreadContext);
+ frontend::LayerHierarchyBuilder mLayerHierarchyBuilder GUARDED_BY(kMainThreadContext);
+ frontend::LayerSnapshotBuilder mLayerSnapshotBuilder GUARDED_BY(kMainThreadContext);
- std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles;
- std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers;
- std::vector<LayerCreationArgs> mNewLayerArgs;
+ std::vector<std::pair<uint32_t, std::string>> mDestroyedHandles GUARDED_BY(mCreatedLayersLock);
+ std::vector<std::unique_ptr<frontend::RequestedLayerState>> mNewLayers
+ GUARDED_BY(mCreatedLayersLock);
+ std::vector<LayerCreationArgs> mNewLayerArgs GUARDED_BY(mCreatedLayersLock);
// These classes do not store any client state but help with managing transaction callbacks
// and stats.
- std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers;
+ std::unordered_map<uint32_t, sp<Layer>> mLegacyLayers GUARDED_BY(kMainThreadContext);
- TransactionHandler mTransactionHandler;
- ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos;
- bool mFrontEndDisplayInfosChanged = false;
+ TransactionHandler mTransactionHandler GUARDED_BY(kMainThreadContext);
+ ui::DisplayMap<ui::LayerStack, frontend::DisplayInfo> mFrontEndDisplayInfos
+ GUARDED_BY(kMainThreadContext);
+ bool mFrontEndDisplayInfosChanged GUARDED_BY(kMainThreadContext) = false;
// WindowInfo ids visible during the last commit.
- std::unordered_set<int32_t> mVisibleWindowIds;
+ std::unordered_set<int32_t> mVisibleWindowIds GUARDED_BY(kMainThreadContext);
// Mirroring
// Map of displayid to mirrorRoot
@@ -1603,7 +1619,6 @@
binder::Status overrideHdrTypes(const sp<IBinder>& display,
const std::vector<int32_t>& hdrTypes) override;
binder::Status onPullAtom(int32_t atomId, gui::PullAtomData* outPullData) override;
- binder::Status getLayerDebugInfo(std::vector<gui::LayerDebugInfo>* outLayers) override;
binder::Status getCompositionPreference(gui::CompositionPreference* outPref) override;
binder::Status getDisplayedContentSamplingAttributes(
const sp<IBinder>& display, gui::ContentSamplingAttributes* outAttrs) override;
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 50b167d..b1d8ba9 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -85,7 +85,7 @@
return sp<Layer>::make(args);
}
-sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName) {
+sp<LayerFE> DefaultFactory::createLayerFE(const std::string& layerName, const Layer* /* owner */) {
return sp<LayerFE>::make(layerName);
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 540dec8..7ebf10f 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -41,7 +41,7 @@
std::unique_ptr<compositionengine::CompositionEngine> createCompositionEngine() override;
sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) override;
sp<Layer> createEffectLayer(const LayerCreationArgs& args) override;
- sp<LayerFE> createLayerFE(const std::string& layerName) override;
+ sp<LayerFE> createLayerFE(const std::string& layerName, const Layer* owner) override;
std::unique_ptr<FrameTracer> createFrameTracer() override;
std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index f1fbf01..c7d1fa0 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -85,7 +85,7 @@
virtual sp<Layer> createBufferStateLayer(const LayerCreationArgs& args) = 0;
virtual sp<Layer> createEffectLayer(const LayerCreationArgs& args) = 0;
- virtual sp<LayerFE> createLayerFE(const std::string& layerName) = 0;
+ virtual sp<LayerFE> createLayerFE(const std::string& layerName, const Layer* owner) = 0;
virtual std::unique_ptr<FrameTracer> createFrameTracer() = 0;
virtual std::unique_ptr<frametimeline::FrameTimeline> createFrameTimeline(
std::shared_ptr<TimeStats> timeStats, pid_t surfaceFlingerPid) = 0;
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index 2dc89b5..b3e9fab 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -566,7 +566,7 @@
const frontend::DisplayInfo& displayInfo, uint32_t layerStack) {
perfetto::protos::DisplayInfo proto;
proto.set_layer_stack(layerStack);
- proto.set_display_id(displayInfo.info.displayId);
+ proto.set_display_id(displayInfo.info.displayId.val());
proto.set_logical_width(displayInfo.info.logicalWidth);
proto.set_logical_height(displayInfo.info.logicalHeight);
asProto(proto.mutable_transform_inverse(), displayInfo.info.transform);
@@ -588,7 +588,7 @@
frontend::DisplayInfo TransactionProtoParser::fromProto(
const perfetto::protos::DisplayInfo& proto) {
frontend::DisplayInfo displayInfo;
- displayInfo.info.displayId = proto.display_id();
+ displayInfo.info.displayId = ui::LogicalDisplayId{proto.display_id()};
displayInfo.info.logicalWidth = proto.logical_width();
displayInfo.info.logicalHeight = proto.logical_height();
fromProto2(displayInfo.info.transform, proto.transform_inverse());
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 31cd2d7..89a8f92 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -86,7 +86,7 @@
}
template <typename Visitor>
- void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) {
+ void traverseStatesWithBuffersWhileTrue(Visitor&& visitor) NO_THREAD_SAFETY_ANALYSIS {
for (auto state = states.begin(); state != states.end();) {
if (state->state.hasBufferChanges() && state->externalTexture && state->state.surface) {
int result = visitor(*state);
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index 4a89dd0..6b971a7 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -36,6 +36,7 @@
static_libs: [
"libsurfaceflingerflags",
"android.os.flags-aconfig-cc",
+ "android.server.display.flags-aconfig-cc",
],
}
@@ -47,6 +48,7 @@
static_libs: [
"libsurfaceflingerflags_test",
"android.os.flags-aconfig-cc-test",
+ "android.server.display.flags-aconfig-cc",
],
}
@@ -59,6 +61,7 @@
"libsurfaceflinger_common",
"libsurfaceflingerflags",
"android.os.flags-aconfig-cc",
+ "android.server.display.flags-aconfig-cc",
],
}
@@ -71,5 +74,6 @@
"libsurfaceflinger_common_test",
"libsurfaceflingerflags_test",
"android.os.flags-aconfig-cc-test",
+ "android.server.display.flags-aconfig-cc",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index 4b34a55..45b3290 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -28,6 +28,7 @@
#include <android_os.h>
#include <com_android_graphics_surfaceflinger_flags.h>
+#include <com_android_server_display_feature_flags.h>
namespace android {
using namespace com::android::graphics::surfaceflinger;
@@ -137,6 +138,13 @@
DUMP_READ_ONLY_FLAG(dont_skip_on_early_ro);
DUMP_READ_ONLY_FLAG(protected_if_client);
DUMP_READ_ONLY_FLAG(ce_fence_promise);
+ DUMP_READ_ONLY_FLAG(idle_screen_refresh_rate_timeout);
+ DUMP_READ_ONLY_FLAG(graphite_renderengine);
+ DUMP_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed);
+ DUMP_READ_ONLY_FLAG(deprecate_vsync_sf);
+ DUMP_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter);
+ DUMP_READ_ONLY_FLAG(detached_mirror);
+
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -190,6 +198,9 @@
#define FLAG_MANAGER_SERVER_FLAG_IMPORTED(name, syspropOverride, owner) \
FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, owner)
+#define FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(name, syspropOverride, owner) \
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, owner)
+
/// Legacy server flags ///
FLAG_MANAGER_LEGACY_SERVER_FLAG(test_flag, "", "")
FLAG_MANAGER_LEGACY_SERVER_FLAG(use_adpf_cpu_hint, "debug.sf.enable_adpf_cpu_hint",
@@ -222,6 +233,11 @@
FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
FLAG_MANAGER_READ_ONLY_FLAG(ce_fence_promise, "");
+FLAG_MANAGER_READ_ONLY_FLAG(graphite_renderengine, "debug.renderengine.graphite")
+FLAG_MANAGER_READ_ONLY_FLAG(latch_unsignaled_with_auto_refresh_changed, "");
+FLAG_MANAGER_READ_ONLY_FLAG(deprecate_vsync_sf, "");
+FLAG_MANAGER_READ_ONLY_FLAG(allow_n_vsyncs_in_targeter, "");
+FLAG_MANAGER_READ_ONLY_FLAG(detached_mirror, "");
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
@@ -230,4 +246,9 @@
/// Trunk stable server flags from outside SurfaceFlinger ///
FLAG_MANAGER_SERVER_FLAG_IMPORTED(adpf_use_fmq_channel, "", android::os)
+/// Trunk stable readonly flags from outside SurfaceFlinger ///
+FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(idle_screen_refresh_rate_timeout, "",
+ com::android::server::display::feature::flags)
+FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(adpf_use_fmq_channel_fixed, "", android::os)
+
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 320e34b..592e774 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -51,6 +51,7 @@
bool refresh_rate_overlay_on_external_display() const;
bool adpf_gpu_sf() const;
bool adpf_use_fmq_channel() const;
+ bool adpf_use_fmq_channel_fixed() const;
/// Trunk stable readonly flags ///
bool connected_display() const;
@@ -76,6 +77,12 @@
bool dont_skip_on_early_ro() const;
bool protected_if_client() const;
bool ce_fence_promise() const;
+ bool idle_screen_refresh_rate_timeout() const;
+ bool graphite_renderengine() const;
+ bool latch_unsignaled_with_auto_refresh_changed() const;
+ bool deprecate_vsync_sf() const;
+ bool allow_n_vsyncs_in_targeter() const;
+ bool detached_mirror() const;
protected:
// overridden for unit tests
diff --git a/services/surfaceflinger/common/include/common/test/FlagUtils.h b/services/surfaceflinger/common/include/common/test/FlagUtils.h
index d61fcb5..5317cbb 100644
--- a/services/surfaceflinger/common/include/common/test/FlagUtils.h
+++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h
@@ -18,9 +18,13 @@
#include <common/FlagManager.h>
-#define SET_FLAG_FOR_TEST(name, value) \
- TestFlagSetter _testflag_ { \
- (name), (name), (value) \
+// indirection to resolve __LINE__ in SET_FLAG_FOR_TEST, it's used to create a unique TestFlagSetter
+// setter var name everytime so multiple flags can be set in a test
+#define CONCAT_INNER(a, b) a##b
+#define CONCAT(a, b) CONCAT_INNER(a, b)
+#define SET_FLAG_FOR_TEST(name, value) \
+ TestFlagSetter CONCAT(_testFlag_, __LINE__) { \
+ (name), (name), (value) \
}
namespace android {
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 5174fa7..dea74d0 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -222,4 +222,15 @@
}
}
+flag {
+ name: "allow_n_vsyncs_in_targeter"
+ namespace: "core_graphics"
+ description: "This flag will enable utilizing N vsyncs in the FrameTargeter for past vsyncs"
+ bug: "308858993"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
# This file is locked and should not be changed. Use surfaceflinger_flags_new.aconfig
diff --git a/services/surfaceflinger/surfaceflinger_flags_new.aconfig b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
index 0a5cde3..4d3195d 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -19,14 +19,29 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
+ } # ce_fence_promise
-flag {
- name: "dont_skip_on_early_ro2"
+ flag {
+ name: "deprecate_vsync_sf"
namespace: "core_graphics"
- description: "This flag is guarding the behaviour where SurfaceFlinger is trying to opportunistically present a frame when the configuration change from late to early"
- bug: "273702768"
-} # dont_skip_on_early_ro2
+ description: "Depracate eVsyncSourceSurfaceFlinger and use vsync_app everywhere"
+ bug: "162235855"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # deprecate_vsync_sf
+
+ flag {
+ name: "detached_mirror"
+ namespace: "window_surfaces"
+ description: "Ignore local transform when mirroring a partial hierarchy"
+ bug: "337845753"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # deprecate_vsync_sf
flag {
name: "frame_rate_category_mrr"
@@ -39,4 +54,15 @@
}
} # frame_rate_category_mrr
+flag {
+ name: "latch_unsignaled_with_auto_refresh_changed"
+ namespace: "core_graphics"
+ description: "Ignore eAutoRefreshChanged with latch unsignaled"
+ bug: "331513837"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # latch_unsignaled_with_auto_refresh_changed
+
# IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index d60ef48..38fc977 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -42,7 +42,6 @@
"EffectLayer_test.cpp",
"HdrSdrRatioOverlay_test.cpp",
"InvalidHandles_test.cpp",
- "LayerBorder_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
"LayerState_test.cpp",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 9b83713..c1ef48e 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -21,7 +21,6 @@
#include <android/gui/ISurfaceComposer.h>
#include <gtest/gtest.h>
#include <gui/AidlStatusUtil.h>
-#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
#include <private/android_filesystem_config.h>
@@ -36,7 +35,6 @@
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
-using gui::LayerDebugInfo;
using gui::aidl_utils::statusTFromBinderStatus;
using ui::ColorMode;
@@ -292,35 +290,6 @@
/**
* The following tests are for methods accessible directly through SurfaceFlinger.
*/
-TEST_F(CredentialsTest, GetLayerDebugInfo) {
- setupBackgroundSurface();
- sp<gui::ISurfaceComposer> sf(ComposerServiceAIDL::getComposerService());
-
- // Historically, only root and shell can access the getLayerDebugInfo which
- // is called when we call dumpsys. I don't see a reason why we should change this.
- std::vector<LayerDebugInfo> outLayers;
- binder::Status status = binder::Status::ok();
- // Check with root.
- {
- UIDFaker f(AID_ROOT);
- status = sf->getLayerDebugInfo(&outLayers);
- ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
- }
-
- // Check as a shell.
- {
- UIDFaker f(AID_SHELL);
- status = sf->getLayerDebugInfo(&outLayers);
- ASSERT_EQ(NO_ERROR, statusTFromBinderStatus(status));
- }
-
- // Check as anyone else.
- {
- UIDFaker f(AID_BIN);
- status = sf->getLayerDebugInfo(&outLayers);
- ASSERT_EQ(PERMISSION_DENIED, statusTFromBinderStatus(status));
- }
-}
TEST_F(CredentialsTest, IsWideColorDisplayBasicCorrectness) {
const auto display = getFirstDisplayToken();
@@ -389,8 +358,13 @@
.apply();
}
- // Called from non privileged process
- Transaction().setTrustedOverlay(surfaceControl, true);
+ // Attempt to set a trusted overlay from a non-privileged process. This should fail silently.
+ {
+ UIDFaker f{AID_BIN};
+ Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true);
+ }
+
+ // Verify that the layer was not made a trusted overlay.
{
UIDFaker f(AID_SYSTEM);
auto windowIsPresentAndNotTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
@@ -401,12 +375,14 @@
}
return !foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
};
- windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndNotTrusted);
+ ASSERT_TRUE(
+ windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndNotTrusted));
}
+ // Verify that privileged processes are able to set trusted overlays.
{
UIDFaker f(AID_SYSTEM);
- Transaction().setTrustedOverlay(surfaceControl, true);
+ Transaction().setTrustedOverlay(surfaceControl, true).apply(/*synchronous=*/true);
auto windowIsPresentAndTrusted = [&](const std::vector<WindowInfo>& windowInfos) {
auto foundWindowInfo =
WindowInfosListenerUtils::findMatchingWindowInfo(windowInfo, windowInfos);
@@ -415,7 +391,8 @@
}
return foundWindowInfo->inputConfig.test(WindowInfo::InputConfig::TRUSTED_OVERLAY);
};
- windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndTrusted);
+ ASSERT_TRUE(
+ windowInfosListenerUtils.waitForWindowInfosPredicate(windowIsPresentAndTrusted));
}
}
diff --git a/services/surfaceflinger/tests/LayerBorder_test.cpp b/services/surfaceflinger/tests/LayerBorder_test.cpp
deleted file mode 100644
index 00e134b..0000000
--- a/services/surfaceflinger/tests/LayerBorder_test.cpp
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-// TODO: Amend all tests when screenshots become fully reworked for borders
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-
-#include <chrono> // std::chrono::seconds
-#include <thread> // std::this_thread::sleep_for
-#include "LayerTransactionTest.h"
-
-namespace android {
-
-class LayerBorderTest : public LayerTransactionTest {
-protected:
- virtual void SetUp() {
- LayerTransactionTest::SetUp();
- ASSERT_EQ(NO_ERROR, mClient->initCheck());
-
- toHalf3 = ColorTransformHelper::toHalf3;
- toHalf4 = ColorTransformHelper::toHalf4;
-
- const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
- ASSERT_FALSE(ids.empty());
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
- ASSERT_FALSE(display == nullptr);
- mColorOrange = toHalf4({255, 140, 0, 255});
- mParentLayer = createColorLayer("Parent layer", Color::RED);
-
- mContainerLayer = mClient->createSurface(String8("Container Layer"), 0 /* width */,
- 0 /* height */, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceContainer |
- ISurfaceComposerClient::eNoColorFill,
- mParentLayer->getHandle());
- EXPECT_NE(nullptr, mContainerLayer.get()) << "failed to create container layer";
-
- mEffectLayer1 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
- 0 /* height */, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect |
- ISurfaceComposerClient::eNoColorFill,
- mContainerLayer->getHandle());
- EXPECT_NE(nullptr, mEffectLayer1.get()) << "failed to create effect layer 1";
-
- mEffectLayer2 = mClient->createSurface(String8("Effect Layer"), 0 /* width */,
- 0 /* height */, PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect |
- ISurfaceComposerClient::eNoColorFill,
- mContainerLayer->getHandle());
-
- EXPECT_NE(nullptr, mEffectLayer2.get()) << "failed to create effect layer 2";
-
- asTransaction([&](Transaction& t) {
- t.setDisplayLayerStack(display, ui::DEFAULT_LAYER_STACK);
- t.setLayer(mParentLayer, INT32_MAX - 20).show(mParentLayer);
- t.setFlags(mParentLayer, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
-
- t.setColor(mEffectLayer1, toHalf3(Color::BLUE));
-
- t.setColor(mEffectLayer2, toHalf3(Color::GREEN));
- });
- }
-
- virtual void TearDown() {
- // Uncomment the line right below when running any of the tests
- // std::this_thread::sleep_for (std::chrono::seconds(30));
- LayerTransactionTest::TearDown();
- mParentLayer = 0;
- }
-
- std::function<half3(Color)> toHalf3;
- std::function<half4(Color)> toHalf4;
- sp<SurfaceControl> mParentLayer, mContainerLayer, mEffectLayer1, mEffectLayer2;
- half4 mColorOrange;
-};
-
-TEST_F(LayerBorderTest, OverlappingVisibleRegions) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, PartiallyCoveredVisibleRegion) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
-
- t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, NonOverlappingVisibleRegion) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
- t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, EmptyVisibleRegion) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(200, 200, 400, 400));
- t.setCrop(mEffectLayer2, Rect(0, 0, 600, 600));
-
- t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, ZOrderAdjustment) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.setLayer(mParentLayer, 10);
- t.setLayer(mEffectLayer1, 30);
- t.setLayer(mEffectLayer2, 20);
-
- t.enableBorder(mEffectLayer1, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, GrandChildHierarchy) {
- sp<SurfaceControl> containerLayer2 =
- mClient->createSurface(String8("Container Layer"), 0 /* width */, 0 /* height */,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceContainer |
- ISurfaceComposerClient::eNoColorFill,
- mContainerLayer->getHandle());
- EXPECT_NE(nullptr, containerLayer2.get()) << "failed to create container layer 2";
-
- sp<SurfaceControl> effectLayer3 =
- mClient->createSurface(String8("Effect Layer"), 0 /* width */, 0 /* height */,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceEffect |
- ISurfaceComposerClient::eNoColorFill,
- containerLayer2->getHandle());
-
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.setCrop(effectLayer3, Rect(400, 400, 800, 800));
- t.setColor(effectLayer3, toHalf3(Color::BLUE));
-
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(effectLayer3);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, TransparentAlpha) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.setAlpha(mEffectLayer1, 0.0f);
-
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, SemiTransparentAlpha) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
- t.setAlpha(mEffectLayer2, 0.5f);
-
- t.enableBorder(mEffectLayer2, true, 20, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, InvisibleLayers) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- t.hide(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, LayerWithBuffer) {
- asTransaction([&](Transaction& t) {
- t.hide(mEffectLayer1);
- t.hide(mEffectLayer2);
- t.show(mContainerLayer);
-
- sp<SurfaceControl> layer =
- mClient->createSurface(String8("BufferState"), 0 /* width */, 0 /* height */,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceBufferState,
- mContainerLayer->getHandle());
-
- sp<GraphicBuffer> buffer =
- sp<GraphicBuffer>::make(400u, 400u, PIXEL_FORMAT_RGBA_8888, 1u,
- BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
- BufferUsage::COMPOSER_OVERLAY |
- BufferUsage::GPU_TEXTURE,
- "test");
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(0, 0, 200, 200), Color::GREEN);
- TransactionUtils::fillGraphicBufferColor(buffer, Rect(200, 200, 400, 400), Color::BLUE);
-
- t.setBuffer(layer, buffer);
- t.setPosition(layer, 100, 100);
- t.show(layer);
- t.enableBorder(mContainerLayer, true, 20, mColorOrange);
- });
-}
-
-TEST_F(LayerBorderTest, CustomWidth) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 50, mColorOrange);
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, CustomColor) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 400, 400));
- t.setCrop(mEffectLayer2, Rect(200, 200, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 20, toHalf4({255, 0, 255, 255}));
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-TEST_F(LayerBorderTest, CustomWidthAndColorAndOpacity) {
- asTransaction([&](Transaction& t) {
- t.setCrop(mEffectLayer1, Rect(0, 0, 200, 200));
- t.setCrop(mEffectLayer2, Rect(400, 400, 600, 600));
-
- t.enableBorder(mContainerLayer, true, 40, toHalf4({255, 255, 0, 128}));
- t.show(mEffectLayer1);
- t.show(mEffectLayer2);
- t.show(mContainerLayer);
- });
-}
-
-} // namespace android
-
-// TODO(b/129481165): remove the #pragma below and fix conversion issues
-#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 2b1834d..4b3ad8a 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -154,8 +154,6 @@
switch (layerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
- break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction().setPosition(layerG, 16, 16).setRelativeLayer(layerG, layerR, 1).apply();
break;
@@ -200,13 +198,6 @@
// layerR = 0, layerG = layerR + 3, layerB = 2
switch (layerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setPosition(layerG, 8, 8)
- .setRelativeLayer(layerG, layerR, 3)
- .setPosition(layerB, 16, 16)
- .setLayer(layerB, mLayerZBase + 2)
- .apply();
- break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction()
.setPosition(layerG, 8, 8)
@@ -413,13 +404,6 @@
switch (layerType) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
- Transaction()
- .setAlpha(layer1, 0.25f)
- .setAlpha(layer2, 0.75f)
- .setPosition(layer2, 16, 0)
- .setLayer(layer2, mLayerZBase + 1)
- .apply();
- break;
case ISurfaceComposerClient::eFXSurfaceBufferState:
Transaction()
.setAlpha(layer1, 0.25f)
diff --git a/services/surfaceflinger/tests/LayerTransactionTest.h b/services/surfaceflinger/tests/LayerTransactionTest.h
index c9af432..5b056d0 100644
--- a/services/surfaceflinger/tests/LayerTransactionTest.h
+++ b/services/surfaceflinger/tests/LayerTransactionTest.h
@@ -106,6 +106,10 @@
return colorLayer;
}
+ sp<SurfaceControl> mirrorSurface(SurfaceControl* mirrorFromSurface) {
+ return mClient->mirrorSurface(mirrorFromSurface);
+ }
+
ANativeWindow_Buffer getBufferQueueLayerBuffer(const sp<SurfaceControl>& layer) {
// wait for previous transactions (such as setSize) to complete
Transaction().apply(true);
diff --git a/services/surfaceflinger/tests/OWNERS b/services/surfaceflinger/tests/OWNERS
new file mode 100644
index 0000000..56f2f1b
--- /dev/null
+++ b/services/surfaceflinger/tests/OWNERS
@@ -0,0 +1,8 @@
+per-file HdrSdrRatioOverlay_test.cpp = alecmouri@google.com, sallyqi@google.com, jreck@google.com
+
+# Most layer-related files are owned by WM
+per-file Layer* = set noparent
+per-file Layer* = pdwilliams@google.com, vishnun@google.com, melodymhsu@google.com
+
+per-file LayerHistoryTest.cpp = file:/services/surfaceflinger/OWNERS
+per-file LayerInfoTest.cpp = file:/services/surfaceflinger/OWNERS
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 18262f6..9a78550 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -1039,6 +1039,29 @@
ASSERT_TRUE(mCapture->capturedHdrLayers());
}
+TEST_F(ScreenCaptureTest, captureOffscreenNullSnapshot) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ mBGSurfaceControl.get()));
+
+ // A mirrored layer will not have a snapshot. Testing an offscreen mirrored layer
+ // ensures that the screenshot path handles cases where snapshots are null.
+ sp<SurfaceControl> mirroredLayer;
+ ASSERT_NO_FATAL_FAILURE(mirroredLayer = mirrorSurface(layer.get()));
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mirroredLayer->getHandle();
+ captureArgs.sourceCrop = Rect(0, 0, 1, 1);
+
+ // Screenshot path should only use the children of the layer hierarchy so
+ // that it will not create a new snapshot. A snapshot would otherwise be
+ // created to pass on the properties of the parent, which is not needed
+ // for the purposes of this test since we explicitly want a null snapshot.
+ captureArgs.childrenOnly = true;
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
index 34e4ba5..d4c801f 100644
--- a/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
+++ b/services/surfaceflinger/tests/unittests/CommitAndCompositeTest.h
@@ -60,7 +60,7 @@
.setNativeWindow(mNativeWindow)
.setPowerMode(hal::PowerMode::ON)
.setRefreshRateSelector(mFlinger.scheduler()->refreshRateSelector())
- .skipRegisterDisplay()
+ .skipSchedulerRegistration()
.inject();
}
diff --git a/services/surfaceflinger/tests/unittests/CommitTest.cpp b/services/surfaceflinger/tests/unittests/CommitTest.cpp
index df53d19..7f29418 100644
--- a/services/surfaceflinger/tests/unittests/CommitTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CommitTest.cpp
@@ -17,9 +17,17 @@
#undef LOG_TAG
#define LOG_TAG "CommitTest"
+#include <DisplayHardware/HWComposer.h>
+#include <FrontEnd/LayerCreationArgs.h>
+#include <FrontEnd/RequestedLayerState.h>
+#include <compositionengine/CompositionEngine.h>
+#include <compositionengine/Feature.h>
+#include <compositionengine/mock/CompositionEngine.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
+#include <gui/LayerMetadata.h>
+#include <gui/SurfaceComposerClient.h>
+#include <mock/DisplayHardware/MockComposer.h>
#include <renderengine/mock/RenderEngine.h>
#include "TestableSurfaceFlinger.h"
@@ -27,18 +35,27 @@
class CommitTest : public testing::Test {
protected:
- CommitTest() {
+ TestableSurfaceFlinger mFlinger;
+ renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+
+ void flinger_setup() {
mFlinger.setupMockScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
}
- TestableSurfaceFlinger mFlinger;
- renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+
+ LayerCreationArgs createArgs(uint32_t id, LayerMetadata metadata, uint32_t parentId) {
+ LayerCreationArgs args(mFlinger.flinger(), nullptr, "layer",
+ gui::ISurfaceComposerClient::eNoColorFill, metadata, id);
+ args.parentId = parentId;
+ return args;
+ }
};
namespace {
TEST_F(CommitTest, noUpdatesDoesNotScheduleComposite) {
+ flinger_setup();
bool unused;
bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
/*transactionsFlushed=*/0, unused);
@@ -47,6 +64,7 @@
// Ensure that we handle eTransactionNeeded correctly
TEST_F(CommitTest, eTransactionNeededFlagSchedulesComposite) {
+ flinger_setup();
// update display level color matrix
mFlinger.setDaltonizerType(ColorBlindnessType::Deuteranomaly);
bool unused;
@@ -55,5 +73,92 @@
EXPECT_TRUE(mustComposite);
}
+TEST_F(CommitTest, metadataNotIncluded) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ compositionengine::mock::CompositionEngine* mCompositionEngine =
+ new compositionengine::mock::CompositionEngine();
+
+ // CompositionEngine setup with unset flag
+ compositionengine::FeatureFlags flags;
+ impl::HWComposer hwc = impl::HWComposer(std::make_unique<Hwc2::mock::Composer>());
+
+ EXPECT_CALL(*mCompositionEngine, getFeatureFlags).WillOnce(testing::Return(flags));
+ EXPECT_THAT(flags.test(compositionengine::Feature::kSnapshotLayerMetadata), false);
+
+ EXPECT_CALL(*mCompositionEngine, getHwComposer).WillOnce(testing::ReturnRef(hwc));
+
+ mFlinger.setupCompositionEngine(
+ std::unique_ptr<compositionengine::CompositionEngine>(mCompositionEngine));
+
+ // Create a parent layer with metadata and a child layer without. Metadata should not
+ // be included in the child layer when the flag is not set.
+ std::unordered_map<uint32_t, std::vector<uint8_t>> metadata = {{1, {'a', 'b'}}};
+ auto parentArgs = createArgs(1, LayerMetadata(metadata), UNASSIGNED_LAYER_ID);
+ auto parent = std::make_unique<frontend::RequestedLayerState>(parentArgs);
+ mFlinger.addLayer(parent);
+ mFlinger.injectLegacyLayer(sp<Layer>::make(parentArgs));
+
+ auto childArgs = createArgs(11, LayerMetadata(), 1);
+ auto child = std::make_unique<frontend::RequestedLayerState>(childArgs);
+ mFlinger.addLayer(child);
+ mFlinger.injectLegacyLayer(sp<Layer>::make(childArgs));
+
+ bool unused;
+ bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+ /*transactionsFlushed=*/1, unused);
+ EXPECT_TRUE(mustComposite);
+
+ auto parentMetadata = mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->layerMetadata.mMap;
+ auto childMetadata = mFlinger.mutableLayerSnapshotBuilder().getSnapshot(11)->layerMetadata.mMap;
+
+ EXPECT_EQ(metadata.at(1), parentMetadata.at(1));
+ EXPECT_NE(parentMetadata, childMetadata);
+}
+
+TEST_F(CommitTest, metadataIsIncluded) {
+ mFlinger.setupMockScheduler();
+ mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ compositionengine::mock::CompositionEngine* mCompositionEngine =
+ new compositionengine::mock::CompositionEngine();
+
+ // CompositionEngine setup with set flag
+ compositionengine::FeatureFlags flags;
+ flags |= compositionengine::Feature::kSnapshotLayerMetadata;
+ impl::HWComposer hwc = impl::HWComposer(std::make_unique<Hwc2::mock::Composer>());
+
+ EXPECT_CALL(*mCompositionEngine, getFeatureFlags).WillOnce(testing::Return(flags));
+ EXPECT_THAT(flags.test(compositionengine::Feature::kSnapshotLayerMetadata), true);
+
+ EXPECT_CALL(*mCompositionEngine, getHwComposer).WillOnce(testing::ReturnRef(hwc));
+
+ mFlinger.setupCompositionEngine(
+ std::unique_ptr<compositionengine::CompositionEngine>(mCompositionEngine));
+
+ // Create a parent layer with metadata and a child layer without. Metadata from the
+ // parent should be included in the child layer when the flag is set.
+ std::unordered_map<uint32_t, std::vector<uint8_t>> metadata = {{1, {'a', 'b'}}};
+ auto parentArgs = createArgs(1, LayerMetadata(metadata), UNASSIGNED_LAYER_ID);
+ auto parent = std::make_unique<frontend::RequestedLayerState>(parentArgs);
+ mFlinger.addLayer(parent);
+ mFlinger.injectLegacyLayer(sp<Layer>::make(parentArgs));
+
+ auto childArgs = createArgs(11, LayerMetadata(), 1);
+ auto child = std::make_unique<frontend::RequestedLayerState>(childArgs);
+ mFlinger.addLayer(child);
+ mFlinger.injectLegacyLayer(sp<Layer>::make(childArgs));
+
+ bool unused;
+ bool mustComposite = mFlinger.updateLayerSnapshots(VsyncId{1}, /*frameTimeNs=*/0,
+ /*transactionsFlushed=*/1, unused);
+ EXPECT_TRUE(mustComposite);
+
+ auto parentMetadata = mFlinger.mutableLayerSnapshotBuilder().getSnapshot(1)->layerMetadata.mMap;
+ auto childMetadata = mFlinger.mutableLayerSnapshotBuilder().getSnapshot(11)->layerMetadata.mMap;
+
+ EXPECT_EQ(metadata.at(1), parentMetadata.at(1));
+ EXPECT_EQ(parentMetadata, childMetadata);
+}
+
} // namespace
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 7d8a30a..0ddddbd 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -282,7 +282,7 @@
.setSecure(Derived::IS_SECURE)
.setPowerMode(Derived::INIT_POWER_MODE)
.setRefreshRateSelector(test->mFlinger.scheduler()->refreshRateSelector())
- .skipRegisterDisplay()
+ .skipSchedulerRegistration()
.inject();
Mock::VerifyAndClear(test->mNativeWindow.get());
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 3eabe1f..625d2e6 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -493,7 +493,7 @@
EXPECT_CALL(mockTracker, nextAnticipatedVSyncTimeFrom(_, _))
.WillOnce(Return(preferredExpectedPresentationTime));
- VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection);
+ VsyncEventData vsyncEventData = mThread->getLatestVsyncEventData(mConnection, now);
// Check EventThread immediately requested a resync.
EXPECT_TRUE(mResyncCallRecorder.waitForCall().has_value());
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 67e6249..fd15eef 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
@@ -155,6 +155,17 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setPosition(uint32_t id, float x, float y) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+ transactions.back().states.front().state.what = layer_state_t::ePositionChanged;
+ transactions.back().states.front().state.x = x;
+ transactions.back().states.front().state.y = y;
+ transactions.back().states.front().layerId = id;
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
virtual void mirrorLayer(uint32_t id, uint32_t parentId, uint32_t layerIdToMirror) {
std::vector<std::unique_ptr<RequestedLayerState>> layers;
layers.emplace_back(std::make_unique<RequestedLayerState>(
@@ -281,6 +292,24 @@
mLifecycleManager.applyTransactions(transactions);
}
+ void setInputInfo(uint32_t id, std::function<void(gui::WindowInfo&)> configureInput) {
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back();
+ transactions.back().states.push_back({});
+
+ transactions.back().states.front().state.what = layer_state_t::eInputInfoChanged;
+ transactions.back().states.front().layerId = id;
+ transactions.back().states.front().state.windowInfoHandle =
+ sp<gui::WindowInfoHandle>::make();
+ auto inputInfo = transactions.back().states.front().state.windowInfoHandle->editInfo();
+ if (!inputInfo->token) {
+ inputInfo->token = sp<BBinder>::make();
+ }
+ configureInput(*inputInfo);
+
+ mLifecycleManager.applyTransactions(transactions);
+ }
+
void setTouchableRegionCrop(uint32_t id, Region region, uint32_t touchCropId,
bool replaceTouchableRegionWithCrop) {
std::vector<TransactionState> transactions;
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 2fb80b1..a61fa1e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -1003,8 +1003,6 @@
mappings.push_back(std::make_pair(kAppId1, kThreshold1));
mappings.push_back(std::make_pair(kAppId2, kThreshold2));
- mFlinger.enableNewFrontEnd();
-
mScheduler->onActiveDisplayAreaChanged(DISPLAY_WIDTH * DISPLAY_HEIGHT);
mScheduler->updateSmallAreaDetection(mappings);
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index c63aaeb..088d0d2 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -46,6 +46,7 @@
using MockLayer = android::mock::MockLayer;
using android::mock::createDisplayMode;
+using android::mock::createVrrDisplayMode;
// WARNING: LEGACY TESTS FOR LEGACY FRONT END
// Update LayerHistoryIntegrationTest instead
@@ -138,12 +139,14 @@
ASSERT_EQ(desiredRefreshRate, summary[0].desiredRefreshRate);
}
- std::shared_ptr<RefreshRateSelector> mSelector =
- std::make_shared<RefreshRateSelector>(makeModes(createDisplayMode(DisplayModeId(0),
- LO_FPS),
- createDisplayMode(DisplayModeId(1),
- HI_FPS)),
- DisplayModeId(0));
+ static constexpr auto kVrrModeId = DisplayModeId(2);
+ std::shared_ptr<RefreshRateSelector> mSelector = std::make_shared<RefreshRateSelector>(
+ makeModes(createDisplayMode(DisplayModeId(0), LO_FPS),
+ createDisplayMode(DisplayModeId(1), HI_FPS),
+ createVrrDisplayMode(kVrrModeId, HI_FPS,
+ hal::VrrConfig{.minFrameIntervalNs =
+ HI_FPS.getPeriodNsecs()})),
+ DisplayModeId(0));
mock::SchedulerCallback mSchedulerCallback;
TestableSurfaceFlinger mFlinger;
@@ -503,7 +506,7 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
- // layer became inactive, but the vote stays
+ // layer became infrequent, but the vote stays
setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -537,7 +540,7 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
- // layer became inactive, but the vote stays
+ // layer became infrequent, but the vote stays
setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -548,6 +551,87 @@
EXPECT_EQ(0, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTest, oneLayerExplicitGte_vrr) {
+ // Set the test to be on a vrr mode.
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ mSelector->setActiveMode(kVrrModeId, HI_FPS);
+
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
+ Seamlessness::OnlySeamless,
+ FrameRateCategory::Default)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became inactive, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::ExplicitGte, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(33_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
+// Test for MRR device with VRR features enabled.
+TEST_F(LayerHistoryTest, oneLayerExplicitGte_nonVrr) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+ // The vrr_config flag is explicitly not set false because this test for an MRR device
+ // should still work in a VRR-capable world.
+
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(Return(Layer::FrameRate(33_Hz, Layer::FrameRateCompatibility::Gte,
+ Seamlessness::OnlySeamless,
+ FrameRateCategory::Default)));
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+
+ nsecs_t time = systemTime();
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer->getSequence(), layer->getLayerProps(), time, time,
+ LayerHistory::LayerUpdateType::Buffer);
+ time += HI_FPS_PERIOD;
+ }
+
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+
+ // layer became infrequent, but the vote stays
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory_vrrFeatureOff) {
SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, false);
@@ -608,7 +692,7 @@
EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
EXPECT_EQ(FrameRateCategory::High, summarizeLayerHistory(time)[0].frameRateCategory);
- // layer became inactive, but the vote stays
+ // layer became infrequent, but the vote stays
setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
@@ -645,7 +729,7 @@
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(1, frequentLayerCount(time));
- // layer became inactive
+ // layer became infrequent
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
EXPECT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(1, activeLayerCount());
@@ -686,7 +770,7 @@
EXPECT_EQ(73.4_Hz, summarizeLayerHistory(time)[1].desiredRefreshRate);
EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[1].frameRateCategory);
- // layer became inactive, but the vote stays
+ // layer became infrequent, but the vote stays
setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(2, summarizeLayerHistory(time).size());
@@ -1091,7 +1175,7 @@
EXPECT_EQ(0, frequentLayerCount(time));
EXPECT_EQ(0, animatingLayerCount(time));
- // layer became inactive
+ // Layer still active due to front buffering, but it's infrequent.
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_EQ(1, summarizeLayerHistory(time).size());
EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote);
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 94989aa..82adadc 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -1198,6 +1198,42 @@
EXPECT_TRUE(getSnapshot(11)->isSecure);
}
+TEST_F(LayerSnapshotTest, setSensitiveForTracingConfigForSecureLayers) {
+ setFlags(11, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+}
+
+TEST_F(LayerSnapshotTest, setSensitiveForTracingFromInputWindowHandle) {
+ setInputInfo(11, [](auto& inputInfo) {
+ inputInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ });
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(12)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+ EXPECT_FALSE(getSnapshot(2)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY));
+}
+
// b/314350323
TEST_F(LayerSnapshotTest, propagateDropInputMode) {
setDropInputMode(1, gui::DropInputMode::ALL);
@@ -1266,4 +1302,33 @@
EXPECT_EQ(getSnapshot(1221)->inputInfo.canOccludePresentation, true);
}
+TEST_F(LayerSnapshotTest, mirroredHierarchyIgnoresLocalTransform) {
+ SET_FLAG_FOR_TEST(flags::detached_mirror, true);
+ reparentLayer(12, UNASSIGNED_LAYER_ID);
+ setPosition(11, 2, 20);
+ setPosition(111, 20, 200);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 11);
+ std::vector<uint32_t> expected = {1, 11, 111, 13, 14, 11, 111, 2};
+ UPDATE_AND_VERIFY(mSnapshotBuilder, expected);
+
+ // mirror root has no position set
+ EXPECT_EQ(getSnapshot({.id = 11, .mirrorRootIds = 14u})->localTransform.tx(), 0);
+ EXPECT_EQ(getSnapshot({.id = 11, .mirrorRootIds = 14u})->localTransform.ty(), 0);
+ // original root still has a position
+ EXPECT_EQ(getSnapshot({.id = 11})->localTransform.tx(), 2);
+ EXPECT_EQ(getSnapshot({.id = 11})->localTransform.ty(), 20);
+
+ // mirror child still has the correct position
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootIds = 14u})->localTransform.tx(), 20);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootIds = 14u})->localTransform.ty(), 200);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootIds = 14u})->geomLayerTransform.tx(), 20);
+ EXPECT_EQ(getSnapshot({.id = 111, .mirrorRootIds = 14u})->geomLayerTransform.ty(), 200);
+
+ // original child still has the correct position including its parent's position
+ EXPECT_EQ(getSnapshot({.id = 111})->localTransform.tx(), 20);
+ EXPECT_EQ(getSnapshot({.id = 111})->localTransform.ty(), 200);
+ EXPECT_EQ(getSnapshot({.id = 111})->geomLayerTransform.tx(), 22);
+ EXPECT_EQ(getSnapshot({.id = 111})->geomLayerTransform.ty(), 220);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index d9343c7..e74f643 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -20,6 +20,8 @@
#include <DisplayHardware/PowerAdvisor.h>
#include <android_os.h>
#include <binder/Status.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/FlagManager.h>
#include <common/test/FlagUtils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -43,6 +45,7 @@
class PowerAdvisorTest : public testing::Test {
public:
void SetUp() override;
+ void SetUpFmq(bool usesSharedEventFlag, bool isQueueFull);
void startPowerHintSession(bool returnValidSession = true);
void fakeBasicFrameTiming(TimePoint startTime, Duration vsyncPeriod);
void setExpectedTiming(Duration totalFrameTargetDuration, TimePoint expectedPresentTime);
@@ -51,13 +54,37 @@
void setTimingTestingMode(bool testinMode);
void allowReportActualToAcquireMutex();
bool sessionExists();
+ int64_t toNanos(Duration d);
+
+ struct GpuTestConfig {
+ bool adpfGpuFlagOn;
+ Duration frame1GpuFenceDuration;
+ Duration frame2GpuFenceDuration;
+ Duration vsyncPeriod;
+ Duration presentDuration = 0ms;
+ Duration postCompDuration = 0ms;
+ bool frame1RequiresRenderEngine;
+ bool frame2RequiresRenderEngine;
+ bool usesFmq = false;
+ bool usesSharedFmqFlag = true;
+ bool fmqFull = false;
+ };
+
+ void testGpuScenario(GpuTestConfig& config, WorkDuration& ret);
protected:
TestableSurfaceFlinger mFlinger;
std::unique_ptr<PowerAdvisor> mPowerAdvisor;
MockPowerHalController* mMockPowerHalController;
std::shared_ptr<MockPowerHintSessionWrapper> mMockPowerHintSession;
+ std::shared_ptr<AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>> mBackendFmq;
+ std::shared_ptr<AidlMessageQueue<int8_t, SynchronizedReadWrite>> mBackendFlagQueue;
+ android::hardware::EventFlag* mEventFlag;
+ uint32_t mWriteFlagBitmask = 2;
+ uint32_t mReadFlagBitmask = 1;
+ int64_t mSessionId = 123;
SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, true);
+ SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, false);
};
bool PowerAdvisorTest::sessionExists() {
@@ -65,6 +92,10 @@
return mPowerAdvisor->mHintSession != nullptr;
}
+int64_t PowerAdvisorTest::toNanos(Duration d) {
+ return std::chrono::nanoseconds(d).count();
+}
+
void PowerAdvisorTest::SetUp() {
mPowerAdvisor = std::make_unique<impl::PowerAdvisor>(*mFlinger.flinger());
mPowerAdvisor->mPowerHal = std::make_unique<NiceMock<MockPowerHalController>>();
@@ -75,12 +106,44 @@
ByMove(HalResult<int64_t>::fromStatus(ndk::ScopedAStatus::ok(), 16000))));
}
+void PowerAdvisorTest::SetUpFmq(bool usesSharedEventFlag, bool isQueueFull) {
+ mBackendFmq = std::make_shared<
+ AidlMessageQueue<ChannelMessage, SynchronizedReadWrite>>(2, !usesSharedEventFlag);
+ ChannelConfig config;
+ config.channelDescriptor = mBackendFmq->dupeDesc();
+ if (usesSharedEventFlag) {
+ mBackendFlagQueue =
+ std::make_shared<AidlMessageQueue<int8_t, SynchronizedReadWrite>>(1, true);
+ config.eventFlagDescriptor = mBackendFlagQueue->dupeDesc();
+ ASSERT_EQ(android::hardware::EventFlag::createEventFlag(mBackendFlagQueue
+ ->getEventFlagWord(),
+ &mEventFlag),
+ android::NO_ERROR);
+ } else {
+ ASSERT_EQ(android::hardware::EventFlag::createEventFlag(mBackendFmq->getEventFlagWord(),
+ &mEventFlag),
+ android::NO_ERROR);
+ }
+ config.writeFlagBitmask = static_cast<int32_t>(mWriteFlagBitmask);
+ config.readFlagBitmask = static_cast<int32_t>(mReadFlagBitmask);
+ ON_CALL(*mMockPowerHalController, getSessionChannel)
+ .WillByDefault(Return(
+ ByMove(HalResult<ChannelConfig>::fromStatus(Status::ok(), std::move(config)))));
+ startPowerHintSession();
+ if (isQueueFull) {
+ std::vector<ChannelMessage> msgs;
+ msgs.resize(2);
+ mBackendFmq->writeBlocking(msgs.data(), 2, mReadFlagBitmask, mWriteFlagBitmask,
+ std::chrono::nanoseconds(1ms).count(), mEventFlag);
+ }
+}
+
void PowerAdvisorTest::startPowerHintSession(bool returnValidSession) {
mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
if (returnValidSession) {
ON_CALL(*mMockPowerHalController, createHintSessionWithConfig)
.WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{
- .id = 12}),
+ .id = mSessionId}),
Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(binder::Status::ok(),
mMockPowerHintSession))));
@@ -117,6 +180,131 @@
mPowerAdvisor->mDelayReportActualMutexAcquisitonPromise.set_value(true);
}
+void PowerAdvisorTest::testGpuScenario(GpuTestConfig& config, WorkDuration& ret) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::adpf_gpu_sf,
+ config.adpfGpuFlagOn);
+ SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, config.usesFmq);
+ mPowerAdvisor->onBootFinished();
+ bool expectsFmqSuccess = config.usesSharedFmqFlag && !config.fmqFull;
+ if (config.usesFmq) {
+ SetUpFmq(config.usesSharedFmqFlag, config.fmqFull);
+ } else {
+ startPowerHintSession();
+ }
+
+ std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0),
+ GpuVirtualDisplayId(1)};
+ mPowerAdvisor->setDisplays(displayIds);
+ auto display1 = displayIds[0];
+ // 60hz
+
+ TimePoint startTime = TimePoint::now();
+ int64_t target;
+ SessionHint hint;
+ if (!config.usesFmq || !expectsFmqSuccess) {
+ EXPECT_CALL(*mMockPowerHintSession, updateTargetWorkDuration(_))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<0>(&target),
+ testing::Return(testing::ByMove(HalResult<void>::ok()))));
+ EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+ testing::Return(testing::ByMove(HalResult<void>::ok()))));
+ }
+ // advisor only starts on frame 2 so do an initial frame
+ fakeBasicFrameTiming(startTime, config.vsyncPeriod);
+ // send a load hint
+ mPowerAdvisor->notifyCpuLoadUp();
+ if (config.usesFmq && expectsFmqSuccess) {
+ std::vector<ChannelMessage> msgs;
+ ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+ msgs.resize(2);
+ ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 2, mReadFlagBitmask, mWriteFlagBitmask,
+ std::chrono::nanoseconds(1ms).count(), mEventFlag));
+ ASSERT_EQ(msgs[0].sessionID, mSessionId);
+ ASSERT_GE(msgs[0].timeStampNanos, startTime.ns());
+ ASSERT_EQ(msgs[0].data.getTag(),
+ ChannelMessage::ChannelMessageContents::Tag::targetDuration);
+ target = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::targetDuration>();
+ ASSERT_EQ(msgs[1].sessionID, mSessionId);
+ ASSERT_GE(msgs[1].timeStampNanos, startTime.ns());
+ ASSERT_EQ(msgs[1].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::hint);
+ hint = msgs[1].data.get<ChannelMessage::ChannelMessageContents::Tag::hint>();
+ }
+ ASSERT_EQ(target, config.vsyncPeriod.ns());
+ ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+
+ setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod);
+
+ // report GPU
+ mPowerAdvisor->setRequiresRenderEngine(display1, config.frame1RequiresRenderEngine);
+ if (config.adpfGpuFlagOn) {
+ mPowerAdvisor->setGpuStartTime(display1, startTime);
+ }
+ if (config.frame1GpuFenceDuration.count() == Fence::SIGNAL_TIME_PENDING) {
+ mPowerAdvisor->setGpuFenceTime(display1,
+ std::make_unique<FenceTime>(Fence::SIGNAL_TIME_PENDING));
+ } else {
+ TimePoint end = startTime + config.frame1GpuFenceDuration;
+ mPowerAdvisor->setGpuFenceTime(display1, std::make_unique<FenceTime>(end.ns()));
+ }
+
+ // increment the frame
+ std::this_thread::sleep_for(config.vsyncPeriod);
+ startTime = TimePoint::now();
+ fakeBasicFrameTiming(startTime, config.vsyncPeriod);
+ if (config.usesFmq && expectsFmqSuccess) {
+ // same target update will not trigger FMQ write
+ ASSERT_EQ(mBackendFmq->availableToRead(), 0uL);
+ }
+ setExpectedTiming(config.vsyncPeriod, startTime + config.vsyncPeriod);
+
+ // report GPU
+ mPowerAdvisor->setRequiresRenderEngine(display1, config.frame2RequiresRenderEngine);
+ if (config.adpfGpuFlagOn) {
+ mPowerAdvisor->setGpuStartTime(display1, startTime);
+ }
+ if (config.frame2GpuFenceDuration.count() == Fence::SIGNAL_TIME_PENDING) {
+ mPowerAdvisor->setGpuFenceTime(display1,
+ std::make_unique<FenceTime>(Fence::SIGNAL_TIME_PENDING));
+ } else {
+ TimePoint end = startTime + config.frame2GpuFenceDuration;
+ mPowerAdvisor->setGpuFenceTime(display1, std::make_unique<FenceTime>(end.ns()));
+ }
+ mPowerAdvisor->setSfPresentTiming(startTime, startTime + config.presentDuration);
+ mPowerAdvisor->setCompositeEnd(startTime + config.presentDuration + config.postCompDuration);
+
+ // don't report timing for the HWC
+ mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime, startTime);
+ mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime, startTime);
+
+ if (config.usesFmq && expectsFmqSuccess) {
+ mPowerAdvisor->reportActualWorkDuration();
+ ASSERT_EQ(mBackendFmq->availableToRead(), 1uL);
+ std::vector<ChannelMessage> msgs;
+ msgs.resize(1);
+ ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+ std::chrono::nanoseconds(1ms).count(), mEventFlag));
+ ASSERT_EQ(msgs[0].sessionID, mSessionId);
+ ASSERT_GE(msgs[0].timeStampNanos, startTime.ns());
+ ASSERT_EQ(msgs[0].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::workDuration);
+ auto actual = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::workDuration>();
+ ret.workPeriodStartTimestampNanos = actual.workPeriodStartTimestampNanos;
+ ret.cpuDurationNanos = actual.cpuDurationNanos;
+ ret.gpuDurationNanos = actual.gpuDurationNanos;
+ ret.durationNanos = actual.durationNanos;
+ } else {
+ std::vector<aidl::android::hardware::power::WorkDuration> durationReq;
+ EXPECT_CALL(*mMockPowerHintSession, reportActualWorkDuration(_))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<0>(&durationReq),
+ testing::Return(testing::ByMove(HalResult<void>::ok()))));
+ mPowerAdvisor->reportActualWorkDuration();
+ ASSERT_EQ(durationReq.size(), 1u);
+ ret = std::move(durationReq[0]);
+ }
+}
+
Duration PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) {
return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate
: PowerAdvisor::kFenceWaitStartDelayValidated);
@@ -290,13 +478,6 @@
mPowerAdvisor->reportActualWorkDuration();
}
-TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
- EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig(_, _, _, _, _, _)).Times(1);
- mPowerAdvisor->onBootFinished();
- startPowerHintSession();
- mPowerAdvisor->startPowerHintSession({1, 2, 3});
-}
-
TEST_F(PowerAdvisorTest, hintSessionTestNotifyReportRace) {
// notifyDisplayUpdateImminentAndCpuReset or notifyCpuLoadUp gets called in background
// reportActual gets called during callback and sees true session, passes ensure
@@ -379,15 +560,282 @@
}
TEST_F(PowerAdvisorTest, legacyHintSessionCreationStillWorks) {
- SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel, false);
mPowerAdvisor->onBootFinished();
mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
+ EXPECT_CALL(*mMockPowerHalController, createHintSessionWithConfig)
+ .Times(1)
+ .WillOnce(Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+ fromStatus(ndk::ScopedAStatus::fromExceptionCode(
+ EX_UNSUPPORTED_OPERATION),
+ nullptr)));
+
EXPECT_CALL(*mMockPowerHalController, createHintSession)
.Times(1)
.WillOnce(Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(binder::Status::ok(), mMockPowerHintSession)));
mPowerAdvisor->enablePowerHintSession(true);
- mPowerAdvisor->startPowerHintSession({1, 2, 3});
+ ASSERT_TRUE(mPowerAdvisor->startPowerHintSession({1, 2, 3}));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_cpuThenGpuFrames) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = false,
+ // faked buffer fence time for testing
+ .frame1GpuFenceDuration = 41ms,
+ .frame2GpuFenceDuration = 31ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = false,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, 0L);
+ EXPECT_EQ(res.cpuDurationNanos, 0L);
+ EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
+ EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_cpuThenGpuFrames_flagOn) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 40ms,
+ .frame2GpuFenceDuration = 30ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = false,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
+ EXPECT_EQ(res.durationNanos, toNanos(30ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_gpuThenCpuFrames) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = false,
+ // faked fence time for testing
+ .frame1GpuFenceDuration = 41ms,
+ .frame2GpuFenceDuration = 31ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = false,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, 0L);
+ EXPECT_EQ(res.cpuDurationNanos, 0L);
+ EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_gpuThenCpuFrames_flagOn) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 40ms,
+ .frame2GpuFenceDuration = 30ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = false,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, 0L);
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
+ EXPECT_EQ(res.durationNanos, toNanos(10ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_twoSignaledGpuFrames) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = false,
+ // added a margin as a workaround since we set GPU start time at the time of fence set
+ // call
+ .frame1GpuFenceDuration = 31ms,
+ .frame2GpuFenceDuration = 51ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, 0L);
+ EXPECT_EQ(res.cpuDurationNanos, 0L);
+ EXPECT_GE(res.durationNanos, toNanos(50ms + getErrorMargin()));
+ EXPECT_LE(res.durationNanos, toNanos(51ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_twoSignaledGpuFenceFrames_flagOn) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 30ms,
+ .frame2GpuFenceDuration = 50ms,
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(50ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(10ms));
+ EXPECT_EQ(res.durationNanos, toNanos(50ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_UnsingaledGpuFenceFrameUsingPreviousFrame) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = false,
+ .frame1GpuFenceDuration = 31ms,
+ .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+ .vsyncPeriod = 10ms,
+ .presentDuration = 2ms,
+ .postCompDuration = 8ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, 0L);
+ EXPECT_EQ(res.cpuDurationNanos, 0L);
+ EXPECT_GE(res.durationNanos, toNanos(30ms + getErrorMargin()));
+ EXPECT_LE(res.durationNanos, toNanos(31ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, setGpuFenceTime_UnsingaledGpuFenceFrameUsingPreviousFrame_flagOn) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 30ms,
+ .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+ .vsyncPeriod = 10ms,
+ .presentDuration = 22ms,
+ .postCompDuration = 88ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+ EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 30ms,
+ .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+ .vsyncPeriod = 10ms,
+ .presentDuration = 22ms,
+ .postCompDuration = 88ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ .usesFmq = true,
+ .usesSharedFmqFlag = true,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+ EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration_noSharedFlag) {
+ GpuTestConfig config{
+ .adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 30ms,
+ .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+ .vsyncPeriod = 10ms,
+ .presentDuration = 22ms,
+ .postCompDuration = 88ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ .usesFmq = true,
+ .usesSharedFmqFlag = false,
+ };
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+ EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendTargetAndActualDuration_queueFull) {
+ GpuTestConfig config{.adpfGpuFlagOn = true,
+ .frame1GpuFenceDuration = 30ms,
+ .frame2GpuFenceDuration = Duration::fromNs(Fence::SIGNAL_TIME_PENDING),
+ .vsyncPeriod = 10ms,
+ .presentDuration = 22ms,
+ .postCompDuration = 88ms,
+ .frame1RequiresRenderEngine = true,
+ .frame2RequiresRenderEngine = true,
+ .usesFmq = true,
+ .usesSharedFmqFlag = true,
+ .fmqFull = true};
+ WorkDuration res;
+ testGpuScenario(config, res);
+ EXPECT_EQ(res.gpuDurationNanos, toNanos(30ms));
+ EXPECT_EQ(res.cpuDurationNanos, toNanos(110ms));
+ EXPECT_EQ(res.durationNanos, toNanos(110ms + getErrorMargin()));
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint) {
+ SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+ mPowerAdvisor->onBootFinished();
+ SetUpFmq(true, false);
+ auto startTime = uptimeNanos();
+ mPowerAdvisor->notifyCpuLoadUp();
+ std::vector<ChannelMessage> msgs;
+ ASSERT_EQ(mBackendFmq->availableToRead(), 1uL);
+ msgs.resize(1);
+ ASSERT_TRUE(mBackendFmq->readBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+ std::chrono::nanoseconds(1ms).count(), mEventFlag));
+ ASSERT_EQ(msgs[0].sessionID, mSessionId);
+ ASSERT_GE(msgs[0].timeStampNanos, startTime);
+ ASSERT_EQ(msgs[0].data.getTag(), ChannelMessage::ChannelMessageContents::Tag::hint);
+ auto hint = msgs[0].data.get<ChannelMessage::ChannelMessageContents::Tag::hint>();
+ ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint_noSharedFlag) {
+ SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+ mPowerAdvisor->onBootFinished();
+ SetUpFmq(false, false);
+ SessionHint hint;
+ EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+ testing::Return(testing::ByMove(HalResult<void>::ok()))));
+ mPowerAdvisor->notifyCpuLoadUp();
+ ASSERT_EQ(mBackendFmq->availableToRead(), 0uL);
+ ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
+}
+
+TEST_F(PowerAdvisorTest, fmq_sendHint_queueFull) {
+ SET_FLAG_FOR_TEST(android::os::adpf_use_fmq_channel_fixed, true);
+ mPowerAdvisor->onBootFinished();
+ SetUpFmq(true, true);
+ ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+ SessionHint hint;
+ EXPECT_CALL(*mMockPowerHintSession, sendHint(_))
+ .Times(1)
+ .WillOnce(DoAll(testing::SaveArg<0>(&hint),
+ testing::Return(testing::ByMove(HalResult<void>::ok()))));
+ std::vector<ChannelMessage> msgs;
+ msgs.resize(1);
+ mBackendFmq->writeBlocking(msgs.data(), 1, mReadFlagBitmask, mWriteFlagBitmask,
+ std::chrono::nanoseconds(1ms).count(), mEventFlag);
+ mPowerAdvisor->notifyCpuLoadUp();
+ ASSERT_EQ(mBackendFmq->availableToRead(), 2uL);
+ ASSERT_EQ(hint, SessionHint::CPU_LOAD_UP);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 0ede612..cf9a7d3 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -2078,7 +2078,7 @@
constexpr std::chrono::milliseconds kIdleTimerTimeoutMs = 10ms;
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120,
Config{
- .idleTimerTimeout = kIdleTimerTimeoutMs,
+ .legacyIdleTimerTimeout = kIdleTimerTimeoutMs,
});
ASSERT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
ASSERT_EQ(kIdleTimerTimeoutMs, selector.getIdleTimerTimeout());
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
index f127213..5852b1c 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
@@ -28,6 +28,7 @@
class ColorMatrixTest : public CommitAndCompositeTest {};
TEST_F(ColorMatrixTest, colorMatrixChanged) {
+ mFlinger.enableLayerLifecycleManager();
EXPECT_COLOR_MATRIX_CHANGED(true, true);
mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
@@ -45,6 +46,7 @@
}
TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) {
+ mFlinger.enableLayerLifecycleManager();
EXPECT_COLOR_MATRIX_CHANGED(true, true);
mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 22b72f9..f2e2c8a 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -33,24 +33,45 @@
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
using namespace std::chrono_literals;
+ std::mutex timerMutex;
+ std::condition_variable cv;
+
injectDefaultInternalDisplay([](FakeDisplayDeviceInjector&) {});
- mFlinger.scheduler()->replaceTouchTimer(100);
- std::this_thread::sleep_for(10ms); // wait for callback to be triggered
+ std::unique_lock lock(timerMutex);
+ bool didReset = false; // keeps track of what the most recent call was
+
+ auto waitForTimerReset = [&] { cv.wait_for(lock, 100ms, [&] { return didReset; }); };
+ auto waitForTimerExpired = [&] { cv.wait_for(lock, 100ms, [&] { return !didReset; }); };
+
+ // Add extra logic to unblock the test when the timer callbacks get called
+ mFlinger.scheduler()->replaceTouchTimer(10, [&](bool isReset) {
+ {
+ std::unique_lock lock(timerMutex); // guarantee we're waiting on the cv
+ didReset = isReset;
+ }
+ cv.notify_one(); // wake the cv
+ std::unique_lock lock(timerMutex); // guarantee we finished the cv logic
+ });
+
+ waitForTimerReset();
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+ waitForTimerExpired();
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive()); // Stopping timer deactivates touch
EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::CAMERA_SHOT)));
- std::this_thread::sleep_for(10ms); // wait for callback to maybe be triggered
- EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
- std::this_thread::sleep_for(110ms); // wait for reset touch timer to expire and trigger callback
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+ // Wait for the timer to start just in case
+ waitForTimerReset();
+ EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
+ // Wait for the timer to stop, again just in case
+ waitForTimerExpired();
EXPECT_FALSE(mFlinger.scheduler()->isTouchActive());
EXPECT_EQ(NO_ERROR, mFlinger.notifyPowerBoost(static_cast<int32_t>(Boost::INTERACTION)));
- std::this_thread::sleep_for(10ms); // wait for callback to be triggered.
+ waitForTimerReset();
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive());
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index c0796df..1bae5ff 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -255,10 +255,15 @@
colorModes.push_back(ColorMode::DISPLAY_P3);
}
- mFlinger.mutablePhysicalDisplays().emplace_or_replace(*displayId, displayToken, *displayId,
- *connectionType,
- makeModes(activeMode),
- std::move(colorModes), std::nullopt);
+ const auto it = mFlinger.mutablePhysicalDisplays()
+ .emplace_or_replace(*displayId, displayToken, *displayId,
+ *connectionType, makeModes(activeMode),
+ std::move(colorModes), std::nullopt)
+ .first;
+
+ FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger.mutableDisplayModeController()
+ .registerDisplay(it->second.snapshot(), activeMode->getId(), {}));
}
state.isSecure = static_cast<bool>(Case::Display::SECURE);
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1472ebf..1e02c67 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -140,14 +140,25 @@
return mLayerHistory.mActiveLayerInfos.size();
}
- void replaceTouchTimer(int64_t millis) {
+ void replaceTouchTimer(int64_t millis,
+ std::function<void(bool isReset)>&& testCallback = nullptr) {
if (mTouchTimer) {
mTouchTimer.reset();
}
mTouchTimer.emplace(
"Testable Touch timer", std::chrono::milliseconds(millis),
- [this] { touchTimerCallback(TimerState::Reset); },
- [this] { touchTimerCallback(TimerState::Expired); });
+ [this, testCallback] {
+ touchTimerCallback(TimerState::Reset);
+ if (testCallback != nullptr) {
+ testCallback(true);
+ }
+ },
+ [this, testCallback] {
+ touchTimerCallback(TimerState::Expired);
+ if (testCallback != nullptr) {
+ testCallback(false);
+ }
+ });
mTouchTimer->start();
}
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3b03f92..b251e2c 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -18,10 +18,12 @@
#include <algorithm>
#include <chrono>
+#include <memory>
#include <variant>
#include <ftl/fake_guard.h>
#include <ftl/match.h>
+#include <gui/LayerMetadata.h>
#include <gui/ScreenCaptureResults.h>
#include <ui/DynamicDisplayInfo.h>
@@ -38,6 +40,7 @@
#include "FrameTracer/FrameTracer.h"
#include "FrontEnd/LayerCreationArgs.h"
#include "FrontEnd/LayerHandle.h"
+#include "FrontEnd/RequestedLayerState.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
#include "RenderArea.h"
@@ -45,6 +48,7 @@
#include "Scheduler/RefreshRateSelector.h"
#include "SurfaceFlinger.h"
#include "TestableScheduler.h"
+#include "android/gui/ISurfaceComposerClient.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockDisplayMode.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
@@ -127,7 +131,7 @@
sp<Layer> createEffectLayer(const LayerCreationArgs&) override { return nullptr; }
- sp<LayerFE> createLayerFE(const std::string& layerName) override {
+ sp<LayerFE> createLayerFE(const std::string& layerName, const Layer* /* owner */) override {
return sp<LayerFE>::make(layerName);
}
@@ -197,6 +201,11 @@
mFlinger->mCompositionEngine->setTimeStats(timeStats);
}
+ void setupCompositionEngine(
+ std::unique_ptr<compositionengine::CompositionEngine> compositionEngine) {
+ mFlinger->mCompositionEngine = std::move(compositionEngine);
+ }
+
enum class SchedulerCallbackImpl { kNoOp, kMock };
struct DefaultDisplayMode {
@@ -446,6 +455,7 @@
void commitTransactionsLocked(uint32_t transactionFlags) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
+ mFlinger->processDisplayChangesLocked();
mFlinger->commitTransactionsLocked(transactionFlags);
}
@@ -473,7 +483,7 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
+ auto renderScreenImpl(std::unique_ptr<const RenderArea> renderArea,
SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling) {
@@ -499,9 +509,11 @@
auto& getTransactionQueue() { return mFlinger->mTransactionHandler.mLocklessTransactionQueue; }
auto& getPendingTransactionQueue() {
+ ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionQueues;
}
size_t getPendingTransactionCount() {
+ ftl::FakeGuard guard(kMainThreadContext);
return mFlinger->mTransactionHandler.mPendingTransactionCount.load();
}
@@ -520,7 +532,9 @@
}
auto setTransactionStateInternal(TransactionState& transaction) {
- return mFlinger->mTransactionHandler.queueTransaction(std::move(transaction));
+ return FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->mTransactionHandler.queueTransaction(
+ std::move(transaction)));
}
auto flushTransactionQueues() {
@@ -590,6 +604,13 @@
return mFlinger->getDisplayStats(displayToken, outInfo);
}
+ // Used to add a layer before updateLayerSnapshots is called.
+ // Must have transactionsFlushed enabled for the new layer to be updated.
+ void addLayer(std::unique_ptr<frontend::RequestedLayerState>& layer) {
+ std::scoped_lock<std::mutex> lock(mFlinger->mCreatedLayersLock);
+ mFlinger->mNewLayers.emplace_back(std::move(layer));
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -605,15 +626,20 @@
}
void injectLegacyLayer(sp<Layer> layer) {
- mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer;
+ FTL_FAKE_GUARD(kMainThreadContext,
+ mFlinger->mLegacyLayers[static_cast<uint32_t>(layer->sequence)] = layer);
};
- void releaseLegacyLayer(uint32_t sequence) { mFlinger->mLegacyLayers.erase(sequence); };
+ void releaseLegacyLayer(uint32_t sequence) {
+ FTL_FAKE_GUARD(kMainThreadContext, mFlinger->mLegacyLayers.erase(sequence));
+ };
auto setLayerHistoryDisplayArea(uint32_t displayArea) {
return mFlinger->mScheduler->onActiveDisplayAreaChanged(displayArea);
};
- auto updateLayerHistory(nsecs_t now) { return mFlinger->updateLayerHistory(now); };
+ auto updateLayerHistory(nsecs_t now) {
+ return FTL_FAKE_GUARD(kMainThreadContext, mFlinger->updateLayerHistory(now));
+ };
auto setDaltonizerType(ColorBlindnessType type) {
mFlinger->mDaltonizer.setType(type);
return mFlinger->updateColorMatrixLocked();
@@ -639,6 +665,7 @@
auto& mutableSupportsWideColor() { return mFlinger->mSupportsWideColor; }
+ auto& mutableDisplayModeController() { return mFlinger->mDisplayModeController; }
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
auto& mutableDisplays() { return mFlinger->mDisplays; }
@@ -673,10 +700,9 @@
return mFlinger->initTransactionTraceWriter();
}
- void enableNewFrontEnd() {
- mFlinger->mLayerLifecycleManagerEnabled = true;
- mFlinger->mLegacyFrontEndEnabled = false;
- }
+ // Needed since mLayerLifecycleManagerEnabled is false by default and must
+ // be enabled for tests to go through the new front end path.
+ void enableLayerLifecycleManager() { mFlinger->mLayerLifecycleManagerEnabled = true; }
void notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, Period vsyncPeriod,
TimePoint expectedPresentTime, Fps frameInterval,
@@ -954,14 +980,14 @@
auto& setDisplayModes(DisplayModes modes, DisplayModeId activeModeId) {
mDisplayModes = std::move(modes);
- mCreationArgs.activeModeId = activeModeId;
+ mActiveModeId = activeModeId;
mCreationArgs.refreshRateSelector = nullptr;
return *this;
}
auto& setRefreshRateSelector(RefreshRateSelectorPtr selectorPtr) {
mDisplayModes = selectorPtr->displayModes();
- mCreationArgs.activeModeId = selectorPtr->getActiveMode().modePtr->getId();
+ mActiveModeId = selectorPtr->getActiveMode().modePtr->getId();
mCreationArgs.refreshRateSelector = std::move(selectorPtr);
return *this;
}
@@ -1003,8 +1029,9 @@
return *this;
}
- auto& skipRegisterDisplay() {
- mRegisterDisplay = false;
+ // Used to avoid overwriting mocks injected by TestableSurfaceFlinger::setupMockScheduler.
+ auto& skipSchedulerRegistration() {
+ mSchedulerRegistration = false;
return *this;
}
@@ -1017,12 +1044,24 @@
std::shared_ptr<android::scheduler::VSyncTracker> tracker)
NO_THREAD_SAFETY_ANALYSIS {
const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
auto& modes = mDisplayModes;
- auto& activeModeId = mCreationArgs.activeModeId;
+ auto& activeModeId = mActiveModeId;
+ std::optional<Fps> refreshRateOpt;
- if (displayId && !mCreationArgs.refreshRateSelector) {
- if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+ DisplayDeviceState state;
+ state.isSecure = mCreationArgs.isSecure;
+
+ if (const auto physicalId = PhysicalDisplayId::tryCast(*displayId)) {
+ LOG_ALWAYS_FATAL_IF(!mConnectionType);
+ LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
+
+ if (mCreationArgs.isPrimary) {
+ mFlinger.mutableActiveDisplayId() = *physicalId;
+ }
+
+ if (!mCreationArgs.refreshRateSelector) {
if (modes.empty()) {
constexpr DisplayModeId kModeId{0};
DisplayModePtr mode =
@@ -1044,48 +1083,38 @@
mCreationArgs.refreshRateSelector =
std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
}
+
+ const auto activeModeOpt = modes.get(activeModeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+ refreshRateOpt = activeModeOpt->get()->getPeakFps();
+
+ state.physical = {.id = *physicalId,
+ .hwcDisplayId = *mHwcDisplayId,
+ .activeMode = activeModeOpt->get()};
+
+ const auto it = mFlinger.mutablePhysicalDisplays()
+ .emplace_or_replace(*physicalId, mDisplayToken, *physicalId,
+ *mConnectionType, std::move(modes),
+ ui::ColorModes(), std::nullopt)
+ .first;
+
+ mFlinger.mutableDisplayModeController()
+ .registerDisplay(*physicalId, it->second.snapshot(),
+ mCreationArgs.refreshRateSelector);
+
+ if (mFlinger.scheduler() && mSchedulerRegistration) {
+ mFlinger.scheduler()->registerDisplay(*physicalId,
+ mCreationArgs.refreshRateSelector,
+ std::move(controller),
+ std::move(tracker));
+ }
}
sp<DisplayDevice> display = sp<DisplayDevice>::make(mCreationArgs);
mFlinger.mutableDisplays().emplace_or_replace(mDisplayToken, display);
- DisplayDeviceState state;
- state.isSecure = mCreationArgs.isSecure;
-
- if (mConnectionType) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- const auto physicalIdOpt = PhysicalDisplayId::tryCast(*displayId);
- LOG_ALWAYS_FATAL_IF(!physicalIdOpt);
- const auto physicalId = *physicalIdOpt;
-
- if (mCreationArgs.isPrimary) {
- mFlinger.mutableActiveDisplayId() = physicalId;
- }
-
- LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
-
- const auto activeMode = modes.get(activeModeId);
- LOG_ALWAYS_FATAL_IF(!activeMode);
- const auto fps = activeMode->get()->getPeakFps();
-
- state.physical = {.id = physicalId,
- .hwcDisplayId = *mHwcDisplayId,
- .activeMode = activeMode->get()};
-
- mFlinger.mutablePhysicalDisplays().emplace_or_replace(physicalId, mDisplayToken,
- physicalId, *mConnectionType,
- std::move(modes),
- ui::ColorModes(),
- std::nullopt);
-
- if (mFlinger.scheduler() && mRegisterDisplay) {
- mFlinger.scheduler()->registerDisplay(physicalId,
- display->holdRefreshRateSelector(),
- std::move(controller),
- std::move(tracker));
- }
-
- display->setActiveMode(activeModeId, fps, fps);
+ if (refreshRateOpt) {
+ display->setActiveMode(activeModeId, *refreshRateOpt, *refreshRateOpt);
}
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
@@ -1099,7 +1128,8 @@
sp<BBinder> mDisplayToken = sp<BBinder>::make();
DisplayDeviceCreationArgs mCreationArgs;
DisplayModes mDisplayModes;
- bool mRegisterDisplay = true;
+ DisplayModeId mActiveModeId;
+ bool mSchedulerRegistration = true;
const std::optional<ui::DisplayConnectionType> mConnectionType;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index 1f2a1ed..7fb9247 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "TransactionApplicationTest"
+#include <common/test/FlagUtils.h>
#include <compositionengine/Display.h>
#include <compositionengine/mock/DisplaySurface.h>
#include <gmock/gmock.h>
@@ -34,8 +35,11 @@
#include "TestableSurfaceFlinger.h"
#include "TransactionState.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
namespace android {
+using namespace com::android::graphics::surfaceflinger;
using testing::_;
using testing::Return;
@@ -498,6 +502,44 @@
setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
}
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_AutoRefreshChanged) {
+ SET_FLAG_FOR_TEST(flags::latch_unsignaled_with_auto_refresh_changed, false);
+ const sp<IBinder> kApplyToken =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ const auto kLayerId = 1;
+ const auto kExpectedTransactionsPending = 1u;
+
+ const auto unsignaledTransaction =
+ createTransactionInfo(kApplyToken,
+ {
+ createComposerState(kLayerId,
+ fence(Fence::Status::Unsignaled),
+ layer_state_t::eAutoRefreshChanged |
+ layer_state_t::
+ eBufferChanged),
+ });
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
+TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_RemovesUnSignaledInTheQueue_AutoRefreshChanged) {
+ SET_FLAG_FOR_TEST(flags::latch_unsignaled_with_auto_refresh_changed, true);
+ const sp<IBinder> kApplyToken =
+ IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ const auto kLayerId = 1;
+ const auto kExpectedTransactionsPending = 0u;
+
+ const auto unsignaledTransaction =
+ createTransactionInfo(kApplyToken,
+ {
+ createComposerState(kLayerId,
+ fence(Fence::Status::Unsignaled),
+ layer_state_t::eAutoRefreshChanged |
+ layer_state_t::
+ eBufferChanged),
+ });
+ setTransactionStates({unsignaledTransaction}, kExpectedTransactionsPending);
+}
+
TEST_F(LatchUnsignaledAutoSingleLayerTest, Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed) {
const sp<IBinder> kApplyToken =
IInterface::asBinder(TransactionCompletedListener::getIInstance());
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index cbb597a..af02330 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -106,7 +106,7 @@
TEST(TransactionProtoParserTest, parseDisplayInfo) {
frontend::DisplayInfo d1;
- d1.info.displayId = 42;
+ d1.info.displayId = ui::LogicalDisplayId{42};
d1.info.logicalWidth = 43;
d1.info.logicalHeight = 44;
d1.info.transform.set(1, 2, 3, 4);
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index d701a97..3b09554 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -54,6 +54,7 @@
void onFrameBegin(TimePoint, TimePoint) final {}
void onFrameMissed(TimePoint) final {}
void dump(std::string&) const final {}
+ bool isCurrentMode(const ftl::NonNull<DisplayModePtr>&) const final { return false; };
protected:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 8d9623d..51373c1 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -195,9 +195,7 @@
TEST_F(VSyncReactorTest, ignoresProperlyAfterAPeriodConfirmation) {
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
- mReactor.setIgnorePresentFences(true);
-
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
nsecs_t const newPeriod = 5000;
mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
@@ -207,7 +205,7 @@
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, std::nullopt, &periodFlushed));
EXPECT_TRUE(periodFlushed);
- EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, setPeriodCalledOnceConfirmedChange) {
@@ -232,7 +230,8 @@
TEST_F(VSyncReactorTest, changingPeriodBackAbortsConfirmationProcess) {
nsecs_t sampleTime = 0;
nsecs_t const newPeriod = 5000;
- mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
+ auto modePtr = displayMode(newPeriod);
+ mReactor.onDisplayModeChanged(modePtr, false);
bool periodFlushed = true;
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
@@ -240,7 +239,9 @@
EXPECT_TRUE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
- mReactor.onDisplayModeChanged(displayMode(period), false);
+ modePtr = displayMode(period);
+ EXPECT_CALL(*mMockTracker, isCurrentMode(modePtr)).WillOnce(Return(true));
+ mReactor.onDisplayModeChanged(modePtr, false);
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(sampleTime += period, std::nullopt, &periodFlushed));
EXPECT_FALSE(periodFlushed);
}
@@ -463,8 +464,7 @@
TEST_F(VSyncReactorTest, periodChangeWithGivenVsyncPeriod) {
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(2);
- mReactor.setIgnorePresentFences(true);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(3);
nsecs_t const newPeriod = 5000;
mReactor.onDisplayModeChanged(displayMode(newPeriod), false);
@@ -476,7 +476,7 @@
EXPECT_FALSE(mReactor.addHwVsyncTimestamp(newPeriod, newPeriod, &periodFlushed));
EXPECT_TRUE(periodFlushed);
- EXPECT_TRUE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
TEST_F(VSyncReactorTest, periodIsMeasuredIfIgnoringComposer) {
@@ -486,8 +486,7 @@
*mMockTracker, kPendingLimit, true /* supportKernelIdleTimer */);
bool periodFlushed = true;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(4);
- idleReactor.setIgnorePresentFences(true);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(5);
// First, set the same period, which should only be confirmed when we receive two
// matching callbacks
@@ -512,7 +511,7 @@
EXPECT_FALSE(idleReactor.addHwVsyncTimestamp(20000, 5000, &periodFlushed));
EXPECT_TRUE(periodFlushed);
- EXPECT_TRUE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
+ EXPECT_FALSE(idleReactor.addPresentFence(generateSignalledFenceWithTime(0)));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index 8e8eb1d..4efdfe8 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -36,10 +36,12 @@
MOCK_METHOD(void, notifyDisplayUpdateImminentAndCpuReset, (), (override));
MOCK_METHOD(bool, usePowerHintSession, (), (override));
MOCK_METHOD(bool, supportsPowerHintSession, (), (override));
+ MOCK_METHOD(bool, supportsGpuReporting, (), (override));
MOCK_METHOD(void, updateTargetWorkDuration, (Duration targetDuration), (override));
MOCK_METHOD(void, reportActualWorkDuration, (), (override));
MOCK_METHOD(void, enablePowerHintSession, (bool enabled), (override));
MOCK_METHOD(bool, startPowerHintSession, (std::vector<int32_t> && threadIds), (override));
+ MOCK_METHOD(void, setGpuStartTime, (DisplayId displayId, TimePoint startTime), (override));
MOCK_METHOD(void, setGpuFenceTime,
(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override));
MOCK_METHOD(void, setHwcValidateTiming,
@@ -49,8 +51,8 @@
(DisplayId displayId, TimePoint presentStartTime, TimePoint presentEndTime),
(override));
MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override));
- MOCK_METHOD(void, setRequiresClientComposition,
- (DisplayId displayId, bool requiresClientComposition), (override));
+ MOCK_METHOD(void, setRequiresRenderEngine, (DisplayId displayId, bool requiresRenderEngine),
+ (override));
MOCK_METHOD(void, setExpectedPresentTime, (TimePoint expectedPresentTime), (override));
MOCK_METHOD(void, setSfPresentTiming, (TimePoint presentFenceTime, TimePoint presentEndTime),
(override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index e2b0ed1..8dd1a34 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -55,7 +55,7 @@
(override));
MOCK_METHOD(void, requestNextVsync, (const sp<android::EventThreadConnection>&), (override));
MOCK_METHOD(VsyncEventData, getLatestVsyncEventData,
- (const sp<android::EventThreadConnection>&), (const, override));
+ (const sp<android::EventThreadConnection>&, nsecs_t), (const, override));
MOCK_METHOD(void, requestLatestConfig, (const sp<android::EventThreadConnection>&));
MOCK_METHOD(void, pauseVsyncCallback, (bool));
MOCK_METHOD(void, onNewVsyncSchedule, (std::shared_ptr<scheduler::VsyncSchedule>), (override));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index 4204aa0..002fa9f 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -40,7 +40,7 @@
MOCK_CONST_METHOD0(getType, const char*());
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
- MOCK_METHOD1(createClone, sp<Layer>(uint32_t));
+ MOCK_METHOD0(createClone, sp<Layer>());
MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility());
MOCK_CONST_METHOD0(getOwnerUid, uid_t());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index c311901..4f44d1b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -40,6 +40,7 @@
MOCK_METHOD(void, onFrameBegin, (TimePoint, TimePoint), (override));
MOCK_METHOD(void, onFrameMissed, (TimePoint), (override));
MOCK_METHOD(void, dump, (std::string&), (const, override));
+ MOCK_METHOD(bool, isCurrentMode, (const ftl::NonNull<DisplayModePtr>&), (const, override));
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/utils/ColorUtils.h b/services/surfaceflinger/tests/utils/ColorUtils.h
index 38c422a..07916b6 100644
--- a/services/surfaceflinger/tests/utils/ColorUtils.h
+++ b/services/surfaceflinger/tests/utils/ColorUtils.h
@@ -33,10 +33,6 @@
static const Color WHITE;
static const Color BLACK;
static const Color TRANSPARENT;
-
- half3 toHalf3() { return half3{r / 255.0f, g / 255.0f, b / 255.0f}; }
-
- half4 toHalf4() { return half4{r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f}; }
};
const Color Color::RED{255, 0, 0, 255};
@@ -85,14 +81,6 @@
}
color = ret;
}
-
- static half3 toHalf3(const Color& color) {
- return half3{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f};
- }
-
- static half4 toHalf4(const Color& color) {
- return half4{color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f};
- }
};
} // namespace
} // namespace android
diff --git a/services/vibratorservice/TEST_MAPPING b/services/vibratorservice/TEST_MAPPING
index 63a2bd0..af48673 100644
--- a/services/vibratorservice/TEST_MAPPING
+++ b/services/vibratorservice/TEST_MAPPING
@@ -1,13 +1,7 @@
{
"presubmit": [
{
- "name": "libvibratorservice_test",
- "options": [
- // TODO(b/293603710): Fix flakiness
- {
- "exclude-filter": "VibratorCallbackSchedulerTest#TestScheduleRunsOnlyAfterDelay"
- }
- ]
+ "name": "libvibratorservice_test"
}
],
"postsubmit": [
diff --git a/services/vibratorservice/VibratorCallbackScheduler.cpp b/services/vibratorservice/VibratorCallbackScheduler.cpp
index 7eda9ef..b2b1988 100644
--- a/services/vibratorservice/VibratorCallbackScheduler.cpp
+++ b/services/vibratorservice/VibratorCallbackScheduler.cpp
@@ -87,13 +87,13 @@
lock.lock();
}
if (mQueue.empty()) {
- // Wait until a new callback is scheduled.
- mCondition.wait(mMutex);
+ // Wait until a new callback is scheduled or destructor was called.
+ mCondition.wait(lock, [this] { return mFinished || !mQueue.empty(); });
} else {
- // Wait until next callback expires, or a new one is scheduled.
+ // Wait until next callback expires or a new one is scheduled.
// Use the monotonic steady clock to wait for the measured delay interval via wait_for
// instead of using a wall clock via wait_until.
- mCondition.wait_for(mMutex, mQueue.top().getWaitForExpirationDuration());
+ mCondition.wait_for(lock, mQueue.top().getWaitForExpirationDuration());
}
}
}
diff --git a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
index e11a809..9b30337 100644
--- a/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
+++ b/services/vibratorservice/benchmarks/VibratorHalControllerBenchmarks.cpp
@@ -17,7 +17,9 @@
#define LOG_TAG "VibratorHalControllerBenchmarks"
#include <benchmark/benchmark.h>
+#include <binder/ProcessState.h>
#include <vibratorservice/VibratorHalController.h>
+#include <future>
using ::android::enum_range;
using ::android::hardware::vibrator::CompositeEffect;
@@ -30,14 +32,99 @@
using ::benchmark::State;
using ::benchmark::internal::Benchmark;
+using std::chrono::milliseconds;
+
using namespace android;
using namespace std::chrono_literals;
+// Fixed number of iterations for benchmarks that trigger a vibration on the loop.
+// They require slow cleanup to ensure a stable state on each run and less noisy metrics.
+static constexpr auto VIBRATION_ITERATIONS = 500;
+
+// Timeout to wait for vibration callback completion.
+static constexpr auto VIBRATION_CALLBACK_TIMEOUT = 100ms;
+
+// Max duration the vibrator can be turned on, in milliseconds.
+static constexpr auto MAX_ON_DURATION_MS = milliseconds(UINT16_MAX);
+
+// Helper to wait for the vibrator to become idle between vibrate bench iterations.
+class HalCallback {
+public:
+ HalCallback(std::function<void()>&& waitFn, std::function<void()>&& completeFn)
+ : mWaitFn(std::move(waitFn)), mCompleteFn(std::move(completeFn)) {}
+ ~HalCallback() = default;
+
+ std::function<void()> completeFn() const { return mCompleteFn; }
+
+ void waitForComplete() const { mWaitFn(); }
+
+private:
+ std::function<void()> mWaitFn;
+ std::function<void()> mCompleteFn;
+};
+
+// Helper for vibration callbacks, kept by the Fixture until all pending callbacks are done.
+class HalCallbacks {
+public:
+ HalCallback next() {
+ std::unique_lock<std::mutex> lock(mMutex);
+ auto id = mCurrentId++;
+ mPendingPromises[id] = std::promise<void>();
+ mPendingFutures[id] = mPendingPromises[id].get_future(); // Can only be called once.
+ return HalCallback([&, id]() { waitForComplete(id); }, [&, id]() { onComplete(id); });
+ }
+
+ void onComplete(int32_t id) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ auto promise = mPendingPromises.find(id);
+ if (promise != mPendingPromises.end()) {
+ promise->second.set_value();
+ mPendingPromises.erase(promise);
+ }
+ }
+
+ void waitForComplete(int32_t id) {
+ // Wait until the HAL has finished processing previous vibration before starting a new one,
+ // so the HAL state is consistent on each run and metrics are less noisy. Some of the newest
+ // HAL implementations are waiting on previous vibration cleanup and might be significantly
+ // slower, so make sure we measure vibrations on a clean slate.
+ if (mPendingFutures[id].wait_for(VIBRATION_CALLBACK_TIMEOUT) == std::future_status::ready) {
+ mPendingFutures.erase(id);
+ }
+ }
+
+ void waitForPending() {
+ // Wait for pending callbacks from the test, possibly skipped with error.
+ for (auto& [id, future] : mPendingFutures) {
+ future.wait_for(VIBRATION_CALLBACK_TIMEOUT);
+ }
+ mPendingFutures.clear();
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mPendingPromises.clear();
+ }
+ }
+
+private:
+ std::mutex mMutex;
+ std::map<int32_t, std::promise<void>> mPendingPromises GUARDED_BY(mMutex);
+ std::map<int32_t, std::future<void>> mPendingFutures;
+ int32_t mCurrentId;
+};
+
class VibratorBench : public Fixture {
public:
- void SetUp(State& /*state*/) override { mController.init(); }
+ void SetUp(State& /*state*/) override {
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ android::ProcessState::self()->startThreadPool();
+ mController.init();
+ }
- void TearDown(State& state) override { turnVibratorOff(state); }
+ void TearDown(State& /*state*/) override {
+ turnVibratorOff();
+ disableExternalControl();
+ mCallbacks.waitForPending();
+ }
static void DefaultConfig(Benchmark* b) { b->Unit(kMicrosecond); }
@@ -47,38 +134,59 @@
protected:
vibrator::HalController mController;
+ HalCallbacks mCallbacks;
+
+ static void SlowBenchConfig(Benchmark* b) { b->Iterations(VIBRATION_ITERATIONS); }
auto getOtherArg(const State& state, std::size_t index) const { return state.range(index + 0); }
- bool hasCapabilities(vibrator::Capabilities&& query, State& state) {
+ vibrator::HalResult<void> turnVibratorOff() {
+ return mController.doWithRetry<void>([](auto hal) { return hal->off(); }, "off");
+ }
+
+ vibrator::HalResult<void> disableExternalControl() {
+ auto disableExternalControlFn = [](auto hal) { return hal->setExternalControl(false); };
+ return mController.doWithRetry<void>(disableExternalControlFn, "setExternalControl false");
+ }
+
+ bool shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities query, State& state) {
auto result = mController.getInfo().capabilities;
if (result.isFailed()) {
state.SkipWithError(result.errorMessage());
- return false;
+ return true;
}
if (!result.isOk()) {
- return false;
+ state.SkipWithMessage("capability result is unsupported");
+ return true;
}
- return (result.value() & query) == query;
- }
-
- void turnVibratorOff(State& state) {
- checkHalResult(halCall<void>(mController, [](auto hal) { return hal->off(); }), state);
+ if ((result.value() & query) != query) {
+ state.SkipWithMessage("missing capability");
+ return true;
+ }
+ return false;
}
template <class R>
- bool checkHalResult(const vibrator::HalResult<R>& result, State& state) {
+ bool shouldSkipWithError(const vibrator::HalFunction<vibrator::HalResult<R>>& halFn,
+ const char* label, State& state) {
+ return shouldSkipWithError(mController.doWithRetry<R>(halFn, label), state);
+ }
+
+ template <class R>
+ bool shouldSkipWithError(const vibrator::HalResult<R>& result, State& state) {
if (result.isFailed()) {
state.SkipWithError(result.errorMessage());
- return false;
+ return true;
}
- return true;
+ return false;
}
+};
- template <class R>
- vibrator::HalResult<R> halCall(vibrator::HalController& controller,
- const vibrator::HalFunction<vibrator::HalResult<R>>& halFn) {
- return controller.doWithRetry<R>(halFn, "benchmark");
+class SlowVibratorBench : public VibratorBench {
+public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
}
};
@@ -91,25 +199,32 @@
BENCHMARK_WRAPPER(VibratorBench, init, {
for (auto _ : state) {
+ // Setup
state.PauseTiming();
vibrator::HalController controller;
state.ResumeTiming();
+
+ // Test
controller.init();
}
});
BENCHMARK_WRAPPER(VibratorBench, initCached, {
+ // First call to cache values.
+ mController.init();
+
for (auto _ : state) {
mController.init();
}
});
BENCHMARK_WRAPPER(VibratorBench, ping, {
+ auto pingFn = [](auto hal) { return hal->ping(); };
+
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<void>(mController, [](auto hal) { return hal->ping(); });
- state.PauseTiming();
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(pingFn, "ping", state)) {
+ return;
+ }
}
});
@@ -119,164 +234,131 @@
}
});
-BENCHMARK_WRAPPER(VibratorBench, on, {
- auto duration = 60s;
- auto callback = []() {};
+BENCHMARK_WRAPPER(SlowVibratorBench, on, {
+ auto duration = MAX_ON_DURATION_MS;
for (auto _ : state) {
- state.ResumeTiming();
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
-BENCHMARK_WRAPPER(VibratorBench, off, {
- auto duration = 60s;
- auto callback = []() {};
+BENCHMARK_WRAPPER(SlowVibratorBench, off, {
+ auto duration = MAX_ON_DURATION_MS;
for (auto _ : state) {
+ // Setup
state.PauseTiming();
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
- if (!checkHalResult(ret, state)) {
- continue;
+ auto cb = mCallbacks.next();
+ auto onFn = [&](auto hal) { return hal->on(duration, cb.completeFn()); };
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
+ return;
}
+ auto offFn = [&](auto hal) { return hal->off(); };
state.ResumeTiming();
- turnVibratorOff(state);
+
+ // Test
+ if (shouldSkipWithError<void>(offFn, "off", state)) {
+ return;
+ }
+
+ // Cleanup
+ state.PauseTiming();
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
BENCHMARK_WRAPPER(VibratorBench, setAmplitude, {
- if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
return;
}
- auto duration = 60s;
- auto callback = []() {};
+ auto duration = MAX_ON_DURATION_MS;
auto amplitude = 1.0f;
+ auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
- for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- auto result =
- halCall<void>(controller, [&](auto hal) { return hal->on(duration, callback); });
- if (!checkHalResult(result, state)) {
- continue;
- }
- state.ResumeTiming();
- auto ret =
- halCall<void>(controller, [&](auto hal) { return hal->setAmplitude(amplitude); });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
- }
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, setAmplitudeCached, {
- if (!hasCapabilities(vibrator::Capabilities::AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ auto onFn = [&](auto hal) { return hal->on(duration, [&]() {}); };
+ if (shouldSkipWithError<void>(onFn, "on", state)) {
return;
}
- auto duration = 60s;
- auto callback = []() {};
- auto amplitude = 1.0f;
-
- auto onResult =
- halCall<void>(mController, [&](auto hal) { return hal->on(duration, callback); });
- checkHalResult(onResult, state);
-
for (auto _ : state) {
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(setAmplitudeFn, "setAmplitude", state)) {
+ return;
+ }
}
});
BENCHMARK_WRAPPER(VibratorBench, setExternalControl, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
return;
}
+ auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
+
for (auto _ : state) {
- state.PauseTiming();
- vibrator::HalController controller;
- controller.init();
- state.ResumeTiming();
- auto ret =
- halCall<void>(controller, [](auto hal) { return hal->setExternalControl(true); });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- auto result = halCall<void>(controller,
- [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(result, state);
+ // Test
+ if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(disableExternalControl(), state)) {
+ return;
+ }
+ state.ResumeTiming();
}
});
-BENCHMARK_WRAPPER(VibratorBench, setExternalControlCached, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
- return;
- }
-
- for (auto _ : state) {
- state.ResumeTiming();
- auto result =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
- state.PauseTiming();
- if (checkHalResult(result, state)) {
- auto ret = halCall<void>(mController,
- [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(ret, state);
- }
- }
-});
-
-BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitudeCached, {
- if (!hasCapabilities(vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+BENCHMARK_WRAPPER(VibratorBench, setExternalAmplitude, {
+ auto externalAmplitudeControl = vibrator::Capabilities::EXTERNAL_CONTROL &
+ vibrator::Capabilities::EXTERNAL_AMPLITUDE_CONTROL;
+ if (shouldSkipWithMissingCapabilityMessage(externalAmplitudeControl, state)) {
return;
}
auto amplitude = 1.0f;
+ auto setAmplitudeFn = [&](auto hal) { return hal->setAmplitude(amplitude); };
+ auto enableExternalControlFn = [](auto hal) { return hal->setExternalControl(true); };
- auto onResult =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(true); });
- checkHalResult(onResult, state);
-
- for (auto _ : state) {
- auto ret =
- halCall<void>(mController, [&](auto hal) { return hal->setAmplitude(amplitude); });
- checkHalResult(ret, state);
+ if (shouldSkipWithError<void>(enableExternalControlFn, "setExternalControl true", state)) {
+ return;
}
- auto offResult =
- halCall<void>(mController, [](auto hal) { return hal->setExternalControl(false); });
- checkHalResult(offResult, state);
+ for (auto _ : state) {
+ if (shouldSkipWithError<void>(setAmplitudeFn, "setExternalAmplitude", state)) {
+ return;
+ }
+ }
});
BENCHMARK_WRAPPER(VibratorBench, getInfo, {
for (auto _ : state) {
+ // Setup
state.PauseTiming();
vibrator::HalController controller;
controller.init();
state.ResumeTiming();
- auto result = controller.getInfo();
- checkHalResult(result.capabilities, state);
- checkHalResult(result.supportedEffects, state);
- checkHalResult(result.supportedPrimitives, state);
- checkHalResult(result.primitiveDurations, state);
- checkHalResult(result.resonantFrequency, state);
- checkHalResult(result.qFactor, state);
+
+ controller.getInfo();
}
});
@@ -285,13 +367,7 @@
mController.getInfo();
for (auto _ : state) {
- auto result = mController.getInfo();
- checkHalResult(result.capabilities, state);
- checkHalResult(result.supportedEffects, state);
- checkHalResult(result.supportedPrimitives, state);
- checkHalResult(result.primitiveDurations, state);
- checkHalResult(result.resonantFrequency, state);
- checkHalResult(result.qFactor, state);
+ mController.getInfo();
}
});
@@ -334,9 +410,16 @@
}
};
+class SlowVibratorEffectsBench : public VibratorEffectsBench {
+public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
+ }
+};
+
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnEnable, {
- if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -347,24 +430,26 @@
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
+ auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
+ auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<void>(mController, [&](auto hal) {
- return hal->alwaysOnEnable(id, effect, strength);
- });
- state.PauseTiming();
- if (checkHalResult(ret, state)) {
- auto disableResult =
- halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
- checkHalResult(disableResult, state);
+ // Test
+ if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
+ return;
+ }
+ state.ResumeTiming();
}
});
BENCHMARK_WRAPPER(VibratorEffectsBench, alwaysOnDisable, {
- if (!hasCapabilities(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
- state.SkipWithMessage("missing capability");
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::ALWAYS_ON_CONTROL, state)) {
return;
}
if (!hasArgs(state)) {
@@ -375,23 +460,25 @@
int32_t id = 1;
auto effect = getEffect(state);
auto strength = getStrength(state);
+ auto enableFn = [&](auto hal) { return hal->alwaysOnEnable(id, effect, strength); };
+ auto disableFn = [&](auto hal) { return hal->alwaysOnDisable(id); };
for (auto _ : state) {
+ // Setup
state.PauseTiming();
- auto enableResult = halCall<void>(mController, [&](auto hal) {
- return hal->alwaysOnEnable(id, effect, strength);
- });
- if (!checkHalResult(enableResult, state)) {
- continue;
+ if (shouldSkipWithError<void>(enableFn, "alwaysOnEnable", state)) {
+ return;
}
state.ResumeTiming();
- auto disableResult =
- halCall<void>(mController, [&](auto hal) { return hal->alwaysOnDisable(id); });
- checkHalResult(disableResult, state);
+
+ // Test
+ if (shouldSkipWithError<void>(disableFn, "alwaysOnDisable", state)) {
+ return;
+ }
}
});
-BENCHMARK_WRAPPER(VibratorEffectsBench, performEffect, {
+BENCHMARK_WRAPPER(SlowVibratorEffectsBench, performEffect, {
if (!hasArgs(state)) {
state.SkipWithMessage("missing args");
return;
@@ -399,22 +486,38 @@
auto effect = getEffect(state);
auto strength = getStrength(state);
- auto callback = []() {};
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
- return hal->performEffect(effect, strength, callback);
- });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto performFn = [&](auto hal) {
+ return hal->performEffect(effect, strength, cb.completeFn());
+ };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<milliseconds>(performFn, "performEffect", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
-class VibratorPrimitivesBench : public VibratorBench {
+class SlowVibratorPrimitivesBench : public VibratorBench {
public:
+ static void DefaultConfig(Benchmark* b) {
+ VibratorBench::DefaultConfig(b);
+ SlowBenchConfig(b);
+ }
+
static void DefaultArgs(Benchmark* b) {
vibrator::HalController controller;
auto primitivesResult = controller.getInfo().supportedPrimitives;
@@ -449,9 +552,8 @@
}
};
-BENCHMARK_WRAPPER(VibratorPrimitivesBench, performComposedEffect, {
- if (!hasCapabilities(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
- state.SkipWithMessage("missing capability");
+BENCHMARK_WRAPPER(SlowVibratorPrimitivesBench, performComposedEffect, {
+ if (shouldSkipWithMissingCapabilityMessage(vibrator::Capabilities::COMPOSE_EFFECTS, state)) {
return;
}
if (!hasArgs(state)) {
@@ -464,19 +566,29 @@
effect.scale = 1.0f;
effect.delayMs = static_cast<int32_t>(0);
- std::vector<CompositeEffect> effects;
- effects.push_back(effect);
- auto callback = []() {};
+ std::vector<CompositeEffect> effects = {effect};
for (auto _ : state) {
- state.ResumeTiming();
- auto ret = halCall<std::chrono::milliseconds>(mController, [&](auto hal) {
- return hal->performComposedEffect(effects, callback);
- });
+ // Setup
state.PauseTiming();
- if (checkHalResult(ret, state)) {
- turnVibratorOff(state);
+ auto cb = mCallbacks.next();
+ auto performFn = [&](auto hal) {
+ return hal->performComposedEffect(effects, cb.completeFn());
+ };
+ state.ResumeTiming();
+
+ // Test
+ if (shouldSkipWithError<milliseconds>(performFn, "performComposedEffect", state)) {
+ return;
}
+
+ // Cleanup
+ state.PauseTiming();
+ if (shouldSkipWithError(turnVibratorOff(), state)) {
+ return;
+ }
+ cb.waitForComplete();
+ state.ResumeTiming();
}
});
diff --git a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
index 426cd42..881e321 100644
--- a/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
+++ b/services/vibratorservice/test/VibratorCallbackSchedulerTest.cpp
@@ -14,20 +14,13 @@
* limitations under the License.
*/
-#define LOG_TAG "VibratorHalWrapperAidlTest"
-
-#include <android-base/thread_annotations.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <condition_variable>
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <utils/Log.h>
-#include <thread>
-
#include <vibratorservice/VibratorCallbackScheduler.h>
+#include "test_utils.h"
+
using std::chrono::milliseconds;
using std::chrono::steady_clock;
using std::chrono::time_point;
@@ -39,29 +32,25 @@
// -------------------------------------------------------------------------------------------------
// Delay allowed for the scheduler to process callbacks during this test.
-static const auto TEST_TIMEOUT = 50ms;
+static const auto TEST_TIMEOUT = 100ms;
class VibratorCallbackSchedulerTest : public Test {
public:
- void SetUp() override {
- mScheduler = std::make_unique<vibrator::CallbackScheduler>();
- std::lock_guard<std::mutex> lock(mMutex);
- mExpiredCallbacks.clear();
- }
+ void SetUp() override { mScheduler = std::make_unique<vibrator::CallbackScheduler>(); }
protected:
std::mutex mMutex;
- std::condition_variable_any mCondition;
std::unique_ptr<vibrator::CallbackScheduler> mScheduler = nullptr;
+ vibrator::TestCounter mCallbackCounter;
std::vector<int32_t> mExpiredCallbacks GUARDED_BY(mMutex);
std::function<void()> createCallback(int32_t id) {
- return [=]() {
+ return [this, id]() {
{
std::lock_guard<std::mutex> lock(mMutex);
mExpiredCallbacks.push_back(id);
}
- mCondition.notify_all();
+ mCallbackCounter.increment();
};
}
@@ -71,56 +60,42 @@
}
int32_t waitForCallbacks(int32_t callbackCount, milliseconds timeout) {
- time_point<steady_clock> expirationTime = steady_clock::now() + timeout + TEST_TIMEOUT;
- int32_t expiredCallbackCount = 0;
- while (steady_clock::now() < expirationTime) {
- std::lock_guard<std::mutex> lock(mMutex);
- expiredCallbackCount = mExpiredCallbacks.size();
- if (callbackCount <= expiredCallbackCount) {
- return expiredCallbackCount;
- }
- auto currentTimeout = std::chrono::duration_cast<std::chrono::milliseconds>(
- expirationTime - steady_clock::now());
- if (currentTimeout > currentTimeout.zero()) {
- // Use the monotonic steady clock to wait for the requested timeout via wait_for
- // instead of using a wall clock via wait_until.
- mCondition.wait_for(mMutex, currentTimeout);
- }
- }
- return expiredCallbackCount;
+ mCallbackCounter.tryWaitUntilCountIsAtLeast(callbackCount, timeout);
+ return mCallbackCounter.get();
}
};
// -------------------------------------------------------------------------------------------------
TEST_F(VibratorCallbackSchedulerTest, TestScheduleRunsOnlyAfterDelay) {
+ auto callbackDuration = 50ms;
time_point<steady_clock> startTime = steady_clock::now();
- mScheduler->schedule(createCallback(1), 50ms);
+ mScheduler->schedule(createCallback(1), callbackDuration);
- ASSERT_EQ(1, waitForCallbacks(1, 50ms));
+ ASSERT_THAT(waitForCallbacks(1, callbackDuration + TEST_TIMEOUT), Eq(1));
time_point<steady_clock> callbackTime = steady_clock::now();
- // Callback happened at least 50ms after the beginning of the test.
- ASSERT_TRUE(startTime + 50ms <= callbackTime);
- ASSERT_THAT(getExpiredCallbacks(), ElementsAre(1));
+ // Callback took at least the required duration to trigger.
+ ASSERT_THAT(callbackTime, Ge(startTime + callbackDuration));
}
TEST_F(VibratorCallbackSchedulerTest, TestScheduleMultipleCallbacksRunsInDelayOrder) {
// Schedule first callbacks long enough that all 3 will be scheduled together and run in order.
- mScheduler->schedule(createCallback(1), 50ms);
- mScheduler->schedule(createCallback(2), 40ms);
- mScheduler->schedule(createCallback(3), 10ms);
+ mScheduler->schedule(createCallback(1), 50ms + 2 * TEST_TIMEOUT);
+ mScheduler->schedule(createCallback(2), 50ms + TEST_TIMEOUT);
+ mScheduler->schedule(createCallback(3), 50ms);
- ASSERT_EQ(3, waitForCallbacks(3, 50ms));
+ // Callbacks triggered in the expected order based on the requested durations.
+ ASSERT_THAT(waitForCallbacks(3, 50ms + 3 * TEST_TIMEOUT), Eq(3));
ASSERT_THAT(getExpiredCallbacks(), ElementsAre(3, 2, 1));
}
TEST_F(VibratorCallbackSchedulerTest, TestDestructorDropsPendingCallbacksAndKillsThread) {
// Schedule callback long enough that scheduler will be destroyed while it's still scheduled.
- mScheduler->schedule(createCallback(1), 50ms);
+ mScheduler->schedule(createCallback(1), 100ms);
mScheduler.reset(nullptr);
// Should timeout waiting for callback to run.
- ASSERT_EQ(0, waitForCallbacks(1, 50ms));
- ASSERT_TRUE(getExpiredCallbacks().empty());
+ ASSERT_THAT(waitForCallbacks(1, 100ms + TEST_TIMEOUT), Eq(0));
+ ASSERT_THAT(getExpiredCallbacks(), IsEmpty());
}
diff --git a/services/vibratorservice/test/VibratorHalControllerTest.cpp b/services/vibratorservice/test/VibratorHalControllerTest.cpp
index 9b95d74..15fde91 100644
--- a/services/vibratorservice/test/VibratorHalControllerTest.cpp
+++ b/services/vibratorservice/test/VibratorHalControllerTest.cpp
@@ -255,16 +255,17 @@
.WillRepeatedly(Return(vibrator::HalResult<void>::transactionFailed("message")));
}
- std::unique_ptr<int32_t> callbackCounter = std::make_unique<int32_t>();
- auto callback = vibrator::TestFactory::createCountingCallback(callbackCounter.get());
+ auto counter = vibrator::TestCounter(0);
- auto onFn = [&](vibrator::HalWrapper* hal) { return hal->on(10ms, callback); };
+ auto onFn = [&](vibrator::HalWrapper* hal) {
+ return hal->on(10ms, [&counter] { counter.increment(); });
+ };
ASSERT_TRUE(mController->doWithRetry<void>(onFn, "on").isOk());
ASSERT_TRUE(mController->doWithRetry<void>(PING_FN, "ping").isFailed());
mMockHal.reset();
- ASSERT_EQ(0, *callbackCounter.get());
+ ASSERT_EQ(0, counter.get());
// Callback triggered even after HalWrapper was reconnected.
- std::this_thread::sleep_for(15ms);
- ASSERT_EQ(1, *callbackCounter.get());
+ counter.tryWaitUntilCountIsAtLeast(1, 500ms);
+ ASSERT_EQ(1, counter.get());
}
diff --git a/services/vibratorservice/test/test_utils.h b/services/vibratorservice/test/test_utils.h
index 1933a11..715c221 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -85,10 +85,38 @@
~TestFactory() = delete;
};
+class TestCounter {
+public:
+ TestCounter(int32_t init = 0) : mMutex(), mCondVar(), mCount(init) {}
+
+ int32_t get() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mCount;
+ }
+
+ void increment() {
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mCount += 1;
+ }
+ mCondVar.notify_all();
+ }
+
+ void tryWaitUntilCountIsAtLeast(int32_t count, std::chrono::milliseconds timeout) {
+ std::unique_lock<std::mutex> lock(mMutex);
+ mCondVar.wait_for(lock, timeout, [&] { return mCount >= count; });
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mCondVar;
+ int32_t mCount GUARDED_BY(mMutex);
+};
+
// -------------------------------------------------------------------------------------------------
} // namespace vibrator
} // namespace android
-#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
\ No newline at end of file
+#endif // VIBRATORSERVICE_UNITTEST_UTIL_H_
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 81fd118..7ea98f5 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -1372,6 +1372,11 @@
android::GraphicsEnv::getInstance().setTargetStats(
android::GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION,
vulkanApiVersion);
+
+ if (pCreateInfo->pApplicationInfo->pEngineName) {
+ android::GraphicsEnv::getInstance().addVulkanEngineName(
+ pCreateInfo->pApplicationInfo->pEngineName);
+ }
}
// Update stats for the extensions requested