Merge "Revert^2 "Skip C-verification of GL headers."" into main am: aa61e92889
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/3218118
Change-Id: I91626c83e0c0ac0e8dc746ef02494c126adaf53c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba0a38a..a8d12a1 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -39,6 +39,7 @@
std::string calling_package;
int32_t user_id = -1;
bool keep_bugreport_on_retrieval = false;
+ bool skip_user_consent = false;
};
static binder::Status exception(uint32_t code, const std::string& msg,
@@ -62,7 +63,8 @@
[[noreturn]] static void* dumpstate_thread_retrieve(void* data) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
- ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package, ds_info->keep_bugreport_on_retrieval);
+ ds_info->ds->Retrieve(ds_info->calling_uid, ds_info->calling_package,
+ ds_info->keep_bugreport_on_retrieval, ds_info->skip_user_consent);
MYLOGD("Finished retrieving a bugreport. Exiting.\n");
exit(0);
}
@@ -116,7 +118,8 @@
int bugreport_mode,
int bugreport_flags,
const sp<IDumpstateListener>& listener,
- bool is_screenshot_requested) {
+ bool is_screenshot_requested,
+ bool skip_user_consent) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
// Ensure there is only one bugreport in progress at a time.
@@ -151,7 +154,7 @@
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_flags,
- bugreport_fd, screenshot_fd, is_screenshot_requested);
+ bugreport_fd, screenshot_fd, is_screenshot_requested, skip_user_consent);
if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
MYLOGE("Invalid filedescriptor");
@@ -207,6 +210,7 @@
android::base::unique_fd bugreport_fd,
const std::string& bugreport_file,
const bool keep_bugreport_on_retrieval,
+ const bool skip_user_consent,
const sp<IDumpstateListener>& listener) {
ds_ = &(Dumpstate::GetInstance());
@@ -216,6 +220,7 @@
ds_info->calling_package = calling_package;
ds_info->user_id = user_id;
ds_info->keep_bugreport_on_retrieval = keep_bugreport_on_retrieval;
+ ds_info->skip_user_consent = skip_user_consent;
ds_->listener_ = listener;
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
// Use a /dev/null FD when initializing options since none is provided.
@@ -223,7 +228,7 @@
TEMP_FAILURE_RETRY(open("/dev/null", O_WRONLY | O_CLOEXEC)));
options->Initialize(Dumpstate::BugreportMode::BUGREPORT_DEFAULT,
- 0, bugreport_fd, devnull_fd, false);
+ 0, bugreport_fd, devnull_fd, false, skip_user_consent);
if (bugreport_fd.get() == -1) {
MYLOGE("Invalid filedescriptor");
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 7b76c36..c99f70e 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -44,7 +44,7 @@
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd, int bugreport_mode,
int bugreport_flags, const sp<IDumpstateListener>& listener,
- bool is_screenshot_requested) override;
+ bool is_screenshot_requested, bool skip_user_consent) override;
binder::Status retrieveBugreport(int32_t calling_uid,
const std::string& calling_package,
@@ -52,6 +52,7 @@
android::base::unique_fd bugreport_fd,
const std::string& bugreport_file,
const bool keep_bugreport_on_retrieval,
+ const bool skip_user_consent,
const sp<IDumpstateListener>& listener)
override;
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 97c470e..3b8fde9 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -96,7 +96,8 @@
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
int bugreportMode, int bugreportFlags,
- IDumpstateListener listener, boolean isScreenshotRequested);
+ IDumpstateListener listener, boolean isScreenshotRequested,
+ boolean skipUserConsent);
/**
* Cancels the bugreport currently in progress.
@@ -130,5 +131,6 @@
FileDescriptor bugreportFd,
@utf8InCpp String bugreportFile,
boolean keepBugreportOnRetrieval,
+ boolean skipUserConsent,
IDumpstateListener listener);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 1e5dd97..6576ffd 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -1570,6 +1570,7 @@
printf("== ANR Traces\n");
printf("========================================================\n");
+ ds.anr_data_ = GetDumpFds(ANR_DIR, ANR_FILE_PREFIX);
AddAnrTraceFiles();
printf("========================================================\n");
@@ -2979,9 +2980,11 @@
int bugreport_flags,
const android::base::unique_fd& bugreport_fd_in,
const android::base::unique_fd& screenshot_fd_in,
- bool is_screenshot_requested) {
+ bool is_screenshot_requested,
+ bool skip_user_consent) {
this->use_predumped_ui_data = bugreport_flags & BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA;
this->is_consent_deferred = bugreport_flags & BugreportFlag::BUGREPORT_FLAG_DEFER_CONSENT;
+ this->skip_user_consent = skip_user_consent;
// Duplicate the fds because the passed in fds don't outlive the binder transaction.
bugreport_fd.reset(fcntl(bugreport_fd_in.get(), F_DUPFD_CLOEXEC, 0));
screenshot_fd.reset(fcntl(screenshot_fd_in.get(), F_DUPFD_CLOEXEC, 0));
@@ -3069,46 +3072,52 @@
}
Dumpstate::RunStatus Dumpstate::Retrieve(int32_t calling_uid, const std::string& calling_package,
- const bool keep_bugreport_on_retrieval) {
+ const bool keep_bugreport_on_retrieval,
+ const bool skip_user_consent) {
Dumpstate::RunStatus status = RetrieveInternal(calling_uid, calling_package,
- keep_bugreport_on_retrieval);
+ keep_bugreport_on_retrieval,
+ skip_user_consent);
HandleRunStatus(status);
return status;
}
Dumpstate::RunStatus Dumpstate::RetrieveInternal(int32_t calling_uid,
const std::string& calling_package,
- const bool keep_bugreport_on_retrieval) {
- consent_callback_ = new ConsentCallback();
- const String16 incidentcompanion("incidentcompanion");
- sp<android::IBinder> ics(
- defaultServiceManager()->checkService(incidentcompanion));
- android::String16 package(calling_package.c_str());
- if (ics != nullptr) {
- MYLOGD("Checking user consent via incidentcompanion service\n");
- android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, package, String16(), String16(),
- 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
- } else {
- MYLOGD(
- "Unable to check user consent; incidentcompanion service unavailable\n");
- return RunStatus::USER_CONSENT_TIMED_OUT;
- }
- UserConsentResult consent_result = consent_callback_->getResult();
- int timeout_ms = 30 * 1000;
- while (consent_result == UserConsentResult::UNAVAILABLE &&
- consent_callback_->getElapsedTimeMs() < timeout_ms) {
- sleep(1);
- consent_result = consent_callback_->getResult();
- }
- if (consent_result == UserConsentResult::DENIED) {
- return RunStatus::USER_CONSENT_DENIED;
- }
- if (consent_result == UserConsentResult::UNAVAILABLE) {
- MYLOGD("Canceling user consent request via incidentcompanion service\n");
- android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization(
- consent_callback_.get());
- return RunStatus::USER_CONSENT_TIMED_OUT;
+ const bool keep_bugreport_on_retrieval,
+ const bool skip_user_consent) {
+ if (!android::app::admin::flags::onboarding_consentless_bugreports() || !skip_user_consent) {
+ consent_callback_ = new ConsentCallback();
+ const String16 incidentcompanion("incidentcompanion");
+ sp<android::IBinder> ics(
+ defaultServiceManager()->checkService(incidentcompanion));
+ android::String16 package(calling_package.c_str());
+ if (ics != nullptr) {
+ MYLOGD("Checking user consent via incidentcompanion service\n");
+
+ android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
+ calling_uid, package, String16(), String16(),
+ 0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
+ } else {
+ MYLOGD(
+ "Unable to check user consent; incidentcompanion service unavailable\n");
+ return RunStatus::USER_CONSENT_TIMED_OUT;
+ }
+ UserConsentResult consent_result = consent_callback_->getResult();
+ int timeout_ms = 30 * 1000;
+ while (consent_result == UserConsentResult::UNAVAILABLE &&
+ consent_callback_->getElapsedTimeMs() < timeout_ms) {
+ sleep(1);
+ consent_result = consent_callback_->getResult();
+ }
+ if (consent_result == UserConsentResult::DENIED) {
+ return RunStatus::USER_CONSENT_DENIED;
+ }
+ if (consent_result == UserConsentResult::UNAVAILABLE) {
+ MYLOGD("Canceling user consent request via incidentcompanion service\n");
+ android::interface_cast<android::os::IIncidentCompanion>(ics)->cancelAuthorization(
+ consent_callback_.get());
+ return RunStatus::USER_CONSENT_TIMED_OUT;
+ }
}
bool copy_succeeded =
@@ -3357,6 +3366,12 @@
// duration is logged into MYLOG instead.
PrintHeader();
+ bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0;
+ if (options_->use_predumped_ui_data && !system_trace_exists) {
+ MYLOGW("Ignoring 'use predumped data' flag because no predumped data is available");
+ options_->use_predumped_ui_data = false;
+ }
+
std::future<std::string> snapshot_system_trace;
bool is_dumpstate_restricted =
@@ -3581,7 +3596,9 @@
void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
if (multiuser_get_app_id(calling_uid) == AID_SHELL ||
- !CalledByApi() || options_->is_consent_deferred) {
+ !CalledByApi() || options_->is_consent_deferred ||
+ (android::app::admin::flags::onboarding_consentless_bugreports() &&
+ options_->skip_user_consent)) {
// No need to get consent for shell triggered dumpstates, or not
// through bugreporting API (i.e. no fd to copy back), or when consent
// is deferred.
@@ -3667,7 +3684,8 @@
// If the caller has asked to copy the bugreport over to their directory, we need explicit
// user consent (unless the caller is Shell).
UserConsentResult consent_result;
- if (multiuser_get_app_id(calling_uid) == AID_SHELL) {
+ if (multiuser_get_app_id(calling_uid) == AID_SHELL || (options_->skip_user_consent
+ && android::app::admin::flags::onboarding_consentless_bugreports())) {
consent_result = UserConsentResult::APPROVED;
} else {
consent_result = consent_callback_->getResult();
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 46d949e..fcb8cf3 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -364,7 +364,7 @@
* Initialize() dumpstate before calling this method.
*/
RunStatus Retrieve(int32_t calling_uid, const std::string& calling_package,
- const bool keep_bugreport_on_retrieval);
+ const bool keep_bugreport_on_retrieval, const bool skip_user_consent);
@@ -412,6 +412,7 @@
bool do_screenshot = false;
bool is_screenshot_copied = false;
bool is_consent_deferred = false;
+ bool skip_user_consent = false;
bool is_remote_mode = false;
bool show_header_only = false;
bool telephony_only = false;
@@ -448,7 +449,8 @@
void Initialize(BugreportMode bugreport_mode, int bugreport_flags,
const android::base::unique_fd& bugreport_fd,
const android::base::unique_fd& screenshot_fd,
- bool is_screenshot_requested);
+ bool is_screenshot_requested,
+ bool skip_user_consent);
/* Returns true if the options set so far are consistent. */
bool ValidateOptions() const;
@@ -564,7 +566,8 @@
private:
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
RunStatus RetrieveInternal(int32_t calling_uid, const std::string& calling_package,
- const bool keep_bugreport_on_retrieval);
+ const bool keep_bugreport_on_retrieval,
+ const bool skip_user_consent);
RunStatus DumpstateDefaultAfterCritical();
RunStatus dumpstate();
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index ccf64fe..a29923a 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -507,7 +507,7 @@
ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
- true);
+ true, false);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
EXPECT_TRUE(status.isOk());
@@ -545,7 +545,7 @@
android::binder::Status status =
ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
std::move(screenshot_fd), 2000, // invalid bugreport mode
- flags, listener, false);
+ flags, listener, false, false);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
// The service should have died, freeing itself up for a new invocation.
@@ -579,7 +579,7 @@
ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
- true);
+ true, false);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
@@ -587,7 +587,7 @@
status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
std::move(screenshot_fd2),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
- listener2, true);
+ listener2, true, false);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
EXPECT_EQ(listener2->getErrorCode(),
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 2afabed..18c2f94 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -239,7 +239,7 @@
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true, false);
EXPECT_TRUE(options_.do_screenshot);
// Other options retain default values
@@ -253,7 +253,7 @@
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, 0, fd, fd, true, false);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_screenshot);
@@ -267,7 +267,7 @@
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, 0, fd, fd, false, false);
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
@@ -281,7 +281,7 @@
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, 0, fd, fd, true, false);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_progress_updates);
@@ -296,7 +296,7 @@
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, 0, fd, fd, false, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
@@ -311,7 +311,7 @@
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, 0, fd, fd, false, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.wifi_only);
@@ -491,12 +491,12 @@
int flags = Dumpstate::BugreportFlag::BUGREPORT_USE_PREDUMPED_UI_DATA |
Dumpstate::BugreportFlag::BUGREPORT_FLAG_DEFER_CONSENT;
options_.Initialize(
- Dumpstate::BugreportMode::BUGREPORT_FULL, flags, fd, fd, true);
+ Dumpstate::BugreportMode::BUGREPORT_FULL, flags, fd, fd, true, false);
EXPECT_TRUE(options_.is_consent_deferred);
EXPECT_TRUE(options_.use_predumped_ui_data);
options_.Initialize(
- Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true);
+ Dumpstate::BugreportMode::BUGREPORT_FULL, 0, fd, fd, true, false);
EXPECT_FALSE(options_.is_consent_deferred);
EXPECT_FALSE(options_.use_predumped_ui_data);
}
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/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
deleted file mode 100644
index 44235cc..0000000
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ /dev/null
@@ -1,707 +0,0 @@
-/* Copyright 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "SurfaceReplayer"
-
-#include "Replayer.h"
-
-#include <android/native_window.h>
-
-#include <android-base/file.h>
-
-#include <gui/BufferQueue.h>
-#include <gui/ISurfaceComposer.h>
-#include <gui/LayerState.h>
-#include <gui/Surface.h>
-#include <private/gui/ComposerService.h>
-
-#include <utils/Log.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-
-#include <chrono>
-#include <cmath>
-#include <condition_variable>
-#include <cstdlib>
-#include <fstream>
-#include <functional>
-#include <iostream>
-#include <mutex>
-#include <sstream>
-#include <string>
-#include <thread>
-#include <vector>
-
-using namespace android;
-
-std::atomic_bool Replayer::sReplayingManually(false);
-
-Replayer::Replayer(const std::string& filename, bool replayManually, int numThreads, bool wait,
- nsecs_t stopHere)
- : mTrace(),
- mLoaded(false),
- mIncrementIndex(0),
- mCurrentTime(0),
- mNumThreads(numThreads),
- mWaitForTimeStamps(wait),
- mStopTimeStamp(stopHere) {
- srand(RAND_COLOR_SEED);
-
- std::string input;
- if (!android::base::ReadFileToString(filename, &input, true)) {
- std::cerr << "Trace did not load. Does " << filename << " exist?" << std::endl;
- abort();
- }
-
- mLoaded = mTrace.ParseFromString(input);
- if (!mLoaded) {
- std::cerr << "Trace did not load." << std::endl;
- abort();
- }
-
- mCurrentTime = mTrace.increment(0).time_stamp();
-
- sReplayingManually.store(replayManually);
-
- if (stopHere < 0) {
- mHasStopped = true;
- }
-}
-
-Replayer::Replayer(const Trace& t, bool replayManually, int numThreads, bool wait, nsecs_t stopHere)
- : mTrace(t),
- mLoaded(true),
- mIncrementIndex(0),
- mCurrentTime(0),
- mNumThreads(numThreads),
- mWaitForTimeStamps(wait),
- mStopTimeStamp(stopHere) {
- srand(RAND_COLOR_SEED);
- mCurrentTime = mTrace.increment(0).time_stamp();
-
- sReplayingManually.store(replayManually);
-
- if (stopHere < 0) {
- mHasStopped = true;
- }
-}
-
-status_t Replayer::replay() {
- signal(SIGINT, Replayer::stopAutoReplayHandler); //for manual control
-
- ALOGV("There are %d increments.", mTrace.increment_size());
-
- status_t status = loadSurfaceComposerClient();
-
- if (status != NO_ERROR) {
- ALOGE("Couldn't create SurfaceComposerClient (%d)", status);
- return status;
- }
-
- SurfaceComposerClient::enableVSyncInjections(true);
-
- initReplay();
-
- ALOGV("Starting actual Replay!");
- while (!mPendingIncrements.empty()) {
- mCurrentIncrement = mTrace.increment(mIncrementIndex);
-
- if (mHasStopped == false && mCurrentIncrement.time_stamp() >= mStopTimeStamp) {
- mHasStopped = true;
- sReplayingManually.store(true);
- }
-
- waitForConsoleCommmand();
-
- if (mWaitForTimeStamps) {
- waitUntilTimestamp(mCurrentIncrement.time_stamp());
- }
-
- auto event = mPendingIncrements.front();
- mPendingIncrements.pop();
-
- event->complete();
-
- if (event->getIncrementType() == Increment::kVsyncEvent) {
- mWaitingForNextVSync = false;
- }
-
- if (mIncrementIndex + mNumThreads < mTrace.increment_size()) {
- status = dispatchEvent(mIncrementIndex + mNumThreads);
-
- if (status != NO_ERROR) {
- SurfaceComposerClient::enableVSyncInjections(false);
- return status;
- }
- }
-
- mIncrementIndex++;
- mCurrentTime = mCurrentIncrement.time_stamp();
- }
-
- SurfaceComposerClient::enableVSyncInjections(false);
-
- return status;
-}
-
-status_t Replayer::initReplay() {
- for (int i = 0; i < mNumThreads && i < mTrace.increment_size(); i++) {
- status_t status = dispatchEvent(i);
-
- if (status != NO_ERROR) {
- ALOGE("Unable to dispatch event (%d)", status);
- return status;
- }
- }
-
- return NO_ERROR;
-}
-
-void Replayer::stopAutoReplayHandler(int /*signal*/) {
- if (sReplayingManually) {
- SurfaceComposerClient::enableVSyncInjections(false);
- exit(0);
- }
-
- sReplayingManually.store(true);
-}
-
-std::vector<std::string> split(const std::string& s, const char delim) {
- std::vector<std::string> elems;
- std::stringstream ss(s);
- std::string item;
- while (getline(ss, item, delim)) {
- elems.push_back(item);
- }
- return elems;
-}
-
-bool isNumber(const std::string& s) {
- return !s.empty() &&
- std::find_if(s.begin(), s.end(), [](char c) { return !std::isdigit(c); }) == s.end();
-}
-
-void Replayer::waitForConsoleCommmand() {
- if (!sReplayingManually || mWaitingForNextVSync) {
- return;
- }
-
- while (true) {
- std::string input = "";
- std::cout << "> ";
- getline(std::cin, input);
-
- if (input.empty()) {
- input = mLastInput;
- } else {
- mLastInput = input;
- }
-
- if (mLastInput.empty()) {
- continue;
- }
-
- std::vector<std::string> inputs = split(input, ' ');
-
- if (inputs[0] == "n") { // next vsync
- mWaitingForNextVSync = true;
- break;
-
- } else if (inputs[0] == "ni") { // next increment
- break;
-
- } else if (inputs[0] == "c") { // continue
- if (inputs.size() > 1 && isNumber(inputs[1])) {
- long milliseconds = stoi(inputs[1]);
- std::thread([&] {
- std::cout << "Started!" << std::endl;
- std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
- sReplayingManually.store(true);
- std::cout << "Should have stopped!" << std::endl;
- }).detach();
- }
- sReplayingManually.store(false);
- mWaitingForNextVSync = false;
- break;
-
- } else if (inputs[0] == "s") { // stop at this timestamp
- if (inputs.size() < 1) {
- std::cout << "No time stamp given" << std::endl;
- continue;
- }
- sReplayingManually.store(false);
- mStopTimeStamp = stol(inputs[1]);
- mHasStopped = false;
- break;
- } else if (inputs[0] == "l") { // list
- std::cout << "Time stamp: " << mCurrentIncrement.time_stamp() << "\n";
- continue;
- } else if (inputs[0] == "q") { // quit
- SurfaceComposerClient::enableVSyncInjections(false);
- exit(0);
-
- } else if (inputs[0] == "h") { // help
- // add help menu
- std::cout << "Manual Replay options:\n";
- std::cout << " n - Go to next VSync\n";
- std::cout << " ni - Go to next increment\n";
- std::cout << " c - Continue\n";
- std::cout << " c [milliseconds] - Continue until specified number of milliseconds\n";
- std::cout << " s [timestamp] - Continue and stop at specified timestamp\n";
- std::cout << " l - List out timestamp of current increment\n";
- std::cout << " h - Display help menu\n";
- std::cout << std::endl;
- continue;
- }
-
- std::cout << "Invalid Command" << std::endl;
- }
-}
-
-status_t Replayer::dispatchEvent(int index) {
- auto increment = mTrace.increment(index);
- std::shared_ptr<Event> event = std::make_shared<Event>(increment.increment_case());
- mPendingIncrements.push(event);
-
- status_t status = NO_ERROR;
- switch (increment.increment_case()) {
- case increment.kTransaction: {
- std::thread(&Replayer::doTransaction, this, increment.transaction(), event).detach();
- } break;
- case increment.kSurfaceCreation: {
- std::thread(&Replayer::createSurfaceControl, this, increment.surface_creation(), event)
- .detach();
- } break;
- case increment.kBufferUpdate: {
- std::lock_guard<std::mutex> lock1(mLayerLock);
- std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
-
- Dimensions dimensions(increment.buffer_update().w(), increment.buffer_update().h());
- BufferEvent bufferEvent(event, dimensions);
-
- auto layerId = increment.buffer_update().id();
- if (mBufferQueueSchedulers.count(layerId) == 0) {
- mBufferQueueSchedulers[layerId] = std::make_shared<BufferQueueScheduler>(
- mLayers[layerId], mColors[layerId], layerId);
- mBufferQueueSchedulers[layerId]->addEvent(bufferEvent);
-
- std::thread(&BufferQueueScheduler::startScheduling,
- mBufferQueueSchedulers[increment.buffer_update().id()].get())
- .detach();
- } else {
- auto bqs = mBufferQueueSchedulers[increment.buffer_update().id()];
- bqs->addEvent(bufferEvent);
- }
- } break;
- case increment.kVsyncEvent: {
- std::thread(&Replayer::injectVSyncEvent, this, increment.vsync_event(), event).detach();
- } break;
- case increment.kDisplayCreation: {
- std::thread(&Replayer::createDisplay, this, increment.display_creation(), event)
- .detach();
- } break;
- case increment.kDisplayDeletion: {
- std::thread(&Replayer::deleteDisplay, this, increment.display_deletion(), event)
- .detach();
- } break;
- case increment.kPowerModeUpdate: {
- std::thread(&Replayer::updatePowerMode, this, increment.power_mode_update(), event)
- .detach();
- } break;
- default:
- ALOGE("Unknown Increment Type: %d", increment.increment_case());
- status = BAD_VALUE;
- break;
- }
-
- return status;
-}
-
-status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) {
- ALOGV("Started Transaction");
-
- SurfaceComposerClient::Transaction liveTransaction;
-
- status_t status = NO_ERROR;
-
- status = doSurfaceTransaction(liveTransaction, t.surface_change());
- doDisplayTransaction(liveTransaction, t.display_change());
-
- if (t.animation()) {
- liveTransaction.setAnimationTransaction();
- }
-
- event->readyToExecute();
-
- liveTransaction.apply(t.synchronous());
-
- ALOGV("Ended Transaction");
-
- return status;
-}
-
-status_t Replayer::doSurfaceTransaction(
- SurfaceComposerClient::Transaction& transaction,
- const SurfaceChanges& surfaceChanges) {
- status_t status = NO_ERROR;
-
- for (const SurfaceChange& change : surfaceChanges) {
- std::unique_lock<std::mutex> lock(mLayerLock);
- if (mLayers[change.id()] == nullptr) {
- mLayerCond.wait(lock, [&] { return (mLayers[change.id()] != nullptr); });
- }
-
- switch (change.SurfaceChange_case()) {
- case SurfaceChange::SurfaceChangeCase::kPosition:
- setPosition(transaction, change.id(), change.position());
- break;
- case SurfaceChange::SurfaceChangeCase::kSize:
- setSize(transaction, change.id(), change.size());
- break;
- case SurfaceChange::SurfaceChangeCase::kAlpha:
- setAlpha(transaction, change.id(), change.alpha());
- break;
- case SurfaceChange::SurfaceChangeCase::kLayer:
- setLayer(transaction, change.id(), change.layer());
- break;
- case SurfaceChange::SurfaceChangeCase::kCrop:
- setCrop(transaction, change.id(), change.crop());
- break;
- case SurfaceChange::SurfaceChangeCase::kCornerRadius:
- setCornerRadius(transaction, change.id(), change.corner_radius());
- break;
- case SurfaceChange::SurfaceChangeCase::kMatrix:
- setMatrix(transaction, change.id(), change.matrix());
- break;
- case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint:
- setTransparentRegionHint(transaction, change.id(),
- change.transparent_region_hint());
- break;
- case SurfaceChange::SurfaceChangeCase::kLayerStack:
- setLayerStack(transaction, change.id(), change.layer_stack());
- break;
- case SurfaceChange::SurfaceChangeCase::kHiddenFlag:
- setHiddenFlag(transaction, change.id(), change.hidden_flag());
- break;
- case SurfaceChange::SurfaceChangeCase::kOpaqueFlag:
- setOpaqueFlag(transaction, change.id(), change.opaque_flag());
- break;
- case SurfaceChange::SurfaceChangeCase::kSecureFlag:
- setSecureFlag(transaction, change.id(), change.secure_flag());
- break;
- case SurfaceChange::SurfaceChangeCase::kReparent:
- setReparentChange(transaction, change.id(), change.reparent());
- break;
- case SurfaceChange::SurfaceChangeCase::kRelativeParent:
- setRelativeParentChange(transaction, change.id(), change.relative_parent());
- break;
- case SurfaceChange::SurfaceChangeCase::kShadowRadius:
- setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
- break;
- case SurfaceChange::SurfaceChangeCase::kBlurRegions:
- setBlurRegionsChange(transaction, change.id(), change.blur_regions());
- break;
- default:
- status = 1;
- break;
- }
-
- if (status != NO_ERROR) {
- ALOGE("Unknown Transaction Code");
- return status;
- }
- }
- return status;
-}
-
-void Replayer::doDisplayTransaction(SurfaceComposerClient::Transaction& t,
- const DisplayChanges& displayChanges) {
- for (const DisplayChange& change : displayChanges) {
- ALOGV("Doing display transaction");
- std::unique_lock<std::mutex> lock(mDisplayLock);
- if (mDisplays[change.id()] == nullptr) {
- mDisplayCond.wait(lock, [&] { return (mDisplays[change.id()] != nullptr); });
- }
-
- switch (change.DisplayChange_case()) {
- case DisplayChange::DisplayChangeCase::kSurface:
- setDisplaySurface(t, change.id(), change.surface());
- break;
- case DisplayChange::DisplayChangeCase::kLayerStack:
- setDisplayLayerStack(t, change.id(), change.layer_stack());
- break;
- case DisplayChange::DisplayChangeCase::kSize:
- setDisplaySize(t, change.id(), change.size());
- break;
- case DisplayChange::DisplayChangeCase::kProjection:
- setDisplayProjection(t, change.id(), change.projection());
- break;
- default:
- break;
- }
- }
-}
-
-void Replayer::setPosition(SurfaceComposerClient::Transaction& t,
- layer_id id, const PositionChange& pc) {
- ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y());
- t.setPosition(mLayers[id], pc.x(), pc.y());
-}
-
-void Replayer::setSize(SurfaceComposerClient::Transaction& t,
- layer_id id, const SizeChange& sc) {
- ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h());
-}
-
-void Replayer::setLayer(SurfaceComposerClient::Transaction& t,
- layer_id id, const LayerChange& lc) {
- ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer());
- t.setLayer(mLayers[id], lc.layer());
-}
-
-void Replayer::setAlpha(SurfaceComposerClient::Transaction& t,
- layer_id id, const AlphaChange& ac) {
- ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha());
- t.setAlpha(mLayers[id], ac.alpha());
-}
-
-void Replayer::setCrop(SurfaceComposerClient::Transaction& t,
- layer_id id, const CropChange& cc) {
- ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id,
- cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
- cc.rectangle().bottom());
-
- Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(),
- cc.rectangle().bottom());
- t.setCrop(mLayers[id], r);
-}
-
-void Replayer::setCornerRadius(SurfaceComposerClient::Transaction& t,
- layer_id id, const CornerRadiusChange& cc) {
- ALOGV("Layer %d: Setting Corner Radius -- cornerRadius=%d", id, cc.corner_radius());
-
- t.setCornerRadius(mLayers[id], cc.corner_radius());
-}
-
-void Replayer::setBackgroundBlurRadius(SurfaceComposerClient::Transaction& t,
- layer_id id, const BackgroundBlurRadiusChange& cc) {
- ALOGV("Layer %d: Setting Background Blur Radius -- backgroundBlurRadius=%d", id,
- cc.background_blur_radius());
-
- t.setBackgroundBlurRadius(mLayers[id], cc.background_blur_radius());
-}
-
-void Replayer::setMatrix(SurfaceComposerClient::Transaction& t,
- layer_id id, const MatrixChange& mc) {
- ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(),
- mc.dtdx(), mc.dsdy(), mc.dtdy());
- t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy());
-}
-
-void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t,
- layer_id id, const TransparentRegionHintChange& trhc) {
- ALOGV("Setting Transparent Region Hint");
- Region re = Region();
-
- for (const auto& r : trhc.region()) {
- Rect rect = Rect(r.left(), r.top(), r.right(), r.bottom());
- re.merge(rect);
- }
-
- t.setTransparentRegionHint(mLayers[id], re);
-}
-
-void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t,
- layer_id id, const LayerStackChange& lsc) {
- ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack());
- t.setLayerStack(mLayers[id], ui::LayerStack::fromValue(lsc.layer_stack()));
-}
-
-void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const HiddenFlagChange& hfc) {
- ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag());
- layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0;
-
- t.setFlags(mLayers[id], flag, layer_state_t::eLayerHidden);
-}
-
-void Replayer::setOpaqueFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const OpaqueFlagChange& ofc) {
- ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag());
- layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0;
-
- t.setFlags(mLayers[id], flag, layer_state_t::eLayerOpaque);
-}
-
-void Replayer::setSecureFlag(SurfaceComposerClient::Transaction& t,
- layer_id id, const SecureFlagChange& sfc) {
- ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag());
- layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0;
-
- t.setFlags(mLayers[id], flag, layer_state_t::eLayerSecure);
-}
-
-void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t,
- display_id id, const DispSurfaceChange& /*dsc*/) {
- sp<IGraphicBufferProducer> outProducer;
- sp<IGraphicBufferConsumer> outConsumer;
- BufferQueue::createBufferQueue(&outProducer, &outConsumer);
-
- t.setDisplaySurface(mDisplays[id], outProducer);
-}
-
-void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t,
- display_id id, const LayerStackChange& lsc) {
- t.setDisplayLayerStack(mDisplays[id], ui::LayerStack::fromValue(lsc.layer_stack()));
-}
-
-void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t,
- display_id id, const SizeChange& sc) {
- t.setDisplaySize(mDisplays[id], sc.w(), sc.h());
-}
-
-void Replayer::setDisplayProjection(SurfaceComposerClient::Transaction& t,
- display_id id, const ProjectionChange& pc) {
- Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(),
- pc.viewport().bottom());
- Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom());
-
- t.setDisplayProjection(mDisplays[id], ui::toRotation(pc.orientation()), viewport, frame);
-}
-
-status_t Replayer::createSurfaceControl(
- const SurfaceCreation& create, const std::shared_ptr<Event>& event) {
- event->readyToExecute();
-
- ALOGV("Creating Surface Control: ID: %d", create.id());
- sp<SurfaceControl> surfaceControl = mComposerClient->createSurface(
- String8(create.name().c_str()), create.w(), create.h(), PIXEL_FORMAT_RGBA_8888, 0);
-
- if (surfaceControl == nullptr) {
- ALOGE("CreateSurfaceControl: unable to create surface control");
- return BAD_VALUE;
- }
-
- std::lock_guard<std::mutex> lock1(mLayerLock);
- auto& layer = mLayers[create.id()];
- layer = surfaceControl;
-
- mColors[create.id()] = HSV(rand() % 360, 1, 1);
-
- mLayerCond.notify_all();
-
- std::lock_guard<std::mutex> lock2(mBufferQueueSchedulerLock);
- if (mBufferQueueSchedulers.count(create.id()) != 0) {
- mBufferQueueSchedulers[create.id()]->setSurfaceControl(
- mLayers[create.id()], mColors[create.id()]);
- }
-
- return NO_ERROR;
-}
-
-status_t Replayer::injectVSyncEvent(
- const VSyncEvent& vSyncEvent, const std::shared_ptr<Event>& event) {
- ALOGV("Injecting VSync Event");
-
- event->readyToExecute();
-
- SurfaceComposerClient::injectVSync(vSyncEvent.when());
-
- return NO_ERROR;
-}
-
-void Replayer::createDisplay(const DisplayCreation& create, const std::shared_ptr<Event>& event) {
- ALOGV("Creating display");
- event->readyToExecute();
-
- std::lock_guard<std::mutex> lock(mDisplayLock);
- sp<IBinder> display = SurfaceComposerClient::createDisplay(
- String8(create.name().c_str()), create.is_secure());
- mDisplays[create.id()] = display;
-
- mDisplayCond.notify_all();
-
- ALOGV("Done creating display");
-}
-
-void Replayer::deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event) {
- ALOGV("Delete display");
- event->readyToExecute();
-
- std::lock_guard<std::mutex> lock(mDisplayLock);
- SurfaceComposerClient::destroyDisplay(mDisplays[delete_.id()]);
- mDisplays.erase(delete_.id());
-}
-
-void Replayer::updatePowerMode(const PowerModeUpdate& pmu, const std::shared_ptr<Event>& event) {
- ALOGV("Updating power mode");
- event->readyToExecute();
- SurfaceComposerClient::setDisplayPowerMode(mDisplays[pmu.id()], pmu.mode());
-}
-
-void Replayer::waitUntilTimestamp(int64_t timestamp) {
- ALOGV("Waiting for %lld nanoseconds...", static_cast<int64_t>(timestamp - mCurrentTime));
- std::this_thread::sleep_for(std::chrono::nanoseconds(timestamp - mCurrentTime));
-}
-
-status_t Replayer::loadSurfaceComposerClient() {
- mComposerClient = new SurfaceComposerClient;
- return mComposerClient->initCheck();
-}
-
-void Replayer::setReparentChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ReparentChange& c) {
- sp<SurfaceControl> newSurfaceControl = nullptr;
- if (mLayers.count(c.parent_id()) != 0 && mLayers[c.parent_id()] != nullptr) {
- newSurfaceControl = mLayers[c.parent_id()];
- }
- t.reparent(mLayers[id], newSurfaceControl);
-}
-
-void Replayer::setRelativeParentChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const RelativeParentChange& c) {
- if (mLayers.count(c.relative_parent_id()) == 0 || mLayers[c.relative_parent_id()] == nullptr) {
- ALOGE("Layer %d not found in set relative parent transaction", c.relative_parent_id());
- return;
- }
- t.setRelativeLayer(mLayers[id], mLayers[c.relative_parent_id()], c.z());
-}
-
-void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const ShadowRadiusChange& c) {
- t.setShadowRadius(mLayers[id], c.radius());
-}
-
-void Replayer::setBlurRegionsChange(SurfaceComposerClient::Transaction& t,
- layer_id id, const BlurRegionsChange& c) {
- std::vector<BlurRegion> regions;
- for(size_t i=0; i < c.blur_regions_size(); i++) {
- auto protoRegion = c.blur_regions(i);
- regions.push_back(BlurRegion{
- .blurRadius = protoRegion.blur_radius(),
- .alpha = protoRegion.alpha(),
- .cornerRadiusTL = protoRegion.corner_radius_tl(),
- .cornerRadiusTR = protoRegion.corner_radius_tr(),
- .cornerRadiusBL = protoRegion.corner_radius_bl(),
- .cornerRadiusBR = protoRegion.corner_radius_br(),
- .left = protoRegion.left(),
- .top = protoRegion.top(),
- .right = protoRegion.right(),
- .bottom = protoRegion.bottom()
- });
- }
- t.setBlurRegions(mLayers[id], regions);
-}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index ab09de1..64ef827 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -287,6 +287,12 @@
}
prebuilt_etc {
+ name: "android.hardware.telephony.data.prebuilt.xml",
+ src: "android.hardware.telephony.data.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.telephony.gsm.prebuilt.xml",
src: "android.hardware.telephony.gsm.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.satellite.xml b/data/etc/android.hardware.telephony.satellite.xml
new file mode 100644
index 0000000..945e720
--- /dev/null
+++ b/data/etc/android.hardware.telephony.satellite.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<!-- This is the standard set of features for devices to support Telephony Satellite API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.satellite" />
+</permissions>
\ No newline at end of file
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 c6cf7ad..fad8c1b 100644
--- a/include/android/OWNERS
+++ b/include/android/OWNERS
@@ -1 +1,5 @@
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
+per-file input_transfer_token.h = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
diff --git a/include/android/input.h b/include/android/input.h
index fec56f0..ee98d7a 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1002,7 +1002,6 @@
* Keyboard types.
*
* Refer to the documentation on android.view.InputDevice for more details.
- * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated.
*/
enum {
/** none */
diff --git a/include/android/input_transfer_token_jni.h b/include/android/input_transfer_token_jni.h
new file mode 100644
index 0000000..92fe9b6
--- /dev/null
+++ b/include/android/input_transfer_token_jni.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+/**
+ * @file input_transfer_token_jni.h
+ */
+
+#pragma once
+
+#include <sys/cdefs.h>
+#include <jni.h>
+
+__BEGIN_DECLS
+struct AInputTransferToken;
+
+/**
+ * AInputTransferToken can be used to request focus on or to transfer touch gesture to and from
+ * an embedded SurfaceControl
+ */
+typedef struct AInputTransferToken AInputTransferToken;
+
+/**
+ * Return the AInputTransferToken wrapped by a Java InputTransferToken object. This must be released
+ * using AInputTransferToken_release
+ *
+ * inputTransferTokenObj must be a non-null instance of android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+AInputTransferToken* _Nonnull AInputTransferToken_fromJava(JNIEnv* _Nonnull env,
+ jobject _Nonnull inputTransferTokenObj) __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * Return the Java InputTransferToken object that wraps AInputTransferToken
+ *
+ * aInputTransferToken must be non null and the returned value is an object of instance
+ * android.window.InputTransferToken.
+ *
+ * Available since API level 35.
+ */
+jobject _Nonnull AInputTransferToken_toJava(JNIEnv* _Nonnull env,
+ const AInputTransferToken* _Nonnull aInputTransferToken) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Removes a reference that was previously acquired in native.
+ *
+ * Available since API level 35.
+ */
+void AInputTransferToken_release(AInputTransferToken* _Nullable aInputTransferToken)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+__END_DECLS
+/** @} */
diff --git a/include/android/performance_hint.h b/include/android/performance_hint.h
index 62d0423..8736695 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -115,6 +115,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/surface_control.h b/include/android/surface_control.h
index 665d9c6..82cacca 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -239,6 +239,10 @@
* it is acquired. If no acquire_fence_fd was provided, this timestamp will be set to -1.
*
* Available since API level 29.
+ *
+ * @deprecated This may return SIGNAL_PENDING because the stats can arrive before the acquire
+ * 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* _Nonnull surface_transaction_stats,
@@ -573,7 +577,7 @@
* using this API for formats that encode an HDR/SDR ratio as part of generating the buffer.
*
* @param surface_control The layer whose extended range brightness is being specified
- * @param currentBufferRatio The current hdr/sdr ratio of the current buffer as represented as
+ * @param currentBufferRatio The current HDR/SDR ratio of the current buffer as represented as
* peakHdrBrightnessInNits / targetSdrWhitePointInNits. For example if the
* buffer was rendered with a target SDR whitepoint of 100nits and a max
* display brightness of 200nits, this should be set to 2.0f.
@@ -587,7 +591,7 @@
*
* Must be finite && >= 1.0f
*
- * @param desiredRatio The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredRatio The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max desired
* brightness range. This is similar to the "max luminance" value in other
* HDR metadata formats, but represented as a ratio of the target SDR whitepoint
@@ -620,13 +624,13 @@
__INTRODUCED_IN(__ANDROID_API_U__);
/**
- * Sets the desired hdr headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
+ * Sets the desired HDR headroom for the layer. See: ASurfaceTransaction_setExtendedRangeBrightness,
* prefer using this API for formats that conform to HDR standards like HLG or HDR10, that do not
* communicate a HDR/SDR ratio as part of generating the buffer.
*
- * @param surface_control The layer whose desired hdr headroom is being specified
+ * @param surface_control The layer whose desired HDR headroom is being specified
*
- * @param desiredHeadroom The desired hdr/sdr ratio as represented as peakHdrBrightnessInNits /
+ * @param desiredHeadroom The desired HDR/SDR ratio as represented as peakHdrBrightnessInNits /
* targetSdrWhitePointInNits. This can be used to communicate the max
* desired brightness range of the panel. The system may not be able to, or
* may choose not to, deliver the requested range.
diff --git a/include/android/surface_control_input_receiver.h b/include/android/surface_control_input_receiver.h
new file mode 100644
index 0000000..bdc5249
--- /dev/null
+++ b/include/android/surface_control_input_receiver.h
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+/**
+ * @addtogroup NativeActivity Native Activity
+ * @{
+ */
+/**
+ * @file surface_control_input_receiver.h
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <android/input.h>
+#include <android/surface_control.h>
+#include <android/input_transfer_token_jni.h>
+
+__BEGIN_DECLS
+
+/**
+ * The AInputReceiver_onMotionEvent callback is invoked when the registered input channel receives
+ * a motion event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param motionEvent The motion event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onMotionEvent)(void *_Null_unspecified context,
+ AInputEvent *_Nonnull motionEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+/**
+ * The AInputReceiver_onKeyEvent callback is invoked when the registered input channel receives
+ * a key event.
+ *
+ * \param context Optional context provided by the client that is passed when creating the
+ * AInputReceiverCallbacks.
+ *
+ * \param keyEvent The key event. This must be released with AInputEvent_release.
+ *
+ * Available since API level 35.
+ */
+typedef bool (*AInputReceiver_onKeyEvent)(void *_Null_unspecified context,
+ AInputEvent *_Nonnull keyEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+struct AInputReceiverCallbacks;
+
+struct AInputReceiver;
+
+/**
+ * The InputReceiver that holds the reference to the registered input channel. This must be released
+ * using AInputReceiver_release
+ *
+ * Available since API level 35.
+ */
+typedef struct AInputReceiver AInputReceiver __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive batched input event. For
+ * those events that are batched, the invocation will happen once per AChoreographer frame, and
+ * other input events will be delivered immediately.
+ *
+ * This is different from AInputReceiver_createUnbatchedInputReceiver in that the input events are
+ * received batched. The caller must invoke AInputReceiver_release to clean up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aChoreographer The AChoreographer used for batching. This should match the
+ * rendering AChoreographer.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded. This can be retrieved for the host window
+ * by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createBatchedInputReceiver(AChoreographer* _Nonnull aChoreographer,
+ const AInputTransferToken* _Nonnull hostInputTransferToken,
+ const ASurfaceControl* _Nonnull aSurfaceControl,
+ AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Registers an input receiver for an ASurfaceControl that will receive every input event.
+ * This is different from AInputReceiver_createBatchedInputReceiver in that the input events are
+ * received unbatched. The caller must invoke AInputReceiver_release to clean up the resources when
+ * no longer needing to use the input receiver.
+ *
+ * \param aLooper The looper to use when invoking callbacks.
+ * \param hostInputTransferToken The host token to link the embedded. This is used to handle
+ * transferring touch gesture from host to embedded and for ANRs
+ * to ensure the host receives the ANR if any issues with
+ * touch on the embedded. This can be retrieved for the host window
+ * by calling AttachedSurfaceControl#getInputTransferToken()
+ * \param aSurfaceControl The ASurfaceControl to register the InputChannel for
+ * \param aInputReceiverCallbacks The SurfaceControlInputReceiver that will receive the input events
+ *
+ * Returns the reference to AInputReceiver to clean up resources when done.
+ *
+ * Available since API level 35.
+ */
+AInputReceiver* _Nonnull
+AInputReceiver_createUnbatchedInputReceiver(ALooper* _Nonnull aLooper,
+ const AInputTransferToken* _Nonnull hostInputTransferToken,
+ const ASurfaceControl* _Nonnull aSurfaceControl,
+ AInputReceiverCallbacks* _Nonnull aInputReceiverCallbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Returns the AInputTransferToken that can be used to transfer touch gesture to or from other
+ * windows. This InputTransferToken is associated with the SurfaceControl that registered an input
+ * receiver and can be used with the host token for things like transfer touch gesture via
+ * WindowManager#transferTouchGesture().
+ *
+ * This must be released with AInputTransferToken_release.
+ *
+ * \param aInputReceiver The inputReceiver object to retrieve the AInputTransferToken for.
+ *
+ * Available since API level 35.
+ */
+const AInputTransferToken *_Nonnull
+AInputReceiver_getInputTransferToken(AInputReceiver *_Nonnull aInputReceiver)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Unregisters the input channel and deletes the AInputReceiver. This must be called on the same
+ * looper thread it was created with.
+ *
+ * \param aInputReceiver The inputReceiver object to release.
+ *
+ * Available since API level 35.
+ */
+void
+AInputReceiver_release(AInputReceiver *_Nullable aInputReceiver) __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Creates a AInputReceiverCallbacks object that is used when registering for an AInputReceiver.
+ * This must be released using AInputReceiverCallbacks_release
+ *
+ * \param context Optional context provided by the client that will be passed into the callbacks.
+ *
+ * Available since API level 35.
+ */
+AInputReceiverCallbacks* _Nonnull AInputReceiverCallbacks_create(void* _Nullable context)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Releases the AInputReceiverCallbacks. This must be called on the same
+ * looper thread the AInputReceiver was created with. The receiver will not invoke any callbacks
+ * once it's been released.
+ *
+ * Available since API level 35
+ */
+void AInputReceiverCallbacks_release(AInputReceiverCallbacks* _Nullable callbacks)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onMotionEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The motion event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setMotionEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+ AInputReceiver_onMotionEvent _Nonnull onMotionEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+/**
+ * Sets a AInputReceiver_onKeyEvent callback for an AInputReceiverCallbacks
+ *
+ * \param callbacks The callback object to set the motion event on.
+ * \param onMotionEvent The key event that will be invoked
+ *
+ * Available since API level 35.
+ */
+void AInputReceiverCallbacks_setKeyEventCallback(AInputReceiverCallbacks* _Nonnull callbacks,
+ AInputReceiver_onKeyEvent _Nonnull onKeyEvent)
+ __INTRODUCED_IN(__ANDROID_API_V__);
+
+__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/concat.h b/include/ftl/concat.h
index e0774d3..7680bed 100644
--- a/include/ftl/concat.h
+++ b/include/ftl/concat.h
@@ -57,7 +57,7 @@
template <std::size_t N>
struct Concat<N> {
static constexpr std::size_t max_size() { return N; }
- constexpr std::size_t size() const { return end_ - buffer_; }
+ constexpr std::size_t size() const { return static_cast<std::size_t>(end_ - buffer_); }
constexpr const char* c_str() const { return buffer_; }
@@ -68,6 +68,8 @@
protected:
constexpr Concat() : end_(buffer_) {}
+ constexpr Concat(const Concat&) = delete;
+
constexpr void append() { *end_ = '\0'; }
char buffer_[N + 1];
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/expected.h b/include/ftl/expected.h
index 12b6102..7e765c5 100644
--- a/include/ftl/expected.h
+++ b/include/ftl/expected.h
@@ -18,9 +18,87 @@
#include <android-base/expected.h>
#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <utility>
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short), FTL_TRY
+// unwraps T out of R, or bails out of the enclosing function F if R has an error E. The return type
+// of F must be R, since FTL_TRY propagates R in the error case. As a special case, ftl::Unit may be
+// used as the error E to allow FTL_TRY expressions when F returns `void`.
+//
+// The non-standard syntax requires `-Wno-gnu-statement-expression-from-macro-expansion` to compile.
+// The UnitToVoid conversion allows the macro to be used for early exit from a function that returns
+// `void`.
+//
+// Example usage:
+//
+// using StringExp = ftl::Expected<std::string, std::errc>;
+//
+// StringExp repeat(StringExp exp) {
+// const std::string str = FTL_TRY(exp);
+// return StringExp(str + str);
+// }
+//
+// assert(StringExp("haha"s) == repeat(StringExp("ha"s)));
+// assert(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+// return e == std::errc::bad_message;
+// }));
+//
+//
+// FTL_TRY may be used in void-returning functions by using ftl::Unit as the error type:
+//
+// void uppercase(char& c, ftl::Optional<char> opt) {
+// c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
+// }
+//
+// char c = '?';
+// uppercase(c, std::nullopt);
+// assert(c == '?');
+//
+// uppercase(c, 'a');
+// assert(c == 'A');
+//
+#define FTL_TRY(expr) \
+ ({ \
+ auto exp_ = (expr); \
+ if (!exp_.has_value()) { \
+ using E = decltype(exp_)::error_type; \
+ return android::ftl::details::UnitToVoid<E>::from(std::move(exp_)); \
+ } \
+ exp_.value(); \
+ })
+
+// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short),
+// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E.
+// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not
+// need to propagate R because T is not relevant to the caller.
+//
+// Example usage:
+//
+// using StringExp = ftl::Expected<std::string, std::errc>;
+//
+// std::errc repeat(StringExp exp, std::string& out) {
+// const std::string str = FTL_EXPECT(exp);
+// out = str + str;
+// return std::errc::operation_in_progress;
+// }
+//
+// std::string str;
+// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str));
+// assert("haha"s == str);
+// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str));
+// assert("haha"s == str);
+//
+#define FTL_EXPECT(expr) \
+ ({ \
+ auto exp_ = (expr); \
+ if (!exp_.has_value()) { \
+ return std::move(exp_.error()); \
+ } \
+ exp_.value(); \
+ })
+
namespace android::ftl {
// Superset of base::expected<T, E> with monadic operations.
diff --git a/include/ftl/function.h b/include/ftl/function.h
index 3538ca4..bda5b75 100644
--- a/include/ftl/function.h
+++ b/include/ftl/function.h
@@ -123,7 +123,7 @@
// // Create a typedef to give a more meaningful name and bound the size.
// using MyFunction = ftl::Function<int(std::string_view), 2>;
// int* ptr = nullptr;
-// auto f1 = MyFunction::make_function(
+// auto f1 = MyFunction::make(
// [cls = &cls, ptr](std::string_view sv) {
// return cls->on_string(ptr, sv);
// });
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/ftl/optional.h b/include/ftl/optional.h
index 94d8e3d..e245d88 100644
--- a/include/ftl/optional.h
+++ b/include/ftl/optional.h
@@ -20,13 +20,14 @@
#include <optional>
#include <utility>
+#include <android-base/expected.h>
#include <ftl/details/optional.h>
namespace android::ftl {
// Superset of std::optional<T> with monadic operations, as proposed in https://wg21.link/P0798R8.
//
-// TODO: Remove in C++23.
+// TODO: Remove standard APIs in C++23.
//
template <typename T>
struct Optional final : std::optional<T> {
@@ -109,6 +110,13 @@
return std::forward<F>(f)();
}
+ // Maps this Optional<T> to expected<T, E> where nullopt becomes E.
+ template <typename E>
+ constexpr auto ok_or(E&& e) && -> base::expected<T, E> {
+ if (has_value()) return std::move(value());
+ return base::unexpected(std::forward<E>(e));
+ }
+
// Delete new for this class. Its base doesn't have a virtual destructor, and
// if it got deleted via base class pointer, it would cause undefined
// behavior. There's not a good reason to allocate this object on the heap
diff --git a/include/ftl/unit.h b/include/ftl/unit.h
index e38230b..62549a3 100644
--- a/include/ftl/unit.h
+++ b/include/ftl/unit.h
@@ -58,4 +58,22 @@
return {std::forward<F>(f)};
}
+namespace details {
+
+// Identity function for all T except Unit, which maps to void.
+template <typename T>
+struct UnitToVoid {
+ template <typename U>
+ static auto from(U&& value) {
+ return value;
+ }
+};
+
+template <>
+struct UnitToVoid<Unit> {
+ template <typename U>
+ static void from(U&&) {}
+};
+
+} // namespace details
} // namespace android::ftl
diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h
index b0eceef..56294dd 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;
ui::Rotation orientation;
int32_t logicalLeft;
int32_t logicalTop;
@@ -67,7 +66,7 @@
ViewportType type;
DisplayViewport()
- : displayId(ADISPLAY_ID_NONE),
+ : displayId(ui::LogicalDisplayId::INVALID),
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::LogicalDisplayId::INVALID;
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 a84dcfc..ec08cdd 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>
@@ -39,13 +41,11 @@
* Additional private constants not defined in ndk/ui/input.h.
*/
enum {
-#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,
-#else
- AKEY_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
-#endif
+ android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+
/* Signifies that the key is being predispatched */
AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000,
@@ -53,11 +53,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 +68,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 +85,32 @@
* 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,
-#else
- AMOTION_EVENT_FLAG_IS_ACCESSIBILITY_EVENT = 0x800,
-#endif
+ android::os::IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT,
+
+ 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,
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION =
+ android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION,
+
+ /** Private flag, not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants::
+ MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+
+ /** Mask for all private flags that are not used in Java. */
+ AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
};
/**
@@ -115,9 +130,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 {
/*
@@ -194,9 +210,7 @@
namespace android {
-#ifdef __linux__
class Parcel;
-#endif
/*
* Apply the given transform to the point without applying any translation/offset.
@@ -207,8 +221,12 @@
* Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or
* pointing upwards in the negative Y direction, a positive angle points towards the right, and a
* negative angle points towards the left.
+ *
+ * If the angle represents a direction that needs to be preserved, set isDirectional to true to get
+ * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set
+ * isDirectional to false to get an output range of [-pi/2, pi/2].
*/
-float transformAngle(const ui::Transform& transform, float angleRadians);
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional);
/**
* The type of the InputEvent.
@@ -256,6 +274,16 @@
ftl_last = VIRTUAL,
};
+/**
+ * The keyboard type. This should have 1:1 correspondence with the values of anonymous enum
+ * defined in input.h
+ */
+enum class KeyboardType {
+ NONE = AINPUT_KEYBOARD_TYPE_NONE,
+ NON_ALPHABETIC = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ ALPHABETIC = AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+};
+
bool isStylusToolType(ToolType toolType);
struct PointerProperties;
@@ -302,12 +330,8 @@
POLICY_FLAG_RAW_MASK = 0x0000ffff,
-#ifdef __linux__
POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY =
android::os::IInputConstants::POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY,
-#else
- POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY = 0x20000,
-#endif
/* These flags are set by the input dispatcher. */
@@ -464,8 +488,6 @@
// axes, however the window scaling will not.
void scale(float globalScale, float windowXScale, float windowYScale);
- void transform(const ui::Transform& transform);
-
inline float getX() const {
return getAxisValue(AMOTION_EVENT_AXIS_X);
}
@@ -476,10 +498,8 @@
vec2 getXYValue() const { return vec2(getX(), getY()); }
-#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
-#endif
bool operator==(const PointerCoords& other) const;
inline bool operator!=(const PointerCoords& other) const {
@@ -538,9 +558,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 +569,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 +577,7 @@
int32_t mId;
DeviceId mDeviceId;
uint32_t mSource;
- int32_t mDisplayId;
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::INVALID};
std::array<uint8_t, 32> mHmac;
};
@@ -593,7 +613,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);
@@ -662,10 +682,6 @@
inline void setActionButton(int32_t button) { mActionButton = button; }
- inline float getXOffset() const { return mTransform.tx(); }
-
- inline float getYOffset() const { return mTransform.ty(); }
-
inline const ui::Transform& getTransform() const { return mTransform; }
std::optional<ui::Rotation> getSurfaceRotation() const;
@@ -859,7 +875,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,
@@ -870,12 +886,32 @@
void copyFrom(const MotionEvent* other, bool keepHistory);
+ // Initialize this event by keeping only the pointers from "other" that are in splitPointerIds.
+ void splitFrom(const MotionEvent& other, std::bitset<MAX_POINTER_ID + 1> splitPointerIds,
+ int32_t newEventId);
+
void addSample(
nsecs_t eventTime,
const PointerCoords* pointerCoords);
void offsetLocation(float xOffset, float yOffset);
+ /**
+ * Get the X offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawXOffset() const;
+
+ /**
+ * Get the Y offset of this motion event relative to the origin of the raw coordinate space.
+ *
+ * In practice, this is the delta that was added to the raw screen coordinates (i.e. in logical
+ * display space) to adjust for the absolute position of the containing windows and views.
+ */
+ float getRawYOffset() const;
+
void scale(float globalScaleFactor);
// Set 3x3 perspective matrix transformation.
@@ -886,10 +922,8 @@
// Matrix is in row-major form and compatible with SkMatrix.
void applyTransform(const std::array<float, 9>& matrix);
-#ifdef __linux__
status_t readFromParcel(Parcel* parcel);
status_t writeToParcel(Parcel* parcel) const;
-#endif
static bool isTouchEvent(uint32_t source, int32_t action);
inline bool isTouchEvent() const {
@@ -910,15 +944,22 @@
static std::string actionToString(int32_t action);
+ static std::tuple<int32_t /*action*/, std::vector<PointerProperties>,
+ std::vector<PointerCoords>>
+ split(int32_t action, int32_t flags, int32_t historySize, const std::vector<PointerProperties>&,
+ const std::vector<PointerCoords>&, std::bitset<MAX_POINTER_ID + 1> splitPointerIds);
+
// MotionEvent will transform various axes in different ways, based on the source. For
// example, the x and y axes will not have any offsets/translations applied if it comes from a
// relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
// are used to apply these transformations for different axes.
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
- static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
- const PointerCoords&);
- static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&,
- const PointerCoords&);
+ static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
+ static void calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source,
+ int32_t flags, const ui::Transform&);
+ static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags,
+ const ui::Transform&, const PointerCoords&);
// The rounding precision for transformed motion events.
static constexpr float ROUNDING_PRECISION = 0.001f;
@@ -1043,7 +1084,7 @@
DeviceId deviceId;
nsecs_t eventTimeNanos;
uint32_t source;
- int32_t displayId;
+ ui::LogicalDisplayId displayId;
};
/**
@@ -1171,15 +1212,17 @@
*/
struct PointerCaptureRequest {
public:
- inline PointerCaptureRequest() : enable(false), seq(0) {}
- inline PointerCaptureRequest(bool enable, uint32_t seq) : enable(enable), seq(seq) {}
+ inline PointerCaptureRequest() : window(), seq(0) {}
+ inline PointerCaptureRequest(sp<IBinder> window, uint32_t seq) : window(window), seq(seq) {}
inline bool operator==(const PointerCaptureRequest& other) const {
- return enable == other.enable && seq == other.seq;
+ return window == other.window && seq == other.seq;
}
- explicit inline operator bool() const { return enable; }
+ inline bool isEnable() const { return window != nullptr; }
- // True iff this is a request to enable Pointer Capture.
- bool enable;
+ // The requesting window.
+ // If the request is to enable the capture, this is the input token of the window that requested
+ // pointer capture. Otherwise, this is nullptr.
+ sp<IBinder> window;
// The sequence number for the request.
uint32_t seq;
@@ -1190,43 +1233,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
new file mode 100644
index 0000000..611478c
--- /dev/null
+++ b/include/input/InputConsumer.h
@@ -0,0 +1,254 @@
+/*
+ * 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
+
+/*
+ * Native input transport.
+ *
+ * The InputConsumer is used by the application to receive events from the input dispatcher.
+ */
+
+#include "InputTransport.h"
+
+namespace android {
+
+/*
+ * Consumes input events from an input channel.
+ */
+class InputConsumer {
+public:
+ /* Create a consumer associated with an input channel. */
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
+ /* Create a consumer associated with an input channel, override resampling system property */
+ explicit InputConsumer(const std::shared_ptr<InputChannel>& channel,
+ bool enableTouchResampling);
+
+ /* Destroys the consumer and releases its input channel. */
+ ~InputConsumer();
+
+ /* Gets the underlying input channel. */
+ inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
+
+ /* Consumes an input event from the input channel and copies its contents into
+ * an InputEvent object created using the specified factory.
+ *
+ * Tries to combine a series of move events into larger batches whenever possible.
+ *
+ * If consumeBatches is false, then defers consuming pending batched events if it
+ * is possible for additional samples to be added to them later. Call hasPendingBatch()
+ * to determine whether a pending batch is available to be consumed.
+ *
+ * If consumeBatches is true, then events are still batched but they are consumed
+ * immediately as soon as the input channel is exhausted.
+ *
+ * The frameTime parameter specifies the time when the current display frame started
+ * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
+ *
+ * The returned sequence number is never 0 unless the operation failed.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if there is no event present.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Returns NO_MEMORY if the event could not be created.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent);
+
+ /* Sends a finished signal to the publisher to inform it that the message
+ * with the specified sequence number has finished being process and whether
+ * the message was handled by the consumer.
+ *
+ * Returns OK on success.
+ * Returns BAD_VALUE if seq is 0.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t sendFinishedSignal(uint32_t seq, bool handled);
+
+ status_t sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
+
+ /* Returns true if there is a pending batch.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ bool hasPendingBatch() const;
+
+ /* Returns the source of first pending batch if exist.
+ *
+ * Should be called after calling consume() with consumeBatches == false to determine
+ * whether consume() should be called again later on with consumeBatches == true.
+ */
+ int32_t getPendingBatchSource() const;
+
+ /* Returns true when there is *likely* a pending batch or a pending event in the channel.
+ *
+ * This is only a performance hint and may return false negative results. Clients should not
+ * rely on availability of the message based on the return value.
+ */
+ bool probablyHasInput() const;
+
+ std::string dump() const;
+
+private:
+ // True if touch resampling is enabled.
+ const bool mResampleTouch;
+
+ 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;
+
+ // True if mMsg contains a valid input message that was deferred from the previous
+ // call to consume and that still needs to be handled.
+ bool mMsgDeferred;
+
+ // Batched motion events per device and source.
+ struct Batch {
+ std::vector<InputMessage> samples;
+ };
+ std::vector<Batch> mBatches;
+
+ // Touch state per device and source, only for sources of class pointer.
+ struct History {
+ nsecs_t eventTime;
+ BitSet32 idBits;
+ int32_t idToIndex[MAX_POINTER_ID + 1];
+ PointerCoords pointers[MAX_POINTERS];
+
+ void initializeFrom(const InputMessage& msg) {
+ eventTime = msg.body.motion.eventTime;
+ idBits.clear();
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ uint32_t id = msg.body.motion.pointers[i].properties.id;
+ idBits.markBit(id);
+ idToIndex[id] = i;
+ pointers[i].copyFrom(msg.body.motion.pointers[i].coords);
+ }
+ }
+
+ void initializeFrom(const History& other) {
+ eventTime = other.eventTime;
+ idBits = other.idBits; // temporary copy
+ for (size_t i = 0; i < other.idBits.count(); i++) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ int32_t index = other.idToIndex[id];
+ idToIndex[id] = index;
+ pointers[index].copyFrom(other.pointers[index]);
+ }
+ idBits = other.idBits; // final copy
+ }
+
+ const PointerCoords& getPointerById(uint32_t id) const { return pointers[idToIndex[id]]; }
+
+ bool hasPointerId(uint32_t id) const { return idBits.hasBit(id); }
+ };
+ struct TouchState {
+ int32_t deviceId;
+ int32_t source;
+ size_t historyCurrent;
+ size_t historySize;
+ History history[2];
+ History lastResample;
+
+ void initialize(int32_t incomingDeviceId, int32_t incomingSource) {
+ deviceId = incomingDeviceId;
+ source = incomingSource;
+ historyCurrent = 0;
+ historySize = 0;
+ lastResample.eventTime = 0;
+ lastResample.idBits.clear();
+ }
+
+ void addHistory(const InputMessage& msg) {
+ historyCurrent ^= 1;
+ if (historySize < 2) {
+ historySize += 1;
+ }
+ history[historyCurrent].initializeFrom(msg);
+ }
+
+ const History* getHistory(size_t index) const {
+ return &history[(historyCurrent + index) & 1];
+ }
+
+ bool recentCoordinatesAreIdentical(uint32_t id) const {
+ // Return true if the two most recently received "raw" coordinates are identical
+ if (historySize < 2) {
+ return false;
+ }
+ if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) {
+ return false;
+ }
+ float currentX = getHistory(0)->getPointerById(id).getX();
+ float currentY = getHistory(0)->getPointerById(id).getY();
+ float previousX = getHistory(1)->getPointerById(id).getX();
+ float previousY = getHistory(1)->getPointerById(id).getY();
+ if (currentX == previousX && currentY == previousY) {
+ return true;
+ }
+ return false;
+ }
+ };
+ std::vector<TouchState> mTouchStates;
+
+ // Chain of batched sequence numbers. When multiple input messages are combined into
+ // a batch, we append a record here that associates the last sequence number in the
+ // batch with the previous one. When the finished signal is sent, we traverse the
+ // chain to individually finish all input messages that were part of the batch.
+ struct SeqChain {
+ uint32_t seq; // sequence number of batched input message
+ uint32_t chain; // sequence number of previous batched input message
+ };
+ std::vector<SeqChain> mSeqChains;
+
+ // The time at which each event with the sequence number 'seq' was consumed.
+ // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+ // This collection is populated when the event is received, and the entries are erased when the
+ // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+ // will be raised for that connection, and no further events will be posted to that channel.
+ std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+
+ status_t consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime, uint32_t* outSeq,
+ InputEvent** outEvent);
+ status_t consumeSamples(InputEventFactoryInterface* factory, Batch& batch, size_t count,
+ uint32_t* outSeq, InputEvent** outEvent);
+
+ void updateTouchState(InputMessage& msg);
+ void resampleTouchState(nsecs_t frameTime, MotionEvent* event, const InputMessage* next);
+
+ ssize_t findBatch(int32_t deviceId, int32_t source) const;
+ ssize_t findTouchState(int32_t deviceId, int32_t source) const;
+
+ nsecs_t getConsumeTime(uint32_t seq) const;
+ void popConsumeTime(uint32_t seq);
+ status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
+
+ static void rewriteMessage(TouchState& state, InputMessage& msg);
+ static bool canAddSample(const Batch& batch, const InputMessage* msg);
+ static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
+
+ static bool isTouchResamplingEnabled();
+};
+
+} // namespace android
diff --git a/include/input/InputConsumerNoResampling.h b/include/input/InputConsumerNoResampling.h
new file mode 100644
index 0000000..9e48b08
--- /dev/null
+++ b/include/input/InputConsumerNoResampling.h
@@ -0,0 +1,211 @@
+/**
+ * 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 <utils/Looper.h>
+#include "InputTransport.h"
+
+namespace android {
+
+/**
+ * An interface to receive batched input events. Even if you don't want batching, you still have to
+ * use this interface, and some of the events will be batched if your implementation is slow to
+ * handle the incoming input.
+ */
+class InputConsumerCallbacks {
+public:
+ virtual ~InputConsumerCallbacks(){};
+ virtual void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) = 0;
+ virtual void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) = 0;
+ /**
+ * When you receive this callback, you must (eventually) call "consumeBatchedInputEvents".
+ * If you don't want batching, then call "consumeBatchedInputEvents" immediately with
+ * std::nullopt frameTime to receive the pending motion event(s).
+ * @param pendingBatchSource the source of the pending batch.
+ */
+ virtual void onBatchedInputEventPending(int32_t pendingBatchSource) = 0;
+ virtual void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) = 0;
+ virtual void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) = 0;
+ virtual void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) = 0;
+ virtual void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) = 0;
+};
+
+/**
+ * Consumes input events from an input channel.
+ *
+ * This is a re-implementation of InputConsumer that does not have resampling at the current moment.
+ * A lot of the higher-level logic has been folded into this class, to make it easier to use.
+ * In the legacy class, InputConsumer, the consumption logic was partially handled in the jni layer,
+ * as well as various actions like adding the fd to the Choreographer.
+ *
+ * TODO(b/297226446): use this instead of "InputConsumer":
+ * - Add resampling to this class
+ * - Allow various resampling strategies to be specified
+ * - Delete the old "InputConsumer" and use this class instead, renaming it to "InputConsumer".
+ * - Add tracing
+ * - Update all tests to use the new InputConsumer
+ *
+ * This class is not thread-safe. We are currently allowing the constructor to run on any thread,
+ * but all of the remaining APIs should be invoked on the looper thread only.
+ */
+class InputConsumerNoResampling final {
+public:
+ explicit InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
+ sp<Looper> looper, InputConsumerCallbacks& callbacks);
+ ~InputConsumerNoResampling();
+
+ /**
+ * Must be called exactly once for each event received through the callbacks.
+ */
+ void finishInputEvent(uint32_t seq, bool handled);
+ void reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime, nsecs_t presentTime);
+ /**
+ * If you want to consume all events immediately (disable batching), the you still must call
+ * this. For frameTime, use a std::nullopt.
+ * @param frameTime the time up to which consume the events. When there's double (or triple)
+ * buffering, you may want to not consume all events currently available, because you could be
+ * still working on an older frame, but there could already have been events that arrived that
+ * are more recent.
+ * @return whether any events were actually consumed
+ */
+ bool consumeBatchedInputEvents(std::optional<nsecs_t> frameTime);
+ /**
+ * Returns true when there is *likely* a pending batch or a pending event in the channel.
+ *
+ * This is only a performance hint and may return false negative results. Clients should not
+ * rely on availability of the message based on the return value.
+ */
+ bool probablyHasInput() const;
+
+ std::string getName() { return mChannel->getName(); }
+
+ std::string dump() const;
+
+private:
+ std::shared_ptr<InputChannel> mChannel;
+ sp<Looper> mLooper;
+ InputConsumerCallbacks& mCallbacks;
+
+ // Looper-related infrastructure
+ /**
+ * This class is needed to associate the function "handleReceiveCallback" with the provided
+ * looper. The callback sent to the looper is RefBase - based, so we can't just send a reference
+ * of this class directly to the looper.
+ */
+ class LooperEventCallback : public LooperCallback {
+ public:
+ LooperEventCallback(std::function<int(int events)> callback) : mCallback(callback) {}
+ int handleEvent(int /*fd*/, int events, void* /*data*/) override {
+ return mCallback(events);
+ }
+
+ private:
+ std::function<int(int events)> mCallback;
+ };
+ sp<LooperEventCallback> mCallback;
+ /**
+ * The actual code that executes when the looper encounters available data on the InputChannel.
+ */
+ int handleReceiveCallback(int events);
+ int mFdEvents;
+ void setFdEvents(int events);
+
+ void ensureCalledOnLooperThread(const char* func) const;
+
+ // Event-reading infrastructure
+ /**
+ * A fifo queue of events to be sent to the InputChannel. We can't send all InputMessages to
+ * the channel immediately when they are produced, because it's possible that the InputChannel
+ * is blocked (if the channel buffer is full). When that happens, we don't want to drop the
+ * events. Therefore, events should only be erased from the queue after they've been
+ * successfully written to the InputChannel.
+ */
+ std::queue<InputMessage> mOutboundQueue;
+ /**
+ * Try to send all of the events in mOutboundQueue over the InputChannel. Not all events might
+ * actually get sent, because it's possible that the channel is blocked.
+ */
+ void processOutboundEvents();
+
+ /**
+ * The time at which each event with the sequence number 'seq' was consumed.
+ * This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
+ * This collection is populated when the event is received, and the entries are erased when the
+ * events are finished. It should not grow infinitely because if an event is not ack'd, ANR
+ * will be raised for that connection, and no further events will be posted to that channel.
+ */
+ std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
+ /**
+ * Find and return the consumeTime associated with the provided sequence number. Crashes if
+ * the provided seq number is not found.
+ */
+ nsecs_t popConsumeTime(uint32_t seq);
+
+ // Event reading and processing
+ /**
+ * Read all of the available events from the InputChannel
+ */
+ std::vector<InputMessage> readAllMessages();
+
+ /**
+ * Send InputMessage to the corresponding InputConsumerCallbacks function.
+ * @param msg
+ */
+ void handleMessage(const InputMessage& msg) const;
+
+ // Batching
+ /**
+ * Batch messages that can be batched. When an unbatchable message is encountered, send it
+ * to the InputConsumerCallbacks immediately. If there are batches remaining,
+ * notify InputConsumerCallbacks.
+ */
+ void handleMessages(std::vector<InputMessage>&& messages);
+ /**
+ * Batched InputMessages, per deviceId.
+ * For each device, we are storing a queue of batched messages. These will all be collapsed into
+ * a single MotionEvent (up to a specific frameTime) when the consumer calls
+ * `consumeBatchedInputEvents`.
+ */
+ std::map<DeviceId, std::queue<InputMessage>> mBatches;
+ /**
+ * A map from a single sequence number to several sequence numbers. This is needed because of
+ * batching. When batching is enabled, a single MotionEvent will contain several samples. Each
+ * sample came from an individual InputMessage of Type::Motion, and therefore will have to be
+ * finished individually. Therefore, when the app calls "finish" on a (possibly batched)
+ * MotionEvent, we will need to check this map in case there are multiple sequence numbers
+ * associated with a single number that the app provided.
+ *
+ * For example:
+ * Suppose we received 4 InputMessage's of type Motion, with action MOVE:
+ * InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE) InputMessage(MOVE)
+ * seq=10 seq=11 seq=12 seq=13
+ * The app consumed them all as a batch, which means that the app received a single MotionEvent
+ * with historySize=3 and seq = 10.
+ *
+ * This map will look like:
+ * {
+ * 10: [11, 12, 13],
+ * }
+ * So the sequence number 10 will have 3 other sequence numbers associated with it.
+ * When the app calls 'finish' for seq=10, we need to call 'finish' 4 times total, for sequence
+ * numbers 10, 11, 12, 13. The app is not aware of the sequence numbers of each sample inside
+ * the batched MotionEvent that it received.
+ */
+ std::map<uint32_t, std::vector<uint32_t>> mBatchedSequenceNumbers;
+};
+
+} // namespace android
diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h
index 57b659d..7d8c19e 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::LogicalDisplayId::INVALID};
+ bool mEnabled;
bool mHasVibrator;
bool mHasBattery;
diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h
index 2d23b97..25d35e9 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;
}
@@ -118,6 +117,16 @@
return *this;
}
+ MotionEventBuilder& transform(ui::Transform t) {
+ mTransform = t;
+ return *this;
+ }
+
+ MotionEventBuilder& rawTransform(ui::Transform t) {
+ mRawTransform = t;
+ return *this;
+ }
+
MotionEvent build() {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
@@ -134,12 +143,11 @@
}
MotionEvent event;
- static const ui::Transform kIdentityTransform;
event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC,
mAction, mActionButton, mFlags, /*edgeFlags=*/0, AMETA_NONE, mButtonState,
- MotionClassification::NONE, kIdentityTransform,
+ MotionClassification::NONE, mTransform,
/*xPrecision=*/0, /*yPrecision=*/0, mRawXCursorPosition,
- mRawYCursorPosition, kIdentityTransform, mDownTime, mEventTime,
+ mRawYCursorPosition, mRawTransform, mDownTime, mEventTime,
mPointers.size(), pointerProperties.data(), pointerCoords.data());
return event;
}
@@ -150,12 +158,14 @@
int32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::DEFAULT};
int32_t mActionButton{0};
int32_t mButtonState{0};
int32_t mFlags{0};
float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION};
+ ui::Transform mTransform;
+ ui::Transform mRawTransform;
std::vector<PointerBuilder> mPointers;
};
@@ -198,7 +208,7 @@
return *this;
}
- KeyEventBuilder& displayId(int32_t displayId) {
+ KeyEventBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -237,7 +247,7 @@
uint32_t mSource;
nsecs_t mDownTime;
nsecs_t mEventTime;
- int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ ui::LogicalDisplayId mDisplayId{ui::LogicalDisplayId::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 42dcd3c..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,
@@ -451,236 +452,4 @@
InputVerifier mInputVerifier;
};
-/*
- * Consumes input events from an input channel.
- */
-class InputConsumer {
-public:
- /* Create a consumer associated with an input channel. */
- explicit InputConsumer(const std::shared_ptr<InputChannel>& channel);
- /* Create a consumer associated with an input channel, override resampling system property */
- explicit InputConsumer(const std::shared_ptr<InputChannel>& channel,
- bool enableTouchResampling);
-
- /* Destroys the consumer and releases its input channel. */
- ~InputConsumer();
-
- /* Gets the underlying input channel. */
- inline std::shared_ptr<InputChannel> getChannel() { return mChannel; }
-
- /* Consumes an input event from the input channel and copies its contents into
- * an InputEvent object created using the specified factory.
- *
- * Tries to combine a series of move events into larger batches whenever possible.
- *
- * If consumeBatches is false, then defers consuming pending batched events if it
- * is possible for additional samples to be added to them later. Call hasPendingBatch()
- * to determine whether a pending batch is available to be consumed.
- *
- * If consumeBatches is true, then events are still batched but they are consumed
- * immediately as soon as the input channel is exhausted.
- *
- * The frameTime parameter specifies the time when the current display frame started
- * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown.
- *
- * The returned sequence number is never 0 unless the operation failed.
- *
- * Returns OK on success.
- * Returns WOULD_BLOCK if there is no event present.
- * Returns DEAD_OBJECT if the channel's peer has been closed.
- * Returns NO_MEMORY if the event could not be created.
- * Other errors probably indicate that the channel is broken.
- */
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
- uint32_t* outSeq, InputEvent** outEvent);
-
- /* Sends a finished signal to the publisher to inform it that the message
- * with the specified sequence number has finished being process and whether
- * the message was handled by the consumer.
- *
- * Returns OK on success.
- * Returns BAD_VALUE if seq is 0.
- * Other errors probably indicate that the channel is broken.
- */
- status_t sendFinishedSignal(uint32_t seq, bool handled);
-
- status_t sendTimeline(int32_t inputEventId,
- std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
-
- /* Returns true if there is a pending batch.
- *
- * Should be called after calling consume() with consumeBatches == false to determine
- * whether consume() should be called again later on with consumeBatches == true.
- */
- bool hasPendingBatch() const;
-
- /* Returns the source of first pending batch if exist.
- *
- * Should be called after calling consume() with consumeBatches == false to determine
- * whether consume() should be called again later on with consumeBatches == true.
- */
- int32_t getPendingBatchSource() const;
-
- /* Returns true when there is *likely* a pending batch or a pending event in the channel.
- *
- * This is only a performance hint and may return false negative results. Clients should not
- * rely on availability of the message based on the return value.
- */
- bool probablyHasInput() const;
-
- std::string dump() const;
-
-private:
- // True if touch resampling is enabled.
- const bool mResampleTouch;
-
- std::shared_ptr<InputChannel> mChannel;
-
- // The current input message.
- InputMessage mMsg;
-
- // True if mMsg contains a valid input message that was deferred from the previous
- // call to consume and that still needs to be handled.
- bool mMsgDeferred;
-
- // Batched motion events per device and source.
- struct Batch {
- std::vector<InputMessage> samples;
- };
- std::vector<Batch> mBatches;
-
- // Touch state per device and source, only for sources of class pointer.
- struct History {
- nsecs_t eventTime;
- BitSet32 idBits;
- int32_t idToIndex[MAX_POINTER_ID + 1];
- PointerCoords pointers[MAX_POINTERS];
-
- void initializeFrom(const InputMessage& msg) {
- eventTime = msg.body.motion.eventTime;
- idBits.clear();
- for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
- uint32_t id = msg.body.motion.pointers[i].properties.id;
- idBits.markBit(id);
- idToIndex[id] = i;
- pointers[i].copyFrom(msg.body.motion.pointers[i].coords);
- }
- }
-
- void initializeFrom(const History& other) {
- eventTime = other.eventTime;
- idBits = other.idBits; // temporary copy
- for (size_t i = 0; i < other.idBits.count(); i++) {
- uint32_t id = idBits.clearFirstMarkedBit();
- int32_t index = other.idToIndex[id];
- idToIndex[id] = index;
- pointers[index].copyFrom(other.pointers[index]);
- }
- idBits = other.idBits; // final copy
- }
-
- const PointerCoords& getPointerById(uint32_t id) const {
- return pointers[idToIndex[id]];
- }
-
- bool hasPointerId(uint32_t id) const {
- return idBits.hasBit(id);
- }
- };
- struct TouchState {
- int32_t deviceId;
- int32_t source;
- size_t historyCurrent;
- size_t historySize;
- History history[2];
- History lastResample;
-
- void initialize(int32_t deviceId, int32_t source) {
- this->deviceId = deviceId;
- this->source = source;
- historyCurrent = 0;
- historySize = 0;
- lastResample.eventTime = 0;
- lastResample.idBits.clear();
- }
-
- void addHistory(const InputMessage& msg) {
- historyCurrent ^= 1;
- if (historySize < 2) {
- historySize += 1;
- }
- history[historyCurrent].initializeFrom(msg);
- }
-
- const History* getHistory(size_t index) const {
- return &history[(historyCurrent + index) & 1];
- }
-
- bool recentCoordinatesAreIdentical(uint32_t id) const {
- // Return true if the two most recently received "raw" coordinates are identical
- if (historySize < 2) {
- return false;
- }
- if (!getHistory(0)->hasPointerId(id) || !getHistory(1)->hasPointerId(id)) {
- return false;
- }
- float currentX = getHistory(0)->getPointerById(id).getX();
- float currentY = getHistory(0)->getPointerById(id).getY();
- float previousX = getHistory(1)->getPointerById(id).getX();
- float previousY = getHistory(1)->getPointerById(id).getY();
- if (currentX == previousX && currentY == previousY) {
- return true;
- }
- return false;
- }
- };
- std::vector<TouchState> mTouchStates;
-
- // Chain of batched sequence numbers. When multiple input messages are combined into
- // a batch, we append a record here that associates the last sequence number in the
- // batch with the previous one. When the finished signal is sent, we traverse the
- // chain to individually finish all input messages that were part of the batch.
- struct SeqChain {
- uint32_t seq; // sequence number of batched input message
- uint32_t chain; // sequence number of previous batched input message
- };
- std::vector<SeqChain> mSeqChains;
-
- // The time at which each event with the sequence number 'seq' was consumed.
- // This data is provided in 'finishInputEvent' so that the receiving end can measure the latency
- // This collection is populated when the event is received, and the entries are erased when the
- // events are finished. It should not grow infinitely because if an event is not ack'd, ANR
- // will be raised for that connection, and no further events will be posted to that channel.
- std::unordered_map<uint32_t /*seq*/, nsecs_t /*consumeTime*/> mConsumeTimes;
-
- status_t consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
- status_t consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent);
-
- void updateTouchState(InputMessage& msg);
- void resampleTouchState(nsecs_t frameTime, MotionEvent* event,
- const InputMessage *next);
-
- ssize_t findBatch(int32_t deviceId, int32_t source) const;
- ssize_t findTouchState(int32_t deviceId, int32_t source) const;
-
- nsecs_t getConsumeTime(uint32_t seq) const;
- void popConsumeTime(uint32_t seq);
- status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled);
-
- static void rewriteMessage(TouchState& state, InputMessage& msg);
- static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
- static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
- static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
- static void initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg);
- static void initializeDragEvent(DragEvent* event, const InputMessage* msg);
- static void initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg);
- static void addSample(MotionEvent* event, const InputMessage* msg);
- static bool canAddSample(const Batch& batch, const InputMessage* msg);
- static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
-
- static bool isTouchResamplingEnabled();
-};
-
} // namespace android
diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h
index dfcf766..92d5ec4 100644
--- a/include/input/KeyCharacterMap.h
+++ b/include/input/KeyCharacterMap.h
@@ -19,9 +19,7 @@
#include <stdint.h>
#include <list>
-#ifdef __linux__
#include <binder/IBinder.h>
-#endif
#include <android-base/result.h>
#include <input/Input.h>
@@ -144,13 +142,11 @@
std::pair<int32_t /*keyCode*/, int32_t /*metaState*/> applyKeyBehavior(int32_t keyCode,
int32_t metaState) const;
-#ifdef __linux__
/* Reads a key map from a parcel. */
static std::unique_ptr<KeyCharacterMap> readFromParcel(Parcel* parcel);
/* Writes a key map to a parcel. */
void writeToParcel(Parcel* parcel) const;
-#endif
bool operator==(const KeyCharacterMap& other) const = default;
diff --git a/include/input/KeyboardClassifier.h b/include/input/KeyboardClassifier.h
new file mode 100644
index 0000000..457d474
--- /dev/null
+++ b/include/input/KeyboardClassifier.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/result.h>
+#include <input/Input.h>
+#include <input/InputDevice.h>
+
+#include "rust/cxx.h"
+
+namespace android {
+
+namespace input {
+namespace keyboardClassifier {
+struct KeyboardClassifier;
+}
+} // namespace input
+
+/*
+ * Keyboard classifier to classify keyboard into alphabetic and non-alphabetic keyboards
+ */
+class KeyboardClassifier {
+public:
+ KeyboardClassifier();
+ /**
+ * Get the type of keyboard that the classifier currently believes the device to be.
+ */
+ KeyboardType getKeyboardType(DeviceId deviceId);
+ void notifyKeyboardChanged(DeviceId deviceId, const InputDeviceIdentifier& identifier,
+ uint32_t deviceClasses);
+ void processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState);
+
+private:
+ std::optional<rust::Box<android::input::keyboardClassifier::KeyboardClassifier>>
+ mRustClassifier;
+ std::unordered_map<DeviceId, KeyboardType> mKeyboardTypeMap;
+};
+
+} // namespace android
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/powermanager/HalResult.h b/include/powermanager/HalResult.h
new file mode 100644
index 0000000..7fe3822
--- /dev/null
+++ b/include/powermanager/HalResult.h
@@ -0,0 +1,165 @@
+/*
+ * 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 <android/binder_auto_utils.h>
+#include <android/binder_status.h>
+#include <android/hardware/power/1.0/IPower.h>
+#include <binder/Status.h>
+#include <hidl/HidlSupport.h>
+#include <string>
+
+namespace android::power {
+
+static bool checkUnsupported(const ndk::ScopedAStatus& ndkStatus) {
+ return ndkStatus.getExceptionCode() == EX_UNSUPPORTED_OPERATION ||
+ ndkStatus.getStatus() == STATUS_UNKNOWN_TRANSACTION;
+}
+
+static bool checkUnsupported(const binder::Status& status) {
+ return status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION ||
+ status.transactionError() == UNKNOWN_TRANSACTION;
+}
+
+// Result of a call to the Power HAL wrapper, holding data if successful.
+template <typename T>
+class HalResult {
+public:
+ static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
+ static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
+ static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
+ static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
+ if (checkUnsupported(status)) {
+ return HalResult<T>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<T> fromStatus(const binder::Status& status, T& data) {
+ return HalResult<T>::fromStatus(status, T{data});
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T&& data) {
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<T>::unsupported();
+ }
+ if (ndkStatus.isOk()) {
+ return HalResult<T>::ok(std::forward<T>(data));
+ }
+ return HalResult<T>::failed(std::string(ndkStatus.getDescription()));
+ }
+
+ static HalResult<T> fromStatus(const ndk::ScopedAStatus& ndkStatus, T& data) {
+ return HalResult<T>::fromStatus(ndkStatus, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
+ return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
+ return HalResult<T>::fromReturn(ret, T{data});
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T&& data) {
+ return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
+ : HalResult<T>::failed(ret.description());
+ }
+
+ template <typename R>
+ static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
+ T& data) {
+ return HalResult<T>::fromReturn(ret, status, T{data});
+ }
+
+ // This will throw std::bad_optional_access if this result is not ok.
+ const T& value() const { return mValue.value(); }
+ bool isOk() const { return !mUnsupported && mValue.has_value(); }
+ bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::optional<T> mValue;
+ std::string mErrorMessage;
+ bool mUnsupported;
+
+ explicit HalResult(T&& value)
+ : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
+ explicit HalResult(std::string errorMessage, bool unsupported)
+ : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
+};
+
+// Empty result
+template <>
+class HalResult<void> {
+public:
+ static HalResult<void> ok() { return HalResult(); }
+ static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
+ static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
+
+ static HalResult<void> fromStatus(const binder::Status& status) {
+ if (checkUnsupported(status)) {
+ return HalResult<void>::unsupported();
+ }
+ if (status.isOk()) {
+ return HalResult<void>::ok();
+ }
+ return HalResult<void>::failed(std::string(status.toString8().c_str()));
+ }
+
+ static HalResult<void> fromStatus(const ndk::ScopedAStatus& ndkStatus) {
+ if (ndkStatus.isOk()) {
+ return HalResult<void>::ok();
+ }
+ if (checkUnsupported(ndkStatus)) {
+ return HalResult<void>::unsupported();
+ }
+ return HalResult<void>::failed(ndkStatus.getDescription());
+ }
+
+ template <typename R>
+ static HalResult<void> fromReturn(hardware::Return<R>& ret) {
+ return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
+ }
+
+ bool isOk() const { return !mUnsupported && !mFailed; }
+ bool isFailed() const { return !mUnsupported && mFailed; }
+ bool isUnsupported() const { return mUnsupported; }
+ const char* errorMessage() const { return mErrorMessage.c_str(); }
+
+private:
+ std::string mErrorMessage;
+ bool mFailed;
+ bool mUnsupported;
+
+ explicit HalResult(bool unsupported = false)
+ : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
+ explicit HalResult(std::string errorMessage)
+ : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
+};
+} // namespace android::power
diff --git a/include/powermanager/PowerHalController.h b/include/powermanager/PowerHalController.h
index c50bc4a..7e0bd5b 100644
--- a/include/powermanager/PowerHalController.h
+++ b/include/powermanager/PowerHalController.h
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/power/Mode.h>
#include <android-base/thread_annotations.h>
#include <powermanager/PowerHalWrapper.h>
+#include <powermanager/PowerHintSessionWrapper.h>
namespace android {
@@ -38,6 +39,7 @@
virtual std::unique_ptr<HalWrapper> connect();
virtual void reset();
+ virtual int32_t getAidlVersion();
};
// -------------------------------------------------------------------------------------------------
@@ -59,14 +61,13 @@
int32_t durationMs) override;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode,
bool enabled) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) override;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) override;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
virtual HalResult<int64_t> getHintSessionPreferredRate() override;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(
int tgid, int uid) override;
diff --git a/include/powermanager/PowerHalLoader.h b/include/powermanager/PowerHalLoader.h
index cbbfa59..ab66336 100644
--- a/include/powermanager/PowerHalLoader.h
+++ b/include/powermanager/PowerHalLoader.h
@@ -36,6 +36,8 @@
static sp<hardware::power::V1_1::IPower> loadHidlV1_1();
static sp<hardware::power::V1_2::IPower> loadHidlV1_2();
static sp<hardware::power::V1_3::IPower> loadHidlV1_3();
+ // Returns aidl interface version, or 0 if AIDL is not used
+ static int32_t getAidlVersion();
private:
static std::mutex gHalMutex;
@@ -48,6 +50,8 @@
static sp<hardware::power::V1_0::IPower> loadHidlV1_0Locked()
EXCLUSIVE_LOCKS_REQUIRED(gHalMutex);
+ static int32_t gAidlInterfaceVersion;
+
PowerHalLoader() = delete;
~PowerHalLoader() = delete;
};
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index e2da014..6e347a9 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -26,6 +26,9 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/1.2/IPower.h>
#include <android/hardware/power/1.3/IPower.h>
+#include <powermanager/HalResult.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+
#include <binder/Status.h>
#include <utility>
@@ -41,134 +44,6 @@
OFF = 2,
};
-// Result of a call to the Power HAL wrapper, holding data if successful.
-template <typename T>
-class HalResult {
-public:
- static HalResult<T> ok(T&& value) { return HalResult(std::forward<T>(value)); }
- static HalResult<T> ok(T& value) { return HalResult<T>::ok(T{value}); }
- static HalResult<T> failed(std::string msg) { return HalResult(msg, /* unsupported= */ false); }
- static HalResult<T> unsupported() { return HalResult("", /* unsupported= */ true); }
-
- static HalResult<T> fromStatus(const binder::Status& status, T&& data) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<T> fromStatus(const binder::Status& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T&& data) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<T>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<T>::ok(std::forward<T>(data));
- }
- return HalResult<T>::failed(std::string(status.getDescription()));
- }
-
- static HalResult<T> fromStatus(const ndk::ScopedAStatus& status, T& data) {
- return HalResult<T>::fromStatus(status, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T&& data) {
- return ret.isOk() ? HalResult<T>::ok(std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, T& data) {
- return HalResult<T>::fromReturn(ret, T{data});
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T&& data) {
- return ret.isOk() ? HalResult<T>::fromStatus(status, std::forward<T>(data))
- : HalResult<T>::failed(ret.description());
- }
-
- template <typename R>
- static HalResult<T> fromReturn(hardware::Return<R>& ret, hardware::power::V1_0::Status status,
- T& data) {
- return HalResult<T>::fromReturn(ret, status, T{data});
- }
-
- // This will throw std::bad_optional_access if this result is not ok.
- const T& value() const { return mValue.value(); }
- bool isOk() const { return !mUnsupported && mValue.has_value(); }
- bool isFailed() const { return !mUnsupported && !mValue.has_value(); }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::optional<T> mValue;
- std::string mErrorMessage;
- bool mUnsupported;
-
- explicit HalResult(T&& value)
- : mValue{std::move(value)}, mErrorMessage(), mUnsupported(false) {}
- explicit HalResult(std::string errorMessage, bool unsupported)
- : mValue(), mErrorMessage(std::move(errorMessage)), mUnsupported(unsupported) {}
-};
-
-// Empty result of a call to the Power HAL wrapper.
-template <>
-class HalResult<void> {
-public:
- static HalResult<void> ok() { return HalResult(); }
- static HalResult<void> failed(std::string msg) { return HalResult(std::move(msg)); }
- static HalResult<void> unsupported() { return HalResult(/* unsupported= */ true); }
-
- static HalResult<void> fromStatus(const binder::Status& status) {
- if (status.exceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.toString8().c_str()));
- }
-
- static HalResult<void> fromStatus(const ndk::ScopedAStatus& status) {
- if (status.getExceptionCode() == binder::Status::EX_UNSUPPORTED_OPERATION) {
- return HalResult<void>::unsupported();
- }
- if (status.isOk()) {
- return HalResult<void>::ok();
- }
- return HalResult<void>::failed(std::string(status.getDescription()));
- }
-
- template <typename R>
- static HalResult<void> fromReturn(hardware::Return<R>& ret) {
- return ret.isOk() ? HalResult<void>::ok() : HalResult<void>::failed(ret.description());
- }
-
- bool isOk() const { return !mUnsupported && !mFailed; }
- bool isFailed() const { return !mUnsupported && mFailed; }
- bool isUnsupported() const { return mUnsupported; }
- const char* errorMessage() const { return mErrorMessage.c_str(); }
-
-private:
- std::string mErrorMessage;
- bool mFailed;
- bool mUnsupported;
-
- explicit HalResult(bool unsupported = false)
- : mErrorMessage(), mFailed(false), mUnsupported(unsupported) {}
- explicit HalResult(std::string errorMessage)
- : mErrorMessage(std::move(errorMessage)), mFailed(true), mUnsupported(false) {}
-};
-
// Wrapper for Power HAL handlers.
class HalWrapper {
public:
@@ -177,14 +52,13 @@
virtual HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) = 0;
virtual HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSession(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos) = 0;
- virtual HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
+ int64_t durationNanos) = 0;
+ virtual HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) = 0;
virtual HalResult<int64_t> getHintSessionPreferredRate() = 0;
virtual HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) = 0;
@@ -200,14 +74,13 @@
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
int uid) override;
@@ -285,14 +158,13 @@
HalResult<void> setBoost(aidl::android::hardware::power::Boost boost,
int32_t durationMs) override;
HalResult<void> setMode(aidl::android::hardware::power::Mode mode, bool enabled) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>> createHintSession(
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos) override;
- HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
- createHintSessionWithConfig(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
- int64_t durationNanos,
- aidl::android::hardware::power::SessionTag tag,
- aidl::android::hardware::power::SessionConfig* config) override;
+ HalResult<std::shared_ptr<PowerHintSessionWrapper>> createHintSessionWithConfig(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
+ aidl::android::hardware::power::SessionTag tag,
+ aidl::android::hardware::power::SessionConfig* config) override;
HalResult<int64_t> getHintSessionPreferredRate() override;
HalResult<aidl::android::hardware::power::ChannelConfig> getSessionChannel(int tgid,
@@ -307,14 +179,12 @@
std::mutex mBoostMutex;
std::mutex mModeMutex;
std::shared_ptr<aidl::android::hardware::power::IPower> mHandle;
- // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT.
- // Need to increase the array size if more boost supported.
- std::array<
- std::atomic<HalSupport>,
- static_cast<int32_t>(aidl::android::hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) +
- 1>
+ std::array<HalSupport,
+ static_cast<int32_t>(
+ *(ndk::enum_range<aidl::android::hardware::power::Boost>().end() - 1)) +
+ 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
- std::array<std::atomic<HalSupport>,
+ std::array<HalSupport,
static_cast<int32_t>(
*(ndk::enum_range<aidl::android::hardware::power::Mode>().end() - 1)) +
1>
diff --git a/include/powermanager/PowerHintSessionWrapper.h b/include/powermanager/PowerHintSessionWrapper.h
new file mode 100644
index 0000000..ba6fe77
--- /dev/null
+++ b/include/powermanager/PowerHintSessionWrapper.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <aidl/android/hardware/power/Boost.h>
+#include <aidl/android/hardware/power/ChannelConfig.h>
+#include <aidl/android/hardware/power/IPower.h>
+#include <aidl/android/hardware/power/IPowerHintSession.h>
+#include <aidl/android/hardware/power/Mode.h>
+#include <aidl/android/hardware/power/SessionConfig.h>
+#include <android-base/thread_annotations.h>
+#include "HalResult.h"
+
+namespace android::power {
+
+// Wrapper for power hint sessions, which allows for better mocking,
+// support checking, and failure handling than using hint sessions directly
+class PowerHintSessionWrapper {
+public:
+ virtual ~PowerHintSessionWrapper() = default;
+ PowerHintSessionWrapper(
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>&& session);
+ virtual HalResult<void> updateTargetWorkDuration(int64_t in_targetDurationNanos);
+ virtual HalResult<void> reportActualWorkDuration(
+ const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations);
+ virtual HalResult<void> pause();
+ virtual HalResult<void> resume();
+ virtual HalResult<void> close();
+ virtual HalResult<void> sendHint(::aidl::android::hardware::power::SessionHint in_hint);
+ virtual HalResult<void> setThreads(const std::vector<int32_t>& in_threadIds);
+ virtual HalResult<void> setMode(::aidl::android::hardware::power::SessionMode in_type,
+ bool in_enabled);
+ virtual HalResult<aidl::android::hardware::power::SessionConfig> getSessionConfig();
+
+private:
+ std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mSession;
+ int32_t mInterfaceVersion;
+};
+
+} // namespace android::power
\ No newline at end of file
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/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 f30b2aa..e8fe555 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -2982,14 +2982,15 @@
return continueWrite(desired);
}
+ releaseObjects();
+
uint8_t* data = reallocZeroFree(mData, mDataCapacity, desired, mDeallocZero);
if (!data && desired > mDataCapacity) {
+ LOG_ALWAYS_FATAL("out of memory");
mError = NO_MEMORY;
return NO_MEMORY;
}
- releaseObjects();
-
if (data || desired == 0) {
LOG_ALLOC("Parcel %p: restart from %zu to %zu capacity", this, mDataCapacity, desired);
if (mDataCapacity > desired) {
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index cf59420..af280d3 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -267,11 +267,24 @@
}
}
+void ABBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+ void* /* cookie */) {
+ LOG_ALWAYS_FATAL("Should not reach this. Can't linkToDeath local binders.");
+}
+
ABpBinder::ABpBinder(const ::android::sp<::android::IBinder>& binder)
: AIBinder(nullptr /*clazz*/), mRemote(binder) {
LOG_ALWAYS_FATAL_IF(binder == nullptr, "binder == nullptr");
}
-ABpBinder::~ABpBinder() {}
+
+ABpBinder::~ABpBinder() {
+ for (auto& recip : mDeathRecipients) {
+ sp<AIBinder_DeathRecipient> strongRecip = recip.recipient.promote();
+ if (strongRecip) {
+ strongRecip->pruneThisTransferEntry(getBinder(), recip.cookie);
+ }
+ }
+}
sp<AIBinder> ABpBinder::lookupOrCreateFromBinder(const ::android::sp<::android::IBinder>& binder) {
if (binder == nullptr) {
@@ -310,6 +323,12 @@
return ret;
}
+void ABpBinder::addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) {
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+ mDeathRecipients.emplace_back(recipient, cookie);
+}
+
struct AIBinder_Weak {
wp<AIBinder> binder;
};
@@ -435,6 +454,17 @@
LOG_ALWAYS_FATAL_IF(onDied == nullptr, "onDied == nullptr");
}
+void AIBinder_DeathRecipient::pruneThisTransferEntry(const sp<IBinder>& who, void* cookie) {
+ std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
+ mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
+ [&](const sp<TransferDeathRecipient>& tdr) {
+ auto tdrWho = tdr->getWho();
+ return tdrWho != nullptr && tdrWho.promote() == who &&
+ cookie == tdr->getCookie();
+ }),
+ mDeathRecipients.end());
+}
+
void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
[](const sp<TransferDeathRecipient>& tdr) {
@@ -448,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);
@@ -563,8 +608,11 @@
return STATUS_UNEXPECTED_NULL;
}
- // returns binder_status_t
- return recipient->linkToDeath(binder->getBinder(), cookie);
+ binder_status_t ret = recipient->linkToDeath(binder->getBinder(), cookie);
+ if (ret == STATUS_OK) {
+ binder->addDeathRecipient(recipient, cookie);
+ }
+ return ret;
}
binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 9d5368f..f5b738c 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -51,6 +51,8 @@
::android::sp<::android::IBinder> binder = const_cast<AIBinder*>(this)->getBinder();
return binder->remoteBinder() != nullptr;
}
+ virtual void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) = 0;
private:
// AIBinder instance is instance of this class for a local object. In order to transact on a
@@ -78,6 +80,8 @@
::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override;
::android::status_t onTransact(uint32_t code, const ::android::Parcel& data,
::android::Parcel* reply, binder_flags_t flags) override;
+ void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& /* recipient */,
+ void* /* cookie */) override;
private:
ABBinder(const AIBinder_Class* clazz, void* userData);
@@ -106,12 +110,20 @@
bool isServiceFuzzing() const { return mServiceFuzzing; }
void setServiceFuzzing() { mServiceFuzzing = true; }
+ void addDeathRecipient(const ::android::sp<AIBinder_DeathRecipient>& recipient,
+ void* cookie) override;
private:
friend android::sp<ABpBinder>;
explicit ABpBinder(const ::android::sp<::android::IBinder>& binder);
::android::sp<::android::IBinder> mRemote;
bool mServiceFuzzing = false;
+ struct DeathRecipientInfo {
+ android::wp<AIBinder_DeathRecipient> recipient;
+ void* cookie;
+ };
+ std::mutex mDeathRecipientsMutex;
+ std::vector<DeathRecipientInfo> mDeathRecipients;
};
struct AIBinder_Class {
@@ -183,6 +195,7 @@
binder_status_t linkToDeath(const ::android::sp<::android::IBinder>&, void* cookie);
binder_status_t unlinkToDeath(const ::android::sp<::android::IBinder>& binder, void* cookie);
void setOnUnlinked(AIBinder_DeathRecipient_onBinderUnlinked onUnlinked);
+ void pruneThisTransferEntry(const ::android::sp<::android::IBinder>&, void* cookie);
private:
// When the user of this API deletes a Bp object but not the death recipient, the
diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
index d54d62e..c1d0e9f 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -36,13 +36,6 @@
namespace aidl::android::os {
-#if defined(__ANDROID_VENDOR__)
-#define AT_LEAST_V_OR_202404 constexpr(__ANDROID_VENDOR_API__ >= 202404)
-#else
-// TODO(b/322384429) switch this to __ANDROID_API_V__ when V is finalized
-#define AT_LEAST_V_OR_202404 (__builtin_available(android __ANDROID_API_FUTURE__, *))
-#endif
-
/**
* Wrapper class that enables interop with AIDL NDK generation
* Takes ownership of the APersistableBundle* given to it in reset() and will automatically
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h
index 52edae4..41b30a0 100644
--- a/libs/binder/ndk/include_platform/android/binder_manager.h
+++ b/libs/binder/ndk/include_platform/android/binder_manager.h
@@ -30,7 +30,11 @@
* Services with methods that perform file IO, web socket creation or ways to egress data must
* not be added with this flag for privacy concerns.
*/
- ADD_SERVICE_ALLOW_ISOLATED = 1,
+ ADD_SERVICE_ALLOW_ISOLATED = 1 << 0,
+ ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1,
+ ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2,
+ ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3,
+ ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4,
};
/**
diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp
index 259cced..d6ac4ac 100644
--- a/libs/binder/ndk/service_manager.cpp
+++ b/libs/binder/ndk/service_manager.cpp
@@ -50,7 +50,25 @@
sp<IServiceManager> sm = defaultServiceManager();
bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED;
- status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated);
+ int dumpFlags = 0;
+ if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL) {
+ dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL;
+ }
+ if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH) {
+ dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_HIGH;
+ }
+ if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL) {
+ dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_NORMAL;
+ }
+ if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) {
+ dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
+ }
+ if (dumpFlags == 0) {
+ dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT;
+ }
+ status_t exception =
+ sm->addService(String16(instance), binder->getBinder(), allowIsolated, dumpFlags);
+
return PruneException(exception);
}
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index a532ae1..08b857f 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -27,6 +27,7 @@
const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
+const char* IFoo::kInstanceNameToDieFor2 = "libbinder_ndk-test-IFoo-to-die2";
const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
struct IFoo_Class_Data {
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index 0a562f0..0cdd50b 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,6 +27,7 @@
public:
static const char* kSomeInstanceName;
static const char* kInstanceNameToDieFor;
+ static const char* kInstanceNameToDieFor2;
static const char* kIFooDescriptor;
static AIBinder_Class* kClass;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 2bc1422..f518a22 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -536,6 +536,7 @@
bool deathReceived = false;
std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
std::cerr << "Binder died (as requested)." << std::endl;
deathReceived = true;
deathCv.notify_one();
@@ -547,6 +548,7 @@
bool wasDeathReceivedFirst = false;
std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
std::cerr << "Binder unlinked (as requested)." << std::endl;
wasDeathReceivedFirst = deathReceived;
unlinkReceived = true;
@@ -560,7 +562,6 @@
EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(cookie)));
- // the binder driver should return this if the service dies during the transaction
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
foo = nullptr;
@@ -579,6 +580,173 @@
binder = nullptr;
}
+TEST(NdkBinder, DeathRecipientDropBinderNoDeath) {
+ using namespace std::chrono_literals;
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathReceived = false;
+
+ std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathReceived = true;
+ deathCv.notify_one();
+ };
+
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
+
+ std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ // keep the death recipient around
+ ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+ AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+ {
+ AIBinder* binder;
+ sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+
+ EXPECT_EQ(STATUS_OK,
+ AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+ // let the sp<IFoo> and AIBinder fall out of scope
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+ }
+
+ {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_FALSE(deathCv.wait_for(lockDeath, 100ms, [&] { return deathReceived; }));
+ EXPECT_FALSE(deathReceived);
+ }
+
+ {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 1s, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_FALSE(wasDeathReceivedFirst);
+ }
+}
+
+TEST(NdkBinder, DeathRecipientDropBinderOnDied) {
+ using namespace std::chrono_literals;
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathReceived = false;
+
+ sp<IFoo> foo;
+ AIBinder* binder;
+ std::function<void(void)> onDeath = [&] {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathReceived = true;
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+ deathCv.notify_one();
+ };
+
+ std::mutex unlinkMutex;
+ std::condition_variable unlinkCv;
+ bool unlinkReceived = false;
+ bool wasDeathReceivedFirst = false;
+
+ std::function<void(void)> onUnlink = [&] {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ std::cerr << "Binder unlinked (as requested)." << std::endl;
+ wasDeathReceivedFirst = deathReceived;
+ unlinkReceived = true;
+ unlinkCv.notify_one();
+ };
+
+ ndk::ScopedAIBinder_DeathRecipient recipient(AIBinder_DeathRecipient_new(LambdaOnDeath));
+ AIBinder_DeathRecipient_setOnUnlinked(recipient.get(), LambdaOnUnlink);
+
+ foo = IFoo::getService(IFoo::kInstanceNameToDieFor2, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ DeathRecipientCookie* cookie = new DeathRecipientCookie{&onDeath, &onUnlink};
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient.get(), static_cast<void*>(cookie)));
+
+ EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+
+ {
+ std::unique_lock<std::mutex> lockDeath(deathMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockDeath, 1s, [&] { return deathReceived; }));
+ EXPECT_TRUE(deathReceived);
+ }
+
+ {
+ std::unique_lock<std::mutex> lockUnlink(unlinkMutex);
+ EXPECT_TRUE(deathCv.wait_for(lockUnlink, 100ms, [&] { return unlinkReceived; }));
+ EXPECT_TRUE(unlinkReceived);
+ EXPECT_TRUE(wasDeathReceivedFirst);
+ }
+}
+
+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 (uint32_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, 0u);
+}
+
TEST(NdkBinder, RetrieveNonNdkService) {
LIBBINDER_IGNORE("-Wdeprecated-declarations")
AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
@@ -943,6 +1111,10 @@
}
if (fork() == 0) {
prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualThreadPoolService(IFoo::kInstanceNameToDieFor2);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
return manualPollingService(IFoo::kSomeInstanceName);
}
if (fork() == 0) {
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index 5c280f4..e378b86 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -115,6 +115,14 @@
p.setDataPosition(pos);
FUZZ_LOG() << "setDataPosition done";
},
+ [] (const ::android::Parcel& p, FuzzedDataProvider& provider) {
+ size_t len = provider.ConsumeIntegralInRange<size_t>(0, 1024);
+ std::vector<uint8_t> bytes = provider.ConsumeBytes<uint8_t>(len);
+ FUZZ_LOG() << "about to setData: " <<(bytes.data() ? HexString(bytes.data(), bytes.size()) : "null");
+ // TODO: allow all read and write operations
+ (*const_cast<::android::Parcel*>(&p)).setData(bytes.data(), bytes.size());
+ FUZZ_LOG() << "setData done";
+ },
PARCEL_READ_NO_STATUS(size_t, allowFds),
PARCEL_READ_NO_STATUS(size_t, hasFileDescriptors),
PARCEL_READ_NO_STATUS(std::vector<android::sp<android::IBinder>>, debugReadAllStrongBinders),
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/ftl/Android.bp b/libs/ftl/Android.bp
index 32b2b68..368f5e0 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",
@@ -40,5 +41,6 @@
"-Wextra",
"-Wpedantic",
"-Wthread-safety",
+ "-Wno-gnu-statement-expression-from-macro-expansion",
],
}
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/expected_test.cpp b/libs/ftl/expected_test.cpp
index 8cb07e4..d5b1d7e 100644
--- a/libs/ftl/expected_test.cpp
+++ b/libs/ftl/expected_test.cpp
@@ -15,8 +15,11 @@
*/
#include <ftl/expected.h>
+#include <ftl/optional.h>
+#include <ftl/unit.h>
#include <gtest/gtest.h>
+#include <cctype>
#include <string>
#include <system_error>
@@ -74,4 +77,69 @@
}
}
+namespace {
+
+IntExp increment_try(IntExp exp) {
+ const int i = FTL_TRY(exp);
+ return IntExp(i + 1);
+}
+
+std::errc increment_expect(IntExp exp, int& out) {
+ const int i = FTL_EXPECT(exp);
+ out = i + 1;
+ return std::errc::operation_in_progress;
+}
+
+StringExp repeat_try(StringExp exp) {
+ const std::string str = FTL_TRY(exp);
+ return StringExp(str + str);
+}
+
+std::errc repeat_expect(StringExp exp, std::string& out) {
+ const std::string str = FTL_EXPECT(exp);
+ out = str + str;
+ return std::errc::operation_in_progress;
+}
+
+void uppercase(char& c, ftl::Optional<char> opt) {
+ c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit())));
+}
+
+} // namespace
+
+// Keep in sync with example usage in header file.
+TEST(Expected, Try) {
+ EXPECT_EQ(IntExp(100), increment_try(IntExp(99)));
+ EXPECT_TRUE(increment_try(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) {
+ return e == std::errc::value_too_large;
+ }));
+
+ EXPECT_EQ(StringExp("haha"s), repeat_try(StringExp("ha"s)));
+ EXPECT_TRUE(repeat_try(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) {
+ return e == std::errc::bad_message;
+ }));
+
+ char c = '?';
+ uppercase(c, std::nullopt);
+ EXPECT_EQ(c, '?');
+
+ uppercase(c, 'a');
+ EXPECT_EQ(c, 'A');
+}
+
+TEST(Expected, Expect) {
+ int i = 0;
+ EXPECT_EQ(std::errc::operation_in_progress, increment_expect(IntExp(99), i));
+ EXPECT_EQ(100, i);
+ EXPECT_EQ(std::errc::value_too_large,
+ increment_expect(ftl::Unexpected(std::errc::value_too_large), i));
+ EXPECT_EQ(100, i);
+
+ std::string str;
+ EXPECT_EQ(std::errc::operation_in_progress, repeat_expect(StringExp("ha"s), str));
+ EXPECT_EQ("haha"s, str);
+ EXPECT_EQ(std::errc::bad_message, repeat_expect(ftl::Unexpected(std::errc::bad_message), str));
+ EXPECT_EQ("haha"s, str);
+}
+
} // namespace android::test
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/ftl/optional_test.cpp b/libs/ftl/optional_test.cpp
index 91bf7bc..e7f1f14 100644
--- a/libs/ftl/optional_test.cpp
+++ b/libs/ftl/optional_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <ftl/expected.h>
#include <ftl/optional.h>
#include <ftl/static_vector.h>
#include <ftl/string.h>
@@ -23,6 +24,7 @@
#include <cstdlib>
#include <functional>
#include <numeric>
+#include <system_error>
#include <utility>
using namespace std::placeholders;
@@ -204,6 +206,19 @@
.or_else([] { return Optional(-1); }));
}
+TEST(Optional, OkOr) {
+ using CharExp = ftl::Expected<char, std::errc>;
+ using StringExp = ftl::Expected<std::string, std::errc>;
+
+ EXPECT_EQ(CharExp('z'), Optional('z').ok_or(std::errc::broken_pipe));
+ EXPECT_EQ(CharExp(ftl::Unexpected(std::errc::broken_pipe)),
+ Optional<char>().ok_or(std::errc::broken_pipe));
+
+ EXPECT_EQ(StringExp("abc"s), Optional("abc"s).ok_or(std::errc::protocol_error));
+ EXPECT_EQ(StringExp(ftl::Unexpected(std::errc::protocol_error)),
+ Optional<std::string>().ok_or(std::errc::protocol_error));
+}
+
// Comparison.
namespace {
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 7dcbbc0..51d2e53 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -41,6 +41,11 @@
aconfig_declarations: "libgui_flags",
}
+cc_aconfig_library {
+ name: "libguiflags_no_apex",
+ aconfig_declarations: "libgui_flags",
+}
+
cc_library_headers {
name: "libgui_headers",
vendor_available: true,
@@ -87,6 +92,7 @@
"android/gui/DropInputMode.aidl",
"android/gui/StalledTransactionInfo.aidl",
"android/**/TouchOcclusionMode.aidl",
+ "android/gui/TrustedOverlay.aidl",
],
}
@@ -155,9 +161,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 +176,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 +270,6 @@
"IProducerListener.cpp",
"ISurfaceComposer.cpp",
"ITransactionCompletedListener.cpp",
- "LayerDebugInfo.cpp",
"LayerMetadata.cpp",
"LayerStatePermissions.cpp",
"LayerState.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index f317a2e..739c3c2 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -613,8 +613,19 @@
std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
+
+ nsecs_t dequeueTime = -1;
+ {
+ std::lock_guard _lock{mTimestampMutex};
+ auto dequeueTimeIt = mDequeueTimestamps.find(buffer->getId());
+ if (dequeueTimeIt != mDequeueTimestamps.end()) {
+ dequeueTime = dequeueTimeIt->second;
+ mDequeueTimestamps.erase(dequeueTimeIt);
+ }
+ }
+
t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, mProducerId,
- releaseBufferCallback);
+ releaseBufferCallback, dequeueTime);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
@@ -658,17 +669,6 @@
mPendingFrameTimelines.pop();
}
- {
- std::lock_guard _lock{mTimestampMutex};
- auto dequeueTime = mDequeueTimestamps.find(buffer->getId());
- if (dequeueTime != mDequeueTimestamps.end()) {
- Parcel p;
- p.writeInt64(dequeueTime->second);
- t->setMetadata(mSurfaceControl, gui::METADATA_DEQUEUE_TIME, p);
- mDequeueTimestamps.erase(dequeueTime);
- }
- }
-
mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
// All transactions on our apply token are one-way. See comment on mAppliedLastTransaction
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 88d456b..a4d105d 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();
}
@@ -1345,6 +1381,9 @@
}
mCore->mAllowAllocation = true;
+#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_EXTENDEDALLOCATE)
+ mCore->mAdditionalOptions.clear();
+#endif
VALIDATE_CONSISTENCY();
return status;
}
@@ -1413,6 +1452,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);
@@ -1465,6 +1507,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);
@@ -1493,14 +1539,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();
@@ -1527,8 +1601,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;
@@ -1546,6 +1624,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.
@@ -1781,4 +1862,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/Choreographer.cpp b/libs/gui/Choreographer.cpp
index 4518b67..0c8f3fa 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -143,9 +143,9 @@
void Choreographer::postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay) {
+ nsecs_t delay, CallbackType callbackType) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay};
+ FrameCallback callback{cb, cb64, vsyncCallback, data, now + delay, callbackType};
{
std::lock_guard<std::mutex> _l{mLock};
mFrameCallbacks.push(callback);
@@ -285,18 +285,8 @@
}
}
-void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
- VsyncEventData vsyncEventData) {
- std::vector<FrameCallback> callbacks{};
- {
- std::lock_guard<std::mutex> _l{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
- callbacks.push_back(mFrameCallbacks.top());
- mFrameCallbacks.pop();
- }
- }
- mLastVsyncEventData = vsyncEventData;
+void Choreographer::dispatchCallbacks(const std::vector<FrameCallback>& callbacks,
+ VsyncEventData vsyncEventData, nsecs_t timestamp) {
for (const auto& cb : callbacks) {
if (cb.vsyncCallback != nullptr) {
ATRACE_FORMAT("AChoreographer_vsyncCallback %" PRId64,
@@ -319,6 +309,34 @@
}
}
+void Choreographer::dispatchVsync(nsecs_t timestamp, PhysicalDisplayId, uint32_t,
+ VsyncEventData vsyncEventData) {
+ std::vector<FrameCallback> animationCallbacks{};
+ std::vector<FrameCallback> inputCallbacks{};
+ {
+ std::lock_guard<std::mutex> _l{mLock};
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ while (!mFrameCallbacks.empty() && mFrameCallbacks.top().dueTime < now) {
+ if (mFrameCallbacks.top().callbackType == CALLBACK_INPUT) {
+ inputCallbacks.push_back(mFrameCallbacks.top());
+ } else {
+ animationCallbacks.push_back(mFrameCallbacks.top());
+ }
+ mFrameCallbacks.pop();
+ }
+ }
+ mLastVsyncEventData = vsyncEventData;
+ // Callbacks with type CALLBACK_INPUT should always run first
+ {
+ ATRACE_FORMAT("CALLBACK_INPUT");
+ dispatchCallbacks(inputCallbacks, vsyncEventData, timestamp);
+ }
+ {
+ ATRACE_FORMAT("CALLBACK_ANIMATION");
+ dispatchCallbacks(animationCallbacks, vsyncEventData, timestamp);
+ }
+}
+
void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool connected) {
ALOGV("choreographer %p ~ received hotplug event (displayId=%s, connected=%s), ignoring.", this,
to_string(displayId).c_str(), toString(connected));
@@ -407,4 +425,8 @@
return iter->second;
}
+const sp<Looper> Choreographer::getLooper() {
+ return mLooper;
+}
+
} // namespace android
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index f3de96d..c46f9c5 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "DisplayEventDispatcher"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <cinttypes>
#include <cstdint>
@@ -23,10 +24,13 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
#include <utils/Looper.h>
-
#include <utils/Timers.h>
+#include <utils/Trace.h>
+
+#include <com_android_graphics_libgui_flags.h>
namespace android {
+using namespace com::android::graphics::libgui;
// Number of events to read at a time from the DisplayEventDispatcher pipe.
// The value should be large enough that we can quickly drain the pipe
@@ -171,6 +175,13 @@
*outDisplayId = ev.header.displayId;
*outCount = ev.vsync.count;
*outVsyncEventData = ev.vsync.vsyncData;
+
+ // Trace the RenderRate for this app
+ if (ATRACE_ENABLED() && flags::trace_frame_rate_override()) {
+ const auto frameInterval = ev.vsync.vsyncData.frameInterval;
+ int fps = frameInterval > 0 ? 1e9f / frameInterval : 0;
+ ATRACE_INT("RenderRate", fps);
+ }
break;
case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
if (ev.hotplug.connectionError == 0) {
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/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 4e12fd3..535a021 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -100,27 +100,31 @@
int32_t LayerMetadata::getInt32(uint32_t key, int32_t fallback) const {
if (!has(key)) return fallback;
const std::vector<uint8_t>& data = mMap.at(key);
- if (data.size() < sizeof(uint32_t)) return fallback;
- Parcel p;
- p.setData(data.data(), data.size());
- return p.readInt32();
+
+ // TODO: should handle when not equal?
+ if (data.size() < sizeof(int32_t)) return fallback;
+
+ int32_t result;
+ memcpy(&result, data.data(), sizeof(result));
+ return result;
}
void LayerMetadata::setInt32(uint32_t key, int32_t value) {
std::vector<uint8_t>& data = mMap[key];
- Parcel p;
- p.writeInt32(value);
- data.resize(p.dataSize());
- memcpy(data.data(), p.data(), p.dataSize());
+ data.resize(sizeof(value));
+ memcpy(data.data(), &value, sizeof(value));
}
std::optional<int64_t> LayerMetadata::getInt64(uint32_t key) const {
if (!has(key)) return std::nullopt;
const std::vector<uint8_t>& data = mMap.at(key);
+
+ // TODO: should handle when not equal?
if (data.size() < sizeof(int64_t)) return std::nullopt;
- Parcel p;
- p.setData(data.data(), data.size());
- return p.readInt64();
+
+ int64_t result;
+ memcpy(&result, data.data(), sizeof(result));
+ return result;
}
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 1e0aacd..3745805 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -89,8 +89,7 @@
frameRateSelectionStrategy(ANATIVEWINDOW_FRAME_RATE_SELECTION_STRATEGY_PROPAGATE),
fixedTransformHint(ui::Transform::ROT_INVALID),
autoRefresh(false),
- isTrustedOverlay(false),
- borderEnabled(false),
+ trustedOverlay(gui::TrustedOverlay::UNSET),
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);
@@ -186,7 +179,7 @@
SAFE_PARCEL(output.write, stretchEffect);
SAFE_PARCEL(output.write, bufferCrop);
SAFE_PARCEL(output.write, destinationFrame);
- SAFE_PARCEL(output.writeBool, isTrustedOverlay);
+ SAFE_PARCEL(output.writeInt32, static_cast<uint32_t>(trustedOverlay));
SAFE_PARCEL(output.writeUint32, static_cast<uint32_t>(dropInputMode));
@@ -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);
@@ -326,7 +308,9 @@
SAFE_PARCEL(input.read, stretchEffect);
SAFE_PARCEL(input.read, bufferCrop);
SAFE_PARCEL(input.read, destinationFrame);
- SAFE_PARCEL(input.readBool, &isTrustedOverlay);
+ uint32_t trustedOverlayInt;
+ SAFE_PARCEL(input.readUint32, &trustedOverlayInt);
+ trustedOverlay = static_cast<gui::TrustedOverlay>(trustedOverlayInt);
uint32_t mode;
SAFE_PARCEL(input.readUint32, &mode);
@@ -659,12 +643,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;
@@ -698,7 +676,7 @@
}
if (other.what & eTrustedOverlayChanged) {
what |= eTrustedOverlayChanged;
- isTrustedOverlay = other.isTrustedOverlay;
+ trustedOverlay = other.trustedOverlay;
}
if (other.what & eStretchChanged) {
what |= eStretchChanged;
@@ -794,7 +772,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,
@@ -804,7 +781,7 @@
CHECK_DIFF(diff, eFrameRateSelectionStrategyChanged, other, frameRateSelectionStrategy);
CHECK_DIFF(diff, eFixedTransformHintChanged, other, fixedTransformHint);
CHECK_DIFF(diff, eAutoRefreshChanged, other, autoRefresh);
- CHECK_DIFF(diff, eTrustedOverlayChanged, other, isTrustedOverlay);
+ CHECK_DIFF(diff, eTrustedOverlayChanged, other, trustedOverlay);
CHECK_DIFF(diff, eStretchChanged, other, stretchEffect);
CHECK_DIFF(diff, eBufferCropChanged, other, bufferCrop);
CHECK_DIFF(diff, eDestinationFrameChanged, other, destinationFrame);
@@ -1008,6 +985,7 @@
SAFE_PARCEL(output->writeBool, hasBarrier);
SAFE_PARCEL(output->writeUint64, barrierFrameNumber);
SAFE_PARCEL(output->writeUint32, producerId);
+ SAFE_PARCEL(output->writeInt64, dequeueTime);
return NO_ERROR;
}
@@ -1047,6 +1025,7 @@
SAFE_PARCEL(input->readBool, &hasBarrier);
SAFE_PARCEL(input->readUint64, &barrierFrameNumber);
SAFE_PARCEL(input->readUint32, &producerId);
+ SAFE_PARCEL(input->readInt64, &dequeueTime);
return NO_ERROR;
}
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 7f64a04..af91bb3 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -89,6 +89,8 @@
void emptyCallback(nsecs_t, const sp<Fence>&, const std::vector<SurfaceControlStats>&) {}
} // namespace
+const std::string SurfaceComposerClient::kEmpty{};
+
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
@@ -706,6 +708,7 @@
SurfaceComposerClient::Transaction::Transaction() {
mId = generateId();
+ mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
SurfaceComposerClient::Transaction::Transaction(const Transaction& other)
@@ -723,6 +726,7 @@
mComposerStates = other.mComposerStates;
mInputWindowCommands = other.mInputWindowCommands;
mListenerCallbacks = other.mListenerCallbacks;
+ mTransactionCompletedListener = TransactionCompletedListener::getInstance();
}
void SurfaceComposerClient::Transaction::sanitize(int pid, int uid) {
@@ -1000,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);
}
}
@@ -1276,19 +1280,22 @@
}
// ---------------------------------------------------------------------------
-sp<IBinder> SurfaceComposerClient::createDisplay(const String8& displayName, bool secure,
- float requestedRefereshRate) {
+sp<IBinder> SurfaceComposerClient::createVirtualDisplay(const std::string& displayName,
+ bool isSecure, const std::string& uniqueId,
+ float requestedRefreshRate) {
sp<IBinder> display = nullptr;
binder::Status status =
- ComposerServiceAIDL::getComposerService()->createDisplay(std::string(
- displayName.c_str()),
- secure, requestedRefereshRate,
- &display);
+ ComposerServiceAIDL::getComposerService()->createVirtualDisplay(displayName, isSecure,
+ uniqueId,
+ requestedRefreshRate,
+ &display);
return status.isOk() ? display : nullptr;
}
-void SurfaceComposerClient::destroyDisplay(const sp<IBinder>& display) {
- ComposerServiceAIDL::getComposerService()->destroyDisplay(display);
+status_t SurfaceComposerClient::destroyVirtualDisplay(const sp<IBinder>& displayToken) {
+ return ComposerServiceAIDL::getComposerService()
+ ->destroyVirtualDisplay(displayToken)
+ .transactionError();
}
std::vector<PhysicalDisplayId> SurfaceComposerClient::getPhysicalDisplayIds() {
@@ -1354,7 +1361,7 @@
auto& callbackInfo = mListenerCallbacks[TransactionCompletedListener::getIInstance()];
callbackInfo.surfaceControls.insert(sc);
- TransactionCompletedListener::getInstance()->addSurfaceControlToCallbacks(callbackInfo, sc);
+ mTransactionCompletedListener->addSurfaceControlToCallbacks(callbackInfo, sc);
}
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
@@ -1672,7 +1679,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;
@@ -1695,7 +1702,7 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& optFrameNumber,
- uint32_t producerId, ReleaseBufferCallback callback) {
+ uint32_t producerId, ReleaseBufferCallback callback, nsecs_t dequeueTime) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -1711,12 +1718,12 @@
bufferData->frameNumber = frameNumber;
bufferData->producerId = producerId;
bufferData->flags |= BufferData::BufferDataChange::frameNumberChanged;
+ bufferData->dequeueTime = dequeueTime;
if (fence) {
bufferData->acquireFence = *fence;
bufferData->flags |= BufferData::BufferDataChange::fenceChanged;
}
- bufferData->releaseBufferEndpoint =
- IInterface::asBinder(TransactionCompletedListener::getIInstance());
+ bufferData->releaseBufferEndpoint = IInterface::asBinder(mTransactionCompletedListener);
setReleaseBufferCallback(bufferData.get(), callback);
}
@@ -1774,9 +1781,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(
@@ -1932,18 +1940,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;
}
@@ -2175,6 +2180,13 @@
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrustedOverlay(
const sp<SurfaceControl>& sc, bool isTrustedOverlay) {
+ return setTrustedOverlay(sc,
+ isTrustedOverlay ? gui::TrustedOverlay::ENABLED
+ : gui::TrustedOverlay::UNSET);
+}
+
+SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setTrustedOverlay(
+ const sp<SurfaceControl>& sc, gui::TrustedOverlay trustedOverlay) {
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
@@ -2182,7 +2194,7 @@
}
s->what |= layer_state_t::eTrustedOverlayChanged;
- s->isTrustedOverlay = isTrustedOverlay;
+ s->trustedOverlay = trustedOverlay;
return *this;
}
@@ -2250,23 +2262,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) {
@@ -2350,8 +2345,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) {
@@ -2368,8 +2364,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) {
@@ -3131,6 +3126,10 @@
->removeWindowInfosListener(windowInfosListener,
ComposerServiceAIDL::getComposerService());
}
+
+void SurfaceComposerClient::notifyShutdown() {
+ ComposerServiceAIDL::getComposerService()->notifyShutdown();
+}
// ----------------------------------------------------------------------------
status_t ScreenshotClient::captureDisplay(const DisplayCaptureArgs& captureArgs,
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 86bf0ee..82d2554 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -73,14 +73,6 @@
touchableRegion.orSelf(region);
}
-bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const {
- return touchableRegion.contains(x, y);
-}
-
-bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const {
- return x >= frame.left && x < frame.right && y >= frame.top && y < frame.bottom;
-}
-
bool WindowInfo::supportsSplitTouch() const {
return !inputConfig.test(InputConfig::PREVENT_SPLITTING);
}
@@ -154,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) ?:
@@ -183,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
@@ -206,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) ?:
@@ -229,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 51e0193..6d018ea 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;
@@ -73,7 +72,7 @@
void bootFinished();
/**
- * Create a display event connection
+ * Create a display event connection.
*
* layerHandle
* Optional binder handle representing a Layer in SF to associate the new
@@ -90,12 +89,14 @@
@nullable ISurfaceComposerClient createConnection();
/**
- * Create a virtual display
+ * Create a virtual display.
*
* displayName
- * The name of the virtual display
- * secure
- * Whether this virtual display is secure
+ * The name of the virtual display.
+ * isSecure
+ * Whether this virtual display is secure.
+ * uniqueId
+ * The unique ID for the display.
* requestedRefreshRate
* The refresh rate, frames per second, to request on the virtual display.
* This is just a request, the actual rate may be adjusted to align well
@@ -104,14 +105,14 @@
*
* requires ACCESS_SURFACE_FLINGER permission.
*/
- @nullable IBinder createDisplay(@utf8InCpp String displayName, boolean secure,
- float requestedRefreshRate);
+ @nullable IBinder createVirtualDisplay(@utf8InCpp String displayName, boolean isSecure,
+ @utf8InCpp String uniqueId, float requestedRefreshRate);
/**
- * Destroy a virtual display
+ * Destroy a virtual display.
* requires ACCESS_SURFACE_FLINGER permission.
*/
- void destroyDisplay(IBinder display);
+ void destroyVirtualDisplay(IBinder displayToken);
/**
* Get stable IDs for connected physical displays.
@@ -289,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
@@ -579,4 +573,11 @@
@nullable StalledTransactionInfo getStalledTransactionInfo(int pid);
SchedulingPolicy getSchedulingPolicy();
+
+ /**
+ * Notifies the SurfaceFlinger that the ShutdownThread is running. When it is called,
+ * transaction traces will be captured and writted into a file.
+ * This method should not block the ShutdownThread therefore it's handled asynchronously.
+ */
+ oneway void notifyShutdown();
}
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/TrustedOverlay.aidl b/libs/gui/android/gui/TrustedOverlay.aidl
new file mode 100644
index 0000000..06fb5f0
--- /dev/null
+++ b/libs/gui/android/gui/TrustedOverlay.aidl
@@ -0,0 +1,45 @@
+/**
+ * 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.gui;
+
+
+/**
+ * Trusted overlay state prevents layers from being considered as obscuring for
+ * input occlusion detection purposes.
+ *
+ * @hide
+ */
+@Backing(type="int")
+enum TrustedOverlay {
+ /**
+ * The default, layer will inherit the state from its parents. If the parent state is also
+ * unset, the layer will be considered as untrusted.
+ */
+ UNSET,
+
+ /**
+ * Treats this layer and all its children as an untrusted overlay. This will override any
+ * state set by its parent layers.
+ */
+ DISABLED,
+
+ /**
+ * Treats this layer and all its children as a trusted overlay unless the child layer
+ * explicitly disables its trusted state.
+ */
+ ENABLED
+}
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 c16e370..d5dd7c8 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>
@@ -361,6 +364,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/Choreographer.h b/libs/gui/include/gui/Choreographer.h
index 55a7aa7..2e5aa4a 100644
--- a/libs/gui/include/gui/Choreographer.h
+++ b/libs/gui/include/gui/Choreographer.h
@@ -28,12 +28,18 @@
namespace android {
using gui::VsyncEventData;
+enum CallbackType : int8_t {
+ CALLBACK_INPUT,
+ CALLBACK_ANIMATION,
+};
+
struct FrameCallback {
AChoreographer_frameCallback callback;
AChoreographer_frameCallback64 callback64;
AChoreographer_vsyncCallback vsyncCallback;
void* data;
nsecs_t dueTime;
+ CallbackType callbackType;
inline bool operator<(const FrameCallback& rhs) const {
// Note that this is intentionally flipped because we want callbacks due sooner to be at
@@ -78,7 +84,7 @@
void postFrameCallbackDelayed(AChoreographer_frameCallback cb,
AChoreographer_frameCallback64 cb64,
AChoreographer_vsyncCallback vsyncCallback, void* data,
- nsecs_t delay);
+ nsecs_t delay, CallbackType callbackType);
void registerRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data)
EXCLUDES(gChoreographers.lock);
void unregisterRefreshRateCallback(AChoreographer_refreshRateCallback cb, void* data);
@@ -103,12 +109,15 @@
virtual ~Choreographer() override EXCLUDES(gChoreographers.lock);
int64_t getFrameInterval() const;
bool inCallback() const;
+ const sp<Looper> getLooper();
private:
Choreographer(const Choreographer&) = delete;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
VsyncEventData vsyncEventData) override;
+ void dispatchCallbacks(const std::vector<FrameCallback>&, VsyncEventData vsyncEventData,
+ nsecs_t timestamp);
void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override;
void dispatchHotplugConnectionError(nsecs_t timestamp, int32_t connectionError) override;
void dispatchModeChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t modeId,
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..7094658 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::LogicalDisplayId::INVALID;
// 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..eb4a802 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
@@ -131,8 +130,8 @@
CREATE_CONNECTION, // Deprecated. Autogenerated by .aidl now.
GET_STATIC_DISPLAY_INFO, // Deprecated. Autogenerated by .aidl now.
CREATE_DISPLAY_EVENT_CONNECTION, // Deprecated. Autogenerated by .aidl now.
- CREATE_DISPLAY, // Deprecated. Autogenerated by .aidl now.
- DESTROY_DISPLAY, // Deprecated. Autogenerated by .aidl now.
+ CREATE_VIRTUAL_DISPLAY, // Deprecated. Autogenerated by .aidl now.
+ DESTROY_VIRTUAL_DISPLAY, // Deprecated. Autogenerated by .aidl now.
GET_PHYSICAL_DISPLAY_TOKEN, // Deprecated. Autogenerated by .aidl now.
SET_TRANSACTION_STATE,
AUTHENTICATE_SURFACE, // Deprecated. Autogenerated by .aidl now.
diff --git a/libs/gui/include/gui/InputTransferToken.h b/libs/gui/include/gui/InputTransferToken.h
new file mode 100644
index 0000000..6530b50
--- /dev/null
+++ b/libs/gui/include/gui/InputTransferToken.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/Parcel.h>
+#include <private/gui/ParcelUtils.h>
+#include <utils/Errors.h>
+
+namespace android {
+struct InputTransferToken : public RefBase, Parcelable {
+public:
+ InputTransferToken() { mToken = new BBinder(); }
+
+ InputTransferToken(const sp<IBinder>& token) { mToken = token; }
+
+ status_t writeToParcel(Parcel* parcel) const override {
+ SAFE_PARCEL(parcel->writeStrongBinder, mToken);
+ return NO_ERROR;
+ }
+
+ status_t readFromParcel(const Parcel* parcel) {
+ SAFE_PARCEL(parcel->readStrongBinder, &mToken);
+ return NO_ERROR;
+ };
+
+ sp<IBinder> mToken;
+};
+
+static inline bool operator==(const sp<InputTransferToken>& token1,
+ const sp<InputTransferToken>& token2) {
+ if (token1.get() == token2.get()) {
+ return true;
+ }
+ return token1->mToken == token2->mToken;
+}
+
+} // namespace android
\ No newline at end of file
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..5f2f8dc 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -30,6 +30,7 @@
#include <android/gui/DropInputMode.h>
#include <android/gui/FocusRequest.h>
+#include <android/gui/TrustedOverlay.h>
#include <ftl/flags.h>
#include <gui/DisplayCaptureArgs.h>
@@ -127,6 +128,8 @@
client_cache_t cachedBuffer;
+ nsecs_t dequeueTime;
+
// Generates the release callback id based on the buffer id and frame number.
// This is used as an identifier when release callbacks are invoked.
ReleaseCallbackId generateReleaseCallbackId() const;
@@ -179,7 +182,6 @@
eCachingHintChanged = 0x00000200,
eDimmingEnabledChanged = 0x00000400,
eShadowRadiusChanged = 0x00000800,
- eRenderBorderChanged = 0x00001000,
eBufferCropChanged = 0x00002000,
eRelativeLayerChanged = 0x00004000,
eReparent = 0x00008000,
@@ -258,8 +260,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 |
@@ -276,9 +278,9 @@
layer_state_t::eFrameRateSelectionPriority | layer_state_t::eFixedTransformHintChanged;
// Changes affecting data sent to input.
- static constexpr uint64_t INPUT_CHANGES = layer_state_t::eInputInfoChanged |
- layer_state_t::eDropInputModeChanged | layer_state_t::eTrustedOverlayChanged |
- layer_state_t::eLayerStackChanged;
+ static constexpr uint64_t INPUT_CHANGES = layer_state_t::eAlphaChanged |
+ layer_state_t::eInputInfoChanged | layer_state_t::eDropInputModeChanged |
+ layer_state_t::eTrustedOverlayChanged | layer_state_t::eLayerStackChanged;
// Changes that affect the visible region on a display.
static constexpr uint64_t VISIBLE_REGION_CHANGES = layer_state_t::GEOMETRY_CHANGES |
@@ -386,12 +388,7 @@
// An inherited state that indicates that this surface control and its children
// 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;
+ gui::TrustedOverlay trustedOverlay;
// 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 2888826..0862e03 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <sys/types.h>
+
#include <set>
#include <thread>
#include <unordered_map>
@@ -374,17 +375,15 @@
sp<SurfaceControl> mirrorDisplay(DisplayId displayId);
- //! Create a virtual display
- static sp<IBinder> createDisplay(const String8& displayName, bool secure,
- float requestedRefereshRate = 0);
+ static const std::string kEmpty;
+ static sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+ const std::string& uniqueId = kEmpty,
+ float requestedRefreshRate = 0);
- //! Destroy a virtual display
- static void destroyDisplay(const sp<IBinder>& display);
+ static status_t destroyVirtualDisplay(const sp<IBinder>& displayToken);
- //! Get stable IDs for connected physical displays
static std::vector<PhysicalDisplayId> getPhysicalDisplayIds();
- //! Get token for a physical display given its stable ID
static sp<IBinder> getPhysicalDisplayToken(PhysicalDisplayId displayId);
// Returns StalledTransactionInfo if a transaction from the provided pid has not been applied
@@ -431,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;
@@ -567,7 +568,8 @@
Transaction& setBuffer(const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence = std::nullopt,
const std::optional<uint64_t>& frameNumber = std::nullopt,
- uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr);
+ uint32_t producerId = 0, ReleaseBufferCallback callback = nullptr,
+ nsecs_t dequeueTime = -1);
Transaction& unsetBuffer(const sp<SurfaceControl>& sc);
std::shared_ptr<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
@@ -718,6 +720,8 @@
// Sets that this surface control and its children are trusted overlays for input
Transaction& setTrustedOverlay(const sp<SurfaceControl>& sc, bool isTrustedOverlay);
+ Transaction& setTrustedOverlay(const sp<SurfaceControl>& sc,
+ gui::TrustedOverlay trustedOverlay);
// Queues up transactions using this token in SurfaceFlinger. By default, all transactions
// from a client are placed on the same queue. This can be used to prevent multiple
@@ -744,9 +748,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);
@@ -826,6 +827,8 @@
nullptr);
status_t removeWindowInfosListener(const sp<gui::WindowInfosListener>& windowInfosListener);
+ static void notifyShutdown();
+
protected:
ReleaseCallbackThread mReleaseCallbackThread;
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 32d60be..eb3be55 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::LogicalDisplayId::INVALID;
InputApplicationInfo applicationInfo;
bool replaceTouchableRegionWithCrop = false;
wp<IBinder> touchableRegionCropHandle;
@@ -254,10 +256,6 @@
void addTouchableRegion(const Rect& region);
- bool touchableRegionContainsPoint(int32_t x, int32_t y) const;
-
- bool frameContainsPoint(int32_t x, int32_t y) const;
-
bool supportsSplitTouch() const;
bool isSpy() const;
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 78fc590..87cef08 100644
--- a/libs/gui/libgui_flags.aconfig
+++ b/libs/gui/libgui_flags.aconfig
@@ -7,7 +7,7 @@
description: "This flag controls plumbing setFrameRate thru BufferQueue"
bug: "281695725"
is_fixed_read_only: true
-}
+} # bq_setframerate
flag {
name: "bq_consumer_attach_callback"
@@ -23,4 +23,23 @@
description: "Controls a fence fixup for timestamp apis"
bug: "310927247"
is_fixed_read_only: true
-}
+} # frametimestamps_previousrelease
+
+flag {
+ name: "bq_extendedallocate"
+ namespace: "core_graphics"
+ description: "Add BQ support for allocate with extended options"
+ bug: "268382490"
+ is_fixed_read_only: true
+} # bq_extendedallocate
+
+flag {
+ name: "trace_frame_rate_override"
+ namespace: "core_graphics"
+ description: "Trace FrameRateOverride fps"
+ bug: "347314033"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # trace_frame_rate_override
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 e606b99..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: [
@@ -30,6 +32,7 @@
"BLASTBufferQueue_test.cpp",
"BufferItemConsumer_test.cpp",
"BufferQueue_test.cpp",
+ "Choreographer_test.cpp",
"CompositorTiming_test.cpp",
"CpuConsumer_test.cpp",
"EndToEndNativeInputTest.cpp",
@@ -61,6 +64,7 @@
"libSurfaceFlingerProp",
"libGLESv1_CM",
"libinput",
+ "libnativedisplay",
],
static_libs: [
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 be6c7d6..590e2c8 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.
@@ -1525,4 +1530,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/Choreographer_test.cpp b/libs/gui/tests/Choreographer_test.cpp
new file mode 100644
index 0000000..2ac2550
--- /dev/null
+++ b/libs/gui/tests/Choreographer_test.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Choreographer_test"
+
+#include <android-base/stringprintf.h>
+#include <android/choreographer.h>
+#include <gtest/gtest.h>
+#include <gui/Choreographer.h>
+#include <utils/Looper.h>
+#include <chrono>
+#include <future>
+#include <string>
+
+namespace android {
+class ChoreographerTest : public ::testing::Test {};
+
+struct VsyncCallback {
+ std::atomic<bool> completePromise{false};
+ std::chrono::nanoseconds frameTime{0LL};
+ std::chrono::nanoseconds receivedCallbackTime{0LL};
+
+ void onVsyncCallback(const AChoreographerFrameCallbackData* callbackData) {
+ frameTime = std::chrono::nanoseconds{
+ AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData)};
+ receivedCallbackTime = std::chrono::nanoseconds{systemTime(SYSTEM_TIME_MONOTONIC)};
+ completePromise.store(true);
+ }
+
+ bool callbackReceived() { return completePromise.load(); }
+};
+
+static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
+ VsyncCallback* cb = static_cast<VsyncCallback*>(data);
+ cb->onVsyncCallback(callbackData);
+}
+
+TEST_F(ChoreographerTest, InputCallbackBeforeAnimation) {
+ sp<Looper> looper = Looper::prepare(0);
+ Choreographer* choreographer = Choreographer::getForThread();
+ VsyncCallback animationCb;
+ VsyncCallback inputCb;
+
+ choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &animationCb, 0,
+ CALLBACK_ANIMATION);
+ choreographer->postFrameCallbackDelayed(nullptr, nullptr, vsyncCallback, &inputCb, 0,
+ CALLBACK_INPUT);
+
+ nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t currTime;
+ int pollResult;
+ do {
+ pollResult = looper->pollOnce(16);
+ currTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ } while (!(inputCb.callbackReceived() && animationCb.callbackReceived()) &&
+ (pollResult != Looper::POLL_TIMEOUT && pollResult != Looper::POLL_ERROR) &&
+ (currTime - startTime < 3000));
+
+ ASSERT_TRUE(inputCb.callbackReceived()) << "did not receive input callback";
+ ASSERT_TRUE(animationCb.callbackReceived()) << "did not receive animation callback";
+
+ ASSERT_EQ(inputCb.frameTime, animationCb.frameTime)
+ << android::base::StringPrintf("input and animation callback frame times don't match. "
+ "inputFrameTime=%lld animationFrameTime=%lld",
+ inputCb.frameTime.count(),
+ animationCb.frameTime.count());
+
+ ASSERT_LT(inputCb.receivedCallbackTime, animationCb.receivedCallbackTime)
+ << android::base::StringPrintf("input callback was not called first. "
+ "inputCallbackTime=%lld animationCallbackTime=%lld",
+ inputCb.frameTime.count(),
+ animationCb.frameTime.count());
+}
+
+} // namespace android
\ No newline at end of file
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 a9d6e8d..7d0b512 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>
@@ -41,6 +42,7 @@
#include <android/os/IInputFlinger.h>
#include <gui/WindowInfo.h>
#include <input/Input.h>
+#include <input/InputConsumer.h>
#include <input/InputTransport.h>
#include <ui/DisplayMode.h>
@@ -58,7 +60,13 @@
using android::gui::TouchOcclusionMode;
using android::gui::WindowInfo;
-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;
@@ -66,7 +74,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);
}
@@ -77,26 +87,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();
@@ -127,7 +138,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 */,
@@ -137,7 +148,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 */);
@@ -145,7 +156,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,
@@ -154,7 +165,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,
@@ -164,7 +175,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,
@@ -175,7 +186,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) {
@@ -187,14 +198,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());
@@ -213,10 +224,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));
@@ -225,18 +236,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);
@@ -244,16 +255,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);
@@ -261,12 +272,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());
@@ -274,7 +290,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);
@@ -295,13 +311,13 @@
reportedListener->wait();
}
- void requestFocus(int displayId = ADISPLAY_ID_DEFAULT) {
+ void requestFocus(ui::LogicalDisplayId displayId = ui::LogicalDisplayId::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);
}
@@ -319,7 +335,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;
@@ -328,7 +344,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,
@@ -343,7 +359,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);
@@ -370,9 +386,7 @@
class InputSurfacesTest : public ::testing::Test {
public:
- InputSurfacesTest() {
- ProcessState::self()->startThreadPool();
- }
+ InputSurfacesTest() { ProcessState::self()->startThreadPool(); }
void SetUp() {
mComposerClient = new SurfaceComposerClient;
@@ -392,15 +406,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 =
@@ -413,31 +425,31 @@
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);
}
}
void injectTap(int x, int y) {
- injectTapOnDisplay(x, y, ADISPLAY_ID_DEFAULT);
+ injectTapOnDisplay(x, y, ui::LogicalDisplayId::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);
}
}
void injectKey(uint32_t keycode) {
- injectKeyOnDisplay(keycode, ADISPLAY_ID_NONE);
+ injectKeyOnDisplay(keycode, ui::LogicalDisplayId::INVALID);
}
TEST_F(InputSurfacesTest, can_receive_input) {
@@ -468,12 +480,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);
@@ -489,23 +497,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);
@@ -554,7 +556,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);
});
@@ -575,7 +577,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);
@@ -594,7 +596,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);
@@ -643,7 +645,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);
@@ -653,7 +655,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);
});
@@ -694,7 +696,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);
@@ -710,7 +712,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);
@@ -727,7 +729,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);
@@ -767,19 +769,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);
@@ -789,19 +791,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);
@@ -813,19 +815,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);
@@ -866,7 +868,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);
});
@@ -890,7 +892,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);
});
@@ -932,13 +934,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);
@@ -948,16 +948,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);
@@ -969,26 +967,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);
@@ -997,12 +995,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) {
@@ -1011,7 +1009,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);
@@ -1019,12 +1017,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) {
@@ -1032,7 +1030,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));
@@ -1041,12 +1039,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) {
@@ -1066,17 +1064,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) {
@@ -1099,7 +1096,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));
@@ -1111,7 +1108,7 @@
// Does not receive events outside its crop
injectTap(26, 26);
- EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
+ containerSurface->assertNoEvent();
}
/**
@@ -1124,7 +1121,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));
@@ -1136,7 +1133,7 @@
// Does not receive events outside parent bounds
injectTap(31, 31);
- EXPECT_EQ(containerSurface->consumeEvent(/*timeout=*/100ms), nullptr);
+ containerSurface->assertNoEvent();
}
/**
@@ -1162,7 +1159,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) {
@@ -1176,19 +1173,19 @@
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) {
- SurfaceComposerClient::destroyDisplay(token);
- }
+ std::for_each(mVirtualDisplays.begin(), mVirtualDisplays.end(),
+ SurfaceComposerClient::destroyVirtualDisplay);
InputSurfacesTest::TearDown();
}
@@ -1203,7 +1200,7 @@
std::string name = "VirtualDisplay";
name += std::to_string(mVirtualDisplays.size());
- sp<IBinder> token = SurfaceComposerClient::createDisplay(String8(name.c_str()), isSecure);
+ sp<IBinder> token = SurfaceComposerClient::createVirtualDisplay(name, isSecure);
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(token, producer);
t.setDisplayFlags(token, receivesInput ? 0x01 /* DisplayDevice::eReceivesInput */ : 0);
@@ -1223,18 +1220,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);
}
@@ -1242,15 +1239,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);
}
@@ -1258,20 +1255,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) {
@@ -1284,21 +1281,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/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index b18b544..223e4b6 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -239,8 +239,9 @@
float const luma_green = 0.7152;
uint32_t const rgba_blue = 0xFFFF0000;
float const luma_blue = 0.0722;
- float const error_margin = 0.01;
+ float const error_margin = 0.1;
float const luma_gray = 0.50;
+ static constexpr std::chrono::milliseconds EVENT_WAIT_TIME_MS = 5000ms;
};
TEST_F(RegionSamplingTest, invalidLayerHandle_doesNotCrash) {
@@ -261,7 +262,7 @@
composer->removeRegionSamplingListener(listener);
}
-TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) {
+TEST_F(RegionSamplingTest, CollectsLuma) {
fill_render(rgba_green);
sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
@@ -273,7 +274,30 @@
sampleArea.bottom = 200;
composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
+ EXPECT_NEAR(listener->luma(), luma_green, error_margin);
+
+ composer->removeRegionSamplingListener(listener);
+}
+
+TEST_F(RegionSamplingTest, CollectsLumaForSecureLayer) {
+ fill_render(rgba_green);
+ SurfaceComposerClient::Transaction()
+ .setFlags(mContentLayer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure)
+ .apply(/*synchronous=*/true);
+
+ sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
+ sp<Listener> listener = new Listener();
+ gui::ARect sampleArea;
+ sampleArea.left = 100;
+ sampleArea.top = 100;
+ sampleArea.right = 200;
+ sampleArea.bottom = 200;
+ composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
+
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_green, error_margin);
composer->removeRegionSamplingListener(listener);
@@ -291,13 +315,14 @@
sampleArea.bottom = 200;
composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_green, error_margin);
listener->reset();
fill_render(rgba_blue);
- EXPECT_TRUE(listener->wait_event(300ms))
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
<< "timed out waiting for 2nd luma event to be received";
EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
@@ -323,10 +348,10 @@
graySampleArea.bottom = 200;
composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener);
- EXPECT_TRUE(grayListener->wait_event(300ms))
+ EXPECT_TRUE(grayListener->wait_event(EVENT_WAIT_TIME_MS))
<< "timed out waiting for luma event to be received";
EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin);
- EXPECT_TRUE(greenListener->wait_event(300ms))
+ EXPECT_TRUE(greenListener->wait_event(EVENT_WAIT_TIME_MS))
<< "timed out waiting for luma event to be received";
EXPECT_NEAR(greenListener->luma(), luma_green, error_margin);
@@ -334,7 +359,7 @@
composer->removeRegionSamplingListener(grayListener);
}
-TEST_F(RegionSamplingTest, DISABLED_TestIfInvalidInputParameters) {
+TEST_F(RegionSamplingTest, TestIfInvalidInputParameters) {
sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
sp<Listener> listener = new Listener();
@@ -369,7 +394,7 @@
composer->removeRegionSamplingListener(listener);
}
-TEST_F(RegionSamplingTest, DISABLED_TestCallbackAfterRemoveListener) {
+TEST_F(RegionSamplingTest, TestCallbackAfterRemoveListener) {
fill_render(rgba_green);
sp<gui::ISurfaceComposer> composer = ComposerServiceAIDL::getComposerService();
sp<Listener> listener = new Listener();
@@ -381,7 +406,8 @@
composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener);
fill_render(rgba_green);
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_green, error_margin);
listener->reset();
@@ -404,11 +430,13 @@
// Test: listener in (100, 100). See layer before move, no layer after move.
fill_render(rgba_blue);
composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_blue, error_margin);
listener->reset();
SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
composer->removeRegionSamplingListener(listener);
@@ -420,11 +448,13 @@
sampleAreaA.right = sampleArea.right;
sampleAreaA.bottom = sampleArea.bottom;
composer->addRegionSamplingListener(sampleAreaA, mTopLayer->getHandle(), listener);
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_gray, error_margin);
listener->reset();
SurfaceComposerClient::Transaction{}.setPosition(mContentLayer, 600, 600).apply();
- EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received";
+ EXPECT_TRUE(listener->wait_event(EVENT_WAIT_TIME_MS))
+ << "timed out waiting for luma event to be received";
EXPECT_NEAR(listener->luma(), luma_green, error_margin);
composer->removeRegionSamplingListener(listener);
}
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 577d239..43cd0f8 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);
}
@@ -673,13 +673,14 @@
return binder::Status::ok();
}
- binder::Status createDisplay(const std::string& /*displayName*/, bool /*secure*/,
- float /*requestedRefreshRate*/,
- sp<IBinder>* /*outDisplay*/) override {
+ binder::Status createVirtualDisplay(const std::string& /*displayName*/, bool /*isSecure*/,
+ const std::string& /*uniqueId*/,
+ float /*requestedRefreshRate*/,
+ sp<IBinder>* /*outDisplay*/) override {
return binder::Status::ok();
}
- binder::Status destroyDisplay(const sp<IBinder>& /*display*/) override {
+ binder::Status destroyVirtualDisplay(const sp<IBinder>& /*displayToken*/) override {
return binder::Status::ok();
}
@@ -815,10 +816,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();
}
@@ -988,6 +985,8 @@
return binder::Status::ok();
}
+ binder::Status notifyShutdown() override { return binder::Status::ok(); }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
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 2b5d375..d782f42 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",
],
}
@@ -84,11 +85,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",
@@ -117,6 +113,27 @@
"--allowlist-var=AINPUT_SOURCE_HDMI",
"--allowlist-var=AINPUT_SOURCE_SENSOR",
"--allowlist-var=AINPUT_SOURCE_ROTARY_ENCODER",
+ "--allowlist-var=AINPUT_KEYBOARD_TYPE_NONE",
+ "--allowlist-var=AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC",
+ "--allowlist-var=AINPUT_KEYBOARD_TYPE_ALPHABETIC",
+ "--allowlist-var=AMETA_NONE",
+ "--allowlist-var=AMETA_ALT_ON",
+ "--allowlist-var=AMETA_ALT_LEFT_ON",
+ "--allowlist-var=AMETA_ALT_RIGHT_ON",
+ "--allowlist-var=AMETA_SHIFT_ON",
+ "--allowlist-var=AMETA_SHIFT_LEFT_ON",
+ "--allowlist-var=AMETA_SHIFT_RIGHT_ON",
+ "--allowlist-var=AMETA_SYM_ON",
+ "--allowlist-var=AMETA_FUNCTION_ON",
+ "--allowlist-var=AMETA_CTRL_ON",
+ "--allowlist-var=AMETA_CTRL_LEFT_ON",
+ "--allowlist-var=AMETA_CTRL_RIGHT_ON",
+ "--allowlist-var=AMETA_META_ON",
+ "--allowlist-var=AMETA_META_LEFT_ON",
+ "--allowlist-var=AMETA_META_RIGHT_ON",
+ "--allowlist-var=AMETA_CAPS_LOCK_ON",
+ "--allowlist-var=AMETA_NUM_LOCK_ON",
+ "--allowlist-var=AMETA_SCROLL_LOCK_ON",
],
static_libs: [
@@ -131,6 +148,29 @@
],
}
+cc_library_static {
+ name: "iinputflinger_aidl_lib_static",
+ host_supported: true,
+ srcs: [
+ "android/os/IInputFlinger.aidl",
+ "android/os/InputChannelCore.aidl",
+ ],
+ shared_libs: [
+ "libbinder",
+ ],
+ whole_static_libs: [
+ "libgui_window_info_static",
+ ],
+ aidl: {
+ export_aidl_headers: true,
+ local_include_dirs: ["."],
+ include_dirs: [
+ "frameworks/native/libs/gui",
+ "frameworks/native/libs/input",
+ ],
+ },
+}
+
// Contains methods to help access C++ code from rust
cc_library_static {
name: "libinput_from_rust_to_cpp",
@@ -175,16 +215,17 @@
"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
],
srcs: [
- "android/os/IInputFlinger.aidl",
- "android/os/InputChannelCore.aidl",
"AccelerationCurve.cpp",
"Input.cpp",
+ "InputConsumer.cpp",
+ "InputConsumerNoResampling.cpp",
"InputDevice.cpp",
"InputEventLabels.cpp",
"InputTransport.cpp",
"InputVerifier.cpp",
"Keyboard.cpp",
"KeyCharacterMap.cpp",
+ "KeyboardClassifier.cpp",
"KeyLayoutMap.cpp",
"MotionPredictor.cpp",
"MotionPredictorMetricsManager.cpp",
@@ -218,6 +259,7 @@
shared_libs: [
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcutils",
"liblog",
"libPlatformProperties",
@@ -239,7 +281,6 @@
static_libs: [
"inputconstants-cpp",
- "libgui_window_info_static",
"libui-types",
"libtflite_static",
"libkernelconfigs",
@@ -248,10 +289,10 @@
whole_static_libs: [
"com.android.input.flags-aconfig-cc",
"libinput_rust_ffi",
+ "iinputflinger_aidl_lib_static",
],
export_static_lib_headers: [
- "libgui_window_info_static",
"libui-types",
],
@@ -262,21 +303,14 @@
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",
],
+ static_libs: [
+ "libstatslog_libinput",
+ "libstatssocket_lazy",
+ ],
},
host: {
include_dirs: [
@@ -285,37 +319,32 @@
],
},
},
-
- aidl: {
- local_include_dirs: ["."],
- export_aidl_headers: true,
- include_dirs: [
- "frameworks/native/libs/gui",
- ],
- },
}
-// 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 {
+cc_library_static {
name: "libstatslog_libinput",
generated_sources: ["statslog_libinput.cpp"],
generated_headers: ["statslog_libinput.h"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
export_generated_headers: ["statslog_libinput.h"],
shared_libs: [
- "libbinder",
- "libstatsbootstrap",
+ "libcutils",
+ "liblog",
"libutils",
- "android.os.statsbootstrap_aidl-cpp",
+ ],
+ static_libs: [
+ "libstatssocket_lazy",
],
}
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",
+ cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_libinput.h " +
+ "--module libinput --namespace android,libinput",
out: [
"statslog_libinput.h",
],
@@ -324,9 +353,9 @@
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",
+ cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_libinput.cpp " +
+ "--module libinput --namespace android,libinput " +
+ "--importHeader statslog_libinput.h",
out: [
"statslog_libinput.cpp",
],
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 9e0ce1d..b098147 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -27,15 +27,12 @@
#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>
#include <input/InputEventLabels.h>
-#ifdef __linux__
#include <binder/Parcel.h>
-#endif
#if defined(__ANDROID__)
#include <sys/random.h>
#endif
@@ -60,6 +57,58 @@
return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
+int32_t resolveActionForSplitMotionEvent(
+ int32_t action, int32_t flags, const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerProperties>& splitPointerProperties) {
+ LOG_ALWAYS_FATAL_IF(splitPointerProperties.empty());
+ const auto maskedAction = MotionEvent::getActionMasked(action);
+ if (maskedAction != AMOTION_EVENT_ACTION_POINTER_DOWN &&
+ maskedAction != AMOTION_EVENT_ACTION_POINTER_UP) {
+ // The action is unaffected by splitting this motion event.
+ return action;
+ }
+ const auto actionIndex = MotionEvent::getActionIndex(action);
+ if (CC_UNLIKELY(actionIndex >= pointerProperties.size())) {
+ LOG(FATAL) << "Action index is out of bounds, index: " << actionIndex;
+ }
+
+ const auto affectedPointerId = pointerProperties[actionIndex].id;
+ std::optional<uint32_t> splitActionIndex;
+ for (uint32_t i = 0; i < splitPointerProperties.size(); i++) {
+ if (affectedPointerId == splitPointerProperties[i].id) {
+ splitActionIndex = i;
+ break;
+ }
+ }
+ if (!splitActionIndex.has_value()) {
+ // The affected pointer is not part of the split motion event.
+ return AMOTION_EVENT_ACTION_MOVE;
+ }
+
+ if (splitPointerProperties.size() > 1) {
+ return maskedAction | (*splitActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ }
+
+ if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
+ return ((flags & AMOTION_EVENT_FLAG_CANCELED) != 0) ? AMOTION_EVENT_ACTION_CANCEL
+ : AMOTION_EVENT_ACTION_UP;
+ }
+ return AMOTION_EVENT_ACTION_DOWN;
+}
+
+float transformOrientation(const ui::Transform& transform, const PointerCoords& coords,
+ int32_t motionEventFlags) {
+ if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) == 0) {
+ return 0;
+ }
+
+ const bool isDirectionalAngle =
+ (motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION) != 0;
+
+ return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION),
+ isDirectionalAngle);
+}
+
} // namespace
const char* motionClassificationToString(MotionClassification classification) {
@@ -151,7 +200,7 @@
return roundTransformedCoords(transformedXy - transformedOrigin);
}
-float transformAngle(const ui::Transform& transform, float angleRadians) {
+float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional) {
// Construct and transform a vector oriented at the specified clockwise angle from vertical.
// Coordinate system: down is increasing Y, right is increasing X.
float x = sinf(angleRadians);
@@ -165,6 +214,11 @@
transformedPoint.x -= origin.x;
transformedPoint.y -= origin.y;
+ if (!isDirectional && transformedPoint.y > 0) {
+ // Limit the range of atan2f to [-pi/2, pi/2] by reversing the direction of the vector.
+ transformedPoint *= -1;
+ }
+
// Derive the transformed vector's clockwise angle from vertical.
// The return value of atan2f is in range [-pi, pi] which conforms to the orientation API.
return atan2f(transformedPoint.x, -transformedPoint.y);
@@ -254,8 +308,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;
@@ -317,10 +371,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;
@@ -444,7 +499,6 @@
scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale);
}
-#ifdef __linux__
status_t PointerCoords::readFromParcel(Parcel* parcel) {
bits = parcel->readInt64();
@@ -472,7 +526,6 @@
parcel->writeBool(isResampled);
return OK;
}
-#endif
void PointerCoords::tooManyAxes(int axis) {
ALOGW("Could not set value for axis %d because the PointerCoords structure is full and "
@@ -495,36 +548,17 @@
return true;
}
-void PointerCoords::transform(const ui::Transform& transform) {
- const vec2 xy = transform.transform(getXYValue());
- setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
- setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
-
- if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) ||
- BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) {
- const ui::Transform rotation(transform.getOrientation());
- const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
- getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
- setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
- setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
- }
-
- if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) {
- const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
- setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val));
- }
-}
-
// --- 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;
@@ -584,6 +618,28 @@
}
}
+void MotionEvent::splitFrom(const android::MotionEvent& other,
+ std::bitset<MAX_POINTER_ID + 1> splitPointerIds, int32_t newEventId) {
+ // TODO(b/327503168): The down time should be a parameter to the split function, because only
+ // the caller can know when the first event went down on the target.
+ const nsecs_t splitDownTime = other.mDownTime;
+
+ auto [action, pointerProperties, pointerCoords] =
+ split(other.getAction(), other.getFlags(), other.getHistorySize(),
+ other.mPointerProperties, other.mSamplePointerCoords, splitPointerIds);
+
+ // Initialize the event with zero pointers, and manually set the split pointers.
+ initialize(newEventId, other.mDeviceId, other.mSource, other.mDisplayId, /*hmac=*/{}, action,
+ other.mActionButton, other.mFlags, other.mEdgeFlags, other.mMetaState,
+ other.mButtonState, other.mClassification, other.mTransform, other.mXPrecision,
+ other.mYPrecision, other.mRawXCursorPosition, other.mRawYCursorPosition,
+ other.mRawTransform, splitDownTime, other.getEventTime(), /*pointerCount=*/0,
+ pointerProperties.data(), pointerCoords.data());
+ mPointerProperties = std::move(pointerProperties);
+ mSamplePointerCoords = std::move(pointerCoords);
+ mSampleEventTimes = other.mSampleEventTimes;
+}
+
void MotionEvent::addSample(
int64_t eventTime,
const PointerCoords* pointerCoords) {
@@ -665,13 +721,13 @@
float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords);
+ return calculateTransformedAxisValue(axis, mSource, mFlags, mRawTransform, coords);
}
float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex,
size_t historicalIndex) const {
const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex);
- return calculateTransformedAxisValue(axis, mSource, mTransform, coords);
+ return calculateTransformedAxisValue(axis, mSource, mFlags, mTransform, coords);
}
ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const {
@@ -690,6 +746,18 @@
mTransform.set(currXOffset + xOffset, currYOffset + yOffset);
}
+float MotionEvent::getRawXOffset() const {
+ // This is equivalent to the x-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).tx();
+}
+
+float MotionEvent::getRawYOffset() const {
+ // This is equivalent to the y-coordinate of the point that the origin of the raw coordinate
+ // space maps to.
+ return (mTransform * mRawTransform.inverse()).ty();
+}
+
void MotionEvent::scale(float globalScaleFactor) {
mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor);
mRawTransform.set(mRawTransform.tx() * globalScaleFactor,
@@ -716,8 +784,9 @@
transform.set(matrix);
// Apply the transformation to all samples.
- std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(),
- [&transform](PointerCoords& c) { c.transform(transform); });
+ std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), [&](PointerCoords& c) {
+ calculateTransformedCoordsInPlace(c, mSource, mFlags, transform);
+ });
if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION &&
mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) {
@@ -727,7 +796,6 @@
}
}
-#ifdef __linux__
static status_t readFromParcel(ui::Transform& transform, const Parcel& parcel) {
float dsdx, dtdx, tx, dtdy, dsdy, ty;
status_t status = parcel.readFloat(&dsdx);
@@ -762,7 +830,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) {
@@ -830,7 +898,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);
@@ -874,7 +942,6 @@
}
return OK;
}
-#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
@@ -934,6 +1001,46 @@
return android::base::StringPrintf("%" PRId32, action);
}
+std::tuple<int32_t, std::vector<PointerProperties>, std::vector<PointerCoords>> MotionEvent::split(
+ int32_t action, int32_t flags, int32_t historySize,
+ const std::vector<PointerProperties>& pointerProperties,
+ const std::vector<PointerCoords>& pointerCoords,
+ std::bitset<MAX_POINTER_ID + 1> splitPointerIds) {
+ LOG_ALWAYS_FATAL_IF(!splitPointerIds.any());
+ const auto pointerCount = pointerProperties.size();
+ LOG_ALWAYS_FATAL_IF(pointerCoords.size() != (pointerCount * (historySize + 1)));
+ const auto splitCount = splitPointerIds.count();
+
+ std::vector<PointerProperties> splitPointerProperties;
+ std::vector<PointerCoords> splitPointerCoords;
+
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ if (splitPointerIds.test(pointerProperties[i].id)) {
+ splitPointerProperties.emplace_back(pointerProperties[i]);
+ }
+ }
+ for (uint32_t i = 0; i < pointerCoords.size(); i++) {
+ if (splitPointerIds.test(pointerProperties[i % pointerCount].id)) {
+ splitPointerCoords.emplace_back(pointerCoords[i]);
+ }
+ }
+ LOG_ALWAYS_FATAL_IF(splitPointerCoords.size() !=
+ (splitPointerProperties.size() * (historySize + 1)));
+
+ if (CC_UNLIKELY(splitPointerProperties.size() != splitCount)) {
+ // TODO(b/329107108): Promote this to a fatal check once bugs in the caller are resolved.
+ LOG(ERROR) << "Cannot split MotionEvent: Requested splitting " << splitCount
+ << " pointers from the original event, but the original event only contained "
+ << splitPointerProperties.size() << " of those pointers.";
+ }
+
+ // TODO(b/327503168): Verify the splitDownTime here once it is used correctly.
+
+ const auto splitAction = resolveActionForSplitMotionEvent(action, flags, pointerProperties,
+ splitPointerProperties);
+ return {splitAction, splitPointerProperties, splitPointerCoords};
+}
+
// Apply the given transformation to the point without checking whether the entire transform
// should be disregarded altogether for the provided source.
static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
@@ -951,7 +1058,7 @@
}
// Keep in sync with calculateTransformedCoords.
-float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
+float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags,
const ui::Transform& transform,
const PointerCoords& coords) {
if (shouldDisregardTransformation(source)) {
@@ -973,7 +1080,7 @@
}
if (axis == AMOTION_EVENT_AXIS_ORIENTATION) {
- return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
+ return transformOrientation(transform, coords, flags);
}
return coords.getAxisValue(axis);
@@ -981,29 +1088,32 @@
// Keep in sync with calculateTransformedAxisValue. This is an optimization of
// calculateTransformedAxisValue for all PointerCoords axes.
-PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
- const ui::Transform& transform,
- const PointerCoords& coords) {
+void MotionEvent::calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source,
+ int32_t flags, const ui::Transform& transform) {
if (shouldDisregardTransformation(source)) {
- return coords;
+ return;
}
- PointerCoords out = coords;
const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
- out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
- out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y);
const vec2 relativeXy =
transformWithoutTranslation(transform,
{coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X),
coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)});
- out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
- out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y);
- out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
- transformAngle(transform,
- coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)));
+ coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION,
+ transformOrientation(transform, coords, flags));
+}
+PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, int32_t flags,
+ const ui::Transform& transform,
+ const PointerCoords& coords) {
+ PointerCoords out = coords;
+ calculateTransformedCoordsInPlace(out, source, flags, transform);
return out;
}
@@ -1090,7 +1200,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::LogicalDisplayId::INVALID, INVALID_HMAC);
mHasFocus = hasFocus;
}
@@ -1103,7 +1213,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::LogicalDisplayId::INVALID, INVALID_HMAC);
mPointerCaptureEnabled = pointerCaptureEnabled;
}
@@ -1116,7 +1226,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::LogicalDisplayId::INVALID, INVALID_HMAC);
mIsExiting = isExiting;
mX = x;
mY = y;
@@ -1133,7 +1243,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::LogicalDisplayId::INVALID, INVALID_HMAC);
mIsInTouchMode = isInTouchMode;
}
diff --git a/libs/input/InputConsumer.cpp b/libs/input/InputConsumer.cpp
new file mode 100644
index 0000000..fcf490d
--- /dev/null
+++ b/libs/input/InputConsumer.cpp
@@ -0,0 +1,962 @@
+/**
+ * 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 <cstdint>
+#define LOG_TAG "InputTransport"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <math.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <cutils/properties.h>
+#include <ftl/enum.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+#include <com_android_input_flags.h>
+#include <input/InputConsumer.h>
+#include <input/PrintTools.h>
+#include <input/TraceTools.h>
+
+namespace input_flags = com::android::input::flags;
+
+namespace android {
+
+namespace {
+
+/**
+ * Log debug messages relating to the consumer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
+ */
+
+const bool DEBUG_TRANSPORT_CONSUMER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
+
+const bool IS_DEBUGGABLE_BUILD =
+#if defined(__ANDROID__)
+ android::base::GetBoolProperty("ro.debuggable", false);
+#else
+ true;
+#endif
+
+/**
+ * Log debug messages about touch event resampling.
+ *
+ * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG".
+ * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
+ * on debuggable builds (e.g. userdebug).
+ */
+bool debugResampling() {
+ if (!IS_DEBUGGABLE_BUILD) {
+ static const bool DEBUG_TRANSPORT_RESAMPLING =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
+ ANDROID_LOG_INFO);
+ return DEBUG_TRANSPORT_RESAMPLING;
+ }
+ return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
+}
+
+void initializeKeyEvent(KeyEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.key.eventId, msg.body.key.deviceId, msg.body.key.source,
+ 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) {
+ event.initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
+}
+
+void initializeCaptureEvent(CaptureEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
+}
+
+void initializeDragEvent(DragEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
+ msg.body.drag.isExiting);
+}
+
+void initializeMotionEvent(MotionEvent& event, const InputMessage& msg) {
+ uint32_t pointerCount = msg.body.motion.pointerCount;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i] = msg.body.motion.pointers[i].properties;
+ pointerCoords[i] = msg.body.motion.pointers[i].coords;
+ }
+
+ ui::Transform transform;
+ transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx,
+ msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw,
+ 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,
+ 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) {
+ uint32_t pointerCount = msg.body.motion.pointerCount;
+ PointerCoords pointerCoords[pointerCount];
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerCoords[i] = msg.body.motion.pointers[i].coords;
+ }
+
+ event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
+ event.addSample(msg.body.motion.eventTime, pointerCoords);
+}
+
+void initializeTouchModeEvent(TouchModeEvent& event, const InputMessage& msg) {
+ event.initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
+}
+
+// Nanoseconds per milliseconds.
+constexpr nsecs_t NANOS_PER_MS = 1000000;
+
+// Latency added during resampling. A few milliseconds doesn't hurt much but
+// reduces the impact of mispredicted touch positions.
+const std::chrono::duration RESAMPLE_LATENCY = 5ms;
+
+// Minimum time difference between consecutive samples before attempting to resample.
+const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS;
+
+// Maximum time difference between consecutive samples before attempting to resample
+// by extrapolation.
+const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS;
+
+// Maximum time to predict forward from the last known state, to avoid predicting too
+// far into the future. This time is further bounded by 50% of the last time delta.
+const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
+
+/**
+ * System property for enabling / disabling touch resampling.
+ * Resampling extrapolates / interpolates the reported touch event coordinates to better
+ * align them to the VSYNC signal, thus resulting in smoother scrolling performance.
+ * Resampling is not needed (and should be disabled) on hardware that already
+ * has touch events triggered by VSYNC.
+ * Set to "1" to enable resampling (default).
+ * Set to "0" to disable resampling.
+ * Resampling is enabled by default.
+ */
+const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling";
+
+inline float lerp(float a, float b, float alpha) {
+ return a + alpha * (b - a);
+}
+
+inline bool isPointerEvent(int32_t source) {
+ return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
+}
+
+bool shouldResampleTool(ToolType toolType) {
+ return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+ toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
+}
+
+} // namespace
+
+using android::base::Result;
+using android::base::StringPrintf;
+
+// --- InputConsumer ---
+
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
+ : InputConsumer(channel, isTouchResamplingEnabled()) {}
+
+InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel,
+ bool enableTouchResampling)
+ : 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() {
+ ATRACE_ASYNC_END(mLifetimeTraceTag.c_str(), /*cookie=*/mLifetimeTraceCookie);
+}
+
+bool InputConsumer::isTouchResamplingEnabled() {
+ return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
+}
+
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
+
+ *outSeq = 0;
+ *outEvent = nullptr;
+
+ // Fetch the next input message.
+ // Loop until an event can be returned or no additional events are received.
+ while (!*outEvent) {
+ if (mMsgDeferred) {
+ // mMsg contains a valid input message from the previous call to consume
+ // that has not yet been processed.
+ mMsgDeferred = false;
+ } else {
+ // Receive a fresh message.
+ status_t result = mChannel->receiveMessage(&mMsg);
+ if (result == OK) {
+ const auto [_, inserted] =
+ mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
+ LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32,
+ mMsg.header.seq);
+
+ // Trace the event processing timeline - event was just read from the socket
+ ATRACE_ASYNC_BEGIN(mProcessingTraceTag.c_str(), /*cookie=*/mMsg.header.seq);
+ }
+ if (result) {
+ // Consume the next batched event unless batches are being held for later.
+ if (consumeBatches || result != WOULD_BLOCK) {
+ result = consumeBatch(factory, frameTime, outSeq, outEvent);
+ if (*outEvent) {
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
+ }
+ }
+ return result;
+ }
+ }
+
+ switch (mMsg.header.type) {
+ case InputMessage::Type::KEY: {
+ KeyEvent* keyEvent = factory->createKeyEvent();
+ if (!keyEvent) return NO_MEMORY;
+
+ initializeKeyEvent(*keyEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = keyEvent;
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
+ }
+
+ case InputMessage::Type::MOTION: {
+ ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
+ if (batchIndex >= 0) {
+ Batch& batch = mBatches[batchIndex];
+ if (canAddSample(batch, &mMsg)) {
+ batch.samples.push_back(mMsg);
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ break;
+ } else if (isPointerEvent(mMsg.body.motion.source) &&
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
+ // No need to process events that we are going to cancel anyways
+ const size_t count = batch.samples.size();
+ for (size_t i = 0; i < count; i++) {
+ const InputMessage& msg = batch.samples[i];
+ sendFinishedSignal(msg.header.seq, false);
+ }
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+ mBatches.erase(mBatches.begin() + batchIndex);
+ } else {
+ // We cannot append to the batch in progress, so we need to consume
+ // the previous batch right now and defer the new message until later.
+ mMsgDeferred = true;
+ status_t result = consumeSamples(factory, batch, batch.samples.size(),
+ outSeq, outEvent);
+ mBatches.erase(mBatches.begin() + batchIndex);
+ if (result) {
+ return result;
+ }
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
+ }
+ }
+
+ // Start a new batch if needed.
+ if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ Batch batch;
+ batch.samples.push_back(mMsg);
+ mBatches.push_back(batch);
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
+ break;
+ }
+
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
+
+ updateTouchState(mMsg);
+ initializeMotionEvent(*motionEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = motionEvent;
+
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ break;
+ }
+
+ case InputMessage::Type::FINISHED:
+ case InputMessage::Type::TIMELINE: {
+ LOG(FATAL) << "Consumed a " << ftl::enum_string(mMsg.header.type)
+ << " message, which should never be seen by "
+ "InputConsumer on "
+ << mChannel->getName();
+ break;
+ }
+
+ case InputMessage::Type::FOCUS: {
+ FocusEvent* focusEvent = factory->createFocusEvent();
+ if (!focusEvent) return NO_MEMORY;
+
+ initializeFocusEvent(*focusEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = focusEvent;
+ break;
+ }
+
+ case InputMessage::Type::CAPTURE: {
+ CaptureEvent* captureEvent = factory->createCaptureEvent();
+ if (!captureEvent) return NO_MEMORY;
+
+ initializeCaptureEvent(*captureEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = captureEvent;
+ break;
+ }
+
+ case InputMessage::Type::DRAG: {
+ DragEvent* dragEvent = factory->createDragEvent();
+ if (!dragEvent) return NO_MEMORY;
+
+ initializeDragEvent(*dragEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = dragEvent;
+ break;
+ }
+
+ case InputMessage::Type::TOUCH_MODE: {
+ TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
+ if (!touchModeEvent) return NO_MEMORY;
+
+ initializeTouchModeEvent(*touchModeEvent, mMsg);
+ *outSeq = mMsg.header.seq;
+ *outEvent = touchModeEvent;
+ break;
+ }
+ }
+ }
+ return OK;
+}
+
+status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent) {
+ status_t result;
+ for (size_t i = mBatches.size(); i > 0;) {
+ i--;
+ Batch& batch = mBatches[i];
+ if (frameTime < 0) {
+ result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
+ mBatches.erase(mBatches.begin() + i);
+ return result;
+ }
+
+ nsecs_t sampleTime = frameTime;
+ if (mResampleTouch) {
+ sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count();
+ }
+ ssize_t split = findSampleNoLaterThan(batch, sampleTime);
+ if (split < 0) {
+ continue;
+ }
+
+ result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
+ const InputMessage* next;
+ if (batch.samples.empty()) {
+ mBatches.erase(mBatches.begin() + i);
+ next = nullptr;
+ } else {
+ next = &batch.samples[0];
+ }
+ if (!result && mResampleTouch) {
+ resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
+ }
+ return result;
+ }
+
+ return WOULD_BLOCK;
+}
+
+status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, Batch& batch,
+ size_t count, uint32_t* outSeq, InputEvent** outEvent) {
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
+
+ uint32_t chain = 0;
+ for (size_t i = 0; i < count; i++) {
+ InputMessage& msg = batch.samples[i];
+ updateTouchState(msg);
+ if (i) {
+ SeqChain seqChain;
+ seqChain.seq = msg.header.seq;
+ seqChain.chain = chain;
+ mSeqChains.push_back(seqChain);
+ addSample(*motionEvent, msg);
+ } else {
+ initializeMotionEvent(*motionEvent, msg);
+ }
+ chain = msg.header.seq;
+ }
+ batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
+
+ *outSeq = chain;
+ *outEvent = motionEvent;
+ return OK;
+}
+
+void InputConsumer::updateTouchState(InputMessage& msg) {
+ if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) {
+ return;
+ }
+
+ int32_t deviceId = msg.body.motion.deviceId;
+ int32_t source = msg.body.motion.source;
+
+ // Update the touch state history to incorporate the new input message.
+ // If the message is in the past relative to the most recently produced resampled
+ // touch, then use the resampled time and coordinates instead.
+ switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) {
+ case AMOTION_EVENT_ACTION_DOWN: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index < 0) {
+ mTouchStates.push_back({});
+ index = mTouchStates.size() - 1;
+ }
+ TouchState& touchState = mTouchStates[index];
+ touchState.initialize(deviceId, source);
+ touchState.addHistory(msg);
+ break;
+ }
+
+ case AMOTION_EVENT_ACTION_MOVE: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index >= 0) {
+ TouchState& touchState = mTouchStates[index];
+ touchState.addHistory(msg);
+ rewriteMessage(touchState, msg);
+ }
+ break;
+ }
+
+ case AMOTION_EVENT_ACTION_POINTER_DOWN: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index >= 0) {
+ TouchState& touchState = mTouchStates[index];
+ touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
+ rewriteMessage(touchState, msg);
+ }
+ break;
+ }
+
+ case AMOTION_EVENT_ACTION_POINTER_UP: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index >= 0) {
+ TouchState& touchState = mTouchStates[index];
+ rewriteMessage(touchState, msg);
+ touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
+ }
+ break;
+ }
+
+ case AMOTION_EVENT_ACTION_SCROLL: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index >= 0) {
+ TouchState& touchState = mTouchStates[index];
+ rewriteMessage(touchState, msg);
+ }
+ break;
+ }
+
+ case AMOTION_EVENT_ACTION_UP:
+ case AMOTION_EVENT_ACTION_CANCEL: {
+ ssize_t index = findTouchState(deviceId, source);
+ if (index >= 0) {
+ TouchState& touchState = mTouchStates[index];
+ rewriteMessage(touchState, msg);
+ mTouchStates.erase(mTouchStates.begin() + index);
+ }
+ break;
+ }
+ }
+}
+
+/**
+ * Replace the coordinates in msg with the coordinates in lastResample, if necessary.
+ *
+ * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time
+ * is in the past relative to msg and the past two events do not contain identical coordinates),
+ * then invalidate the lastResample data for that pointer.
+ * If the two past events have identical coordinates, then lastResample data for that pointer will
+ * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is
+ * resampled to the new value x1, then x1 will always be used to replace x0 until some new value
+ * not equal to x0 is received.
+ */
+void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) {
+ nsecs_t eventTime = msg.body.motion.eventTime;
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ uint32_t id = msg.body.motion.pointers[i].properties.id;
+ if (state.lastResample.idBits.hasBit(id)) {
+ if (eventTime < state.lastResample.eventTime ||
+ state.recentCoordinatesAreIdentical(id)) {
+ PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
+ const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
+ ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
+ resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(),
+ msgCoords.getY());
+ msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
+ msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
+ msgCoords.isResampled = true;
+ } else {
+ state.lastResample.idBits.clearBit(id);
+ }
+ }
+ }
+}
+
+void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
+ const InputMessage* next) {
+ if (!mResampleTouch || !(isPointerEvent(event->getSource())) ||
+ event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
+ return;
+ }
+
+ ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
+ if (index < 0) {
+ ALOGD_IF(debugResampling(), "Not resampled, no touch state for device.");
+ return;
+ }
+
+ TouchState& touchState = mTouchStates[index];
+ if (touchState.historySize < 1) {
+ ALOGD_IF(debugResampling(), "Not resampled, no history for device.");
+ return;
+ }
+
+ // Ensure that the current sample has all of the pointers that need to be reported.
+ const History* current = touchState.getHistory(0);
+ size_t pointerCount = event->getPointerCount();
+ for (size_t i = 0; i < pointerCount; i++) {
+ uint32_t id = event->getPointerId(i);
+ if (!current->idBits.hasBit(id)) {
+ ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id);
+ return;
+ }
+ if (!shouldResampleTool(event->getToolType(i))) {
+ ALOGD_IF(debugResampling(),
+ "Not resampled, containing unsupported tool type at pointer %d", id);
+ return;
+ }
+ }
+
+ // Find the data to use for resampling.
+ const History* other;
+ History future;
+ float alpha;
+ if (next) {
+ // Interpolate between current sample and future sample.
+ // So current->eventTime <= sampleTime <= future.eventTime.
+ future.initializeFrom(*next);
+ other = &future;
+ nsecs_t delta = future.eventTime - current->eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
+ return;
+ }
+ alpha = float(sampleTime - current->eventTime) / delta;
+ } else if (touchState.historySize >= 2) {
+ // Extrapolate future sample using current sample and past sample.
+ // So other->eventTime <= current->eventTime <= sampleTime.
+ other = touchState.getHistory(1);
+ nsecs_t delta = current->eventTime - other->eventTime;
+ if (delta < RESAMPLE_MIN_DELTA) {
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
+ delta);
+ return;
+ } else if (delta > RESAMPLE_MAX_DELTA) {
+ ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.",
+ delta);
+ return;
+ }
+ nsecs_t maxPredict = current->eventTime + std::min(delta / 2, RESAMPLE_MAX_PREDICTION);
+ if (sampleTime > maxPredict) {
+ ALOGD_IF(debugResampling(),
+ "Sample time is too far in the future, adjusting prediction "
+ "from %" PRId64 " to %" PRId64 " ns.",
+ sampleTime - current->eventTime, maxPredict - current->eventTime);
+ sampleTime = maxPredict;
+ }
+ alpha = float(current->eventTime - sampleTime) / delta;
+ } else {
+ ALOGD_IF(debugResampling(), "Not resampled, insufficient data.");
+ return;
+ }
+
+ if (current->eventTime == sampleTime) {
+ ALOGD_IF(debugResampling(), "Not resampled, 2 events with identical times.");
+ return;
+ }
+
+ for (size_t i = 0; i < pointerCount; i++) {
+ uint32_t id = event->getPointerId(i);
+ if (!other->idBits.hasBit(id)) {
+ ALOGD_IF(debugResampling(), "Not resampled, the other doesn't have pointer id %d.", id);
+ return;
+ }
+ }
+
+ // Resample touch coordinates.
+ History oldLastResample;
+ oldLastResample.initializeFrom(touchState.lastResample);
+ touchState.lastResample.eventTime = sampleTime;
+ touchState.lastResample.idBits.clear();
+ for (size_t i = 0; i < pointerCount; i++) {
+ uint32_t id = event->getPointerId(i);
+ touchState.lastResample.idToIndex[id] = i;
+ touchState.lastResample.idBits.markBit(id);
+ if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) {
+ // We maintain the previously resampled value for this pointer (stored in
+ // oldLastResample) when the coordinates for this pointer haven't changed since then.
+ // This way we don't introduce artificial jitter when pointers haven't actually moved.
+ // The isResampled flag isn't cleared as the values don't reflect what the device is
+ // actually reporting.
+
+ // We know here that the coordinates for the pointer haven't changed because we
+ // would've cleared the resampled bit in rewriteMessage if they had. We can't modify
+ // lastResample in place because the mapping from pointer ID to index may have changed.
+ touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id);
+ continue;
+ }
+
+ PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
+ const PointerCoords& currentCoords = current->getPointerById(id);
+ resampledCoords = currentCoords;
+ resampledCoords.isResampled = true;
+ const PointerCoords& otherCoords = other->getPointerById(id);
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
+ lerp(currentCoords.getX(), otherCoords.getX(), alpha));
+ resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
+ lerp(currentCoords.getY(), otherCoords.getY(), alpha));
+ ALOGD_IF(debugResampling(),
+ "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
+ "other (%0.3f, %0.3f), alpha %0.3f",
+ id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
+ currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
+ }
+
+ event->addSample(sampleTime, touchState.lastResample.pointers);
+}
+
+status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
+
+ if (!seq) {
+ ALOGE("Attempted to send a finished signal with sequence number 0.");
+ return BAD_VALUE;
+ }
+
+ // Send finished signals for the batch sequence chain first.
+ size_t seqChainCount = mSeqChains.size();
+ if (seqChainCount) {
+ uint32_t currentSeq = seq;
+ uint32_t chainSeqs[seqChainCount];
+ size_t chainIndex = 0;
+ for (size_t i = seqChainCount; i > 0;) {
+ i--;
+ const SeqChain& seqChain = mSeqChains[i];
+ if (seqChain.seq == currentSeq) {
+ currentSeq = seqChain.chain;
+ chainSeqs[chainIndex++] = currentSeq;
+ mSeqChains.erase(mSeqChains.begin() + i);
+ }
+ }
+ status_t status = OK;
+ while (!status && chainIndex > 0) {
+ chainIndex--;
+ status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
+ }
+ if (status) {
+ // An error occurred so at least one signal was not sent, reconstruct the chain.
+ for (;;) {
+ SeqChain seqChain;
+ seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
+ seqChain.chain = chainSeqs[chainIndex];
+ mSeqChains.push_back(seqChain);
+ if (!chainIndex) break;
+ chainIndex--;
+ }
+ return status;
+ }
+ }
+
+ // Send finished signal for the last message in the batch.
+ return sendUnchainedFinishedSignal(seq, handled);
+}
+
+status_t InputConsumer::sendTimeline(int32_t inputEventId,
+ std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
+ ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
+ "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
+ mChannel->getName().c_str(), inputEventId,
+ graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TIMELINE;
+ msg.header.seq = 0;
+ msg.body.timeline.eventId = inputEventId;
+ msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
+ return mChannel->sendMessage(&msg);
+}
+
+nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
+ auto it = mConsumeTimes.find(seq);
+ // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
+ // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
+ LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
+ seq);
+ return it->second;
+}
+
+void InputConsumer::popConsumeTime(uint32_t seq) {
+ mConsumeTimes.erase(seq);
+}
+
+status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FINISHED;
+ msg.header.seq = seq;
+ msg.body.finished.handled = handled;
+ msg.body.finished.consumeTime = getConsumeTime(seq);
+ status_t result = mChannel->sendMessage(&msg);
+ if (result == OK) {
+ // Remove the consume time if the socket write succeeded. We will not need to ack this
+ // message anymore. If the socket write did not succeed, we will try again and will still
+ // need consume time.
+ popConsumeTime(seq);
+
+ // Trace the event processing timeline - event was just finished
+ ATRACE_ASYNC_END(mProcessingTraceTag.c_str(), /*cookie=*/seq);
+ }
+ return result;
+}
+
+bool InputConsumer::hasPendingBatch() const {
+ return !mBatches.empty();
+}
+
+int32_t InputConsumer::getPendingBatchSource() const {
+ if (mBatches.empty()) {
+ return AINPUT_SOURCE_CLASS_NONE;
+ }
+
+ const Batch& batch = mBatches[0];
+ const InputMessage& head = batch.samples[0];
+ return head.body.motion.source;
+}
+
+bool InputConsumer::probablyHasInput() const {
+ return hasPendingBatch() || mChannel->probablyHasInput();
+}
+
+ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
+ for (size_t i = 0; i < mBatches.size(); i++) {
+ const Batch& batch = mBatches[i];
+ const InputMessage& head = batch.samples[0];
+ if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
+ for (size_t i = 0; i < mTouchStates.size(); i++) {
+ const TouchState& touchState = mTouchStates[i];
+ if (touchState.deviceId == deviceId && touchState.source == source) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool InputConsumer::canAddSample(const Batch& batch, const InputMessage* msg) {
+ const InputMessage& head = batch.samples[0];
+ uint32_t pointerCount = msg->body.motion.pointerCount;
+ if (head.body.motion.pointerCount != pointerCount ||
+ head.body.motion.action != msg->body.motion.action) {
+ return false;
+ }
+ for (size_t i = 0; i < pointerCount; i++) {
+ if (head.body.motion.pointers[i].properties != msg->body.motion.pointers[i].properties) {
+ return false;
+ }
+ }
+ return true;
+}
+
+ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
+ size_t numSamples = batch.samples.size();
+ size_t index = 0;
+ while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
+ index += 1;
+ }
+ return ssize_t(index) - 1;
+}
+
+std::string InputConsumer::dump() const {
+ std::string out;
+ out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
+ out = out + "mChannel = " + mChannel->getName() + "\n";
+ out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
+ if (mMsgDeferred) {
+ out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
+ }
+ out += "Batches:\n";
+ for (const Batch& batch : mBatches) {
+ out += " Batch:\n";
+ for (const InputMessage& msg : batch.samples) {
+ out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
+ ftl::enum_string(msg.header.type).c_str());
+ switch (msg.header.type) {
+ case InputMessage::Type::KEY: {
+ out += android::base::StringPrintf("action=%s keycode=%" PRId32,
+ KeyEvent::actionToString(
+ msg.body.key.action),
+ msg.body.key.keyCode);
+ break;
+ }
+ case InputMessage::Type::MOTION: {
+ out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
+ for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
+ const float x = msg.body.motion.pointers[i].coords.getX();
+ const float y = msg.body.motion.pointers[i].coords.getY();
+ out += android::base::StringPrintf("\n Pointer %" PRIu32
+ " : x=%.1f y=%.1f",
+ i, x, y);
+ }
+ break;
+ }
+ case InputMessage::Type::FINISHED: {
+ out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64,
+ toString(msg.body.finished.handled),
+ msg.body.finished.consumeTime);
+ break;
+ }
+ case InputMessage::Type::FOCUS: {
+ out += android::base::StringPrintf("hasFocus=%s",
+ toString(msg.body.focus.hasFocus));
+ break;
+ }
+ case InputMessage::Type::CAPTURE: {
+ out += android::base::StringPrintf("hasCapture=%s",
+ toString(msg.body.capture
+ .pointerCaptureEnabled));
+ break;
+ }
+ case InputMessage::Type::DRAG: {
+ out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s",
+ msg.body.drag.x, msg.body.drag.y,
+ toString(msg.body.drag.isExiting));
+ break;
+ }
+ case InputMessage::Type::TIMELINE: {
+ const nsecs_t gpuCompletedTime =
+ msg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
+ const nsecs_t presentTime =
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
+ out += android::base::StringPrintf("inputEventId=%" PRId32
+ ", gpuCompletedTime=%" PRId64
+ ", presentTime=%" PRId64,
+ msg.body.timeline.eventId, gpuCompletedTime,
+ presentTime);
+ break;
+ }
+ case InputMessage::Type::TOUCH_MODE: {
+ out += android::base::StringPrintf("isInTouchMode=%s",
+ toString(msg.body.touchMode.isInTouchMode));
+ break;
+ }
+ }
+ out += "\n";
+ }
+ }
+ if (mBatches.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mSeqChains:\n";
+ for (const SeqChain& chain : mSeqChains) {
+ out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
+ chain.chain);
+ }
+ if (mSeqChains.empty()) {
+ out += " <empty>\n";
+ }
+ out += "mConsumeTimes:\n";
+ for (const auto& [seq, consumeTime] : mConsumeTimes) {
+ out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq,
+ consumeTime);
+ }
+ if (mConsumeTimes.empty()) {
+ out += " <empty>\n";
+ }
+ return out;
+}
+
+} // namespace android
diff --git a/libs/input/InputConsumerNoResampling.cpp b/libs/input/InputConsumerNoResampling.cpp
new file mode 100644
index 0000000..15d992f
--- /dev/null
+++ b/libs/input/InputConsumerNoResampling.cpp
@@ -0,0 +1,539 @@
+/**
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "InputTransport"
+#define ATRACE_TAG ATRACE_TAG_INPUT
+
+#include <inttypes.h>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <cutils/properties.h>
+#include <ftl/enum.h>
+#include <utils/Trace.h>
+
+#include <com_android_input_flags.h>
+#include <input/InputConsumerNoResampling.h>
+#include <input/PrintTools.h>
+#include <input/TraceTools.h>
+
+namespace input_flags = com::android::input::flags;
+
+namespace android {
+
+namespace {
+
+/**
+ * Log debug messages relating to the consumer end of the transport channel.
+ * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
+ */
+const bool DEBUG_TRANSPORT_CONSUMER =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
+
+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,
+ 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;
+}
+
+std::unique_ptr<FocusEvent> createFocusEvent(const InputMessage& msg) {
+ std::unique_ptr<FocusEvent> event = std::make_unique<FocusEvent>();
+ event->initialize(msg.body.focus.eventId, msg.body.focus.hasFocus);
+ return event;
+}
+
+std::unique_ptr<CaptureEvent> createCaptureEvent(const InputMessage& msg) {
+ std::unique_ptr<CaptureEvent> event = std::make_unique<CaptureEvent>();
+ event->initialize(msg.body.capture.eventId, msg.body.capture.pointerCaptureEnabled);
+ return event;
+}
+
+std::unique_ptr<DragEvent> createDragEvent(const InputMessage& msg) {
+ std::unique_ptr<DragEvent> event = std::make_unique<DragEvent>();
+ event->initialize(msg.body.drag.eventId, msg.body.drag.x, msg.body.drag.y,
+ msg.body.drag.isExiting);
+ return event;
+}
+
+std::unique_ptr<MotionEvent> createMotionEvent(const InputMessage& msg) {
+ std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
+ const uint32_t pointerCount = msg.body.motion.pointerCount;
+ std::vector<PointerProperties> pointerProperties;
+ pointerProperties.reserve(pointerCount);
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.reserve(pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerProperties.push_back(msg.body.motion.pointers[i].properties);
+ pointerCoords.push_back(msg.body.motion.pointers[i].coords);
+ }
+
+ ui::Transform transform;
+ transform.set({msg.body.motion.dsdx, msg.body.motion.dtdx, msg.body.motion.tx,
+ msg.body.motion.dtdy, msg.body.motion.dsdy, msg.body.motion.ty, 0, 0, 1});
+ ui::Transform displayTransform;
+ displayTransform.set({msg.body.motion.dsdxRaw, msg.body.motion.dtdxRaw, msg.body.motion.txRaw,
+ 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,
+ 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.data(), pointerCoords.data());
+ return event;
+}
+
+void addSample(MotionEvent& event, const InputMessage& msg) {
+ uint32_t pointerCount = msg.body.motion.pointerCount;
+ std::vector<PointerCoords> pointerCoords;
+ pointerCoords.reserve(pointerCount);
+ for (uint32_t i = 0; i < pointerCount; i++) {
+ pointerCoords.push_back(msg.body.motion.pointers[i].coords);
+ }
+
+ // TODO(b/329770983): figure out if it's safe to combine events with mismatching metaState
+ event.setMetaState(event.getMetaState() | msg.body.motion.metaState);
+ event.addSample(msg.body.motion.eventTime, pointerCoords.data());
+}
+
+std::unique_ptr<TouchModeEvent> createTouchModeEvent(const InputMessage& msg) {
+ std::unique_ptr<TouchModeEvent> event = std::make_unique<TouchModeEvent>();
+ event->initialize(msg.body.touchMode.eventId, msg.body.touchMode.isInTouchMode);
+ return event;
+}
+
+std::string outboundMessageToString(const InputMessage& outboundMsg) {
+ switch (outboundMsg.header.type) {
+ case InputMessage::Type::FINISHED: {
+ return android::base::StringPrintf(" Finish: seq=%" PRIu32 " handled=%s",
+ outboundMsg.header.seq,
+ toString(outboundMsg.body.finished.handled));
+ }
+ case InputMessage::Type::TIMELINE: {
+ return android::base::
+ StringPrintf(" Timeline: inputEventId=%" PRId32 " gpuCompletedTime=%" PRId64
+ ", presentTime=%" PRId64,
+ outboundMsg.body.timeline.eventId,
+ outboundMsg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
+ outboundMsg.body.timeline
+ .graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+ }
+ default: {
+ LOG(FATAL) << "Outbound message must be FINISHED or TIMELINE, got "
+ << ftl::enum_string(outboundMsg.header.type);
+ return "Unreachable";
+ }
+ }
+}
+
+InputMessage createFinishedMessage(uint32_t seq, bool handled, nsecs_t consumeTime) {
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FINISHED;
+ msg.header.seq = seq;
+ msg.body.finished.handled = handled;
+ msg.body.finished.consumeTime = consumeTime;
+ return msg;
+}
+
+InputMessage createTimelineMessage(int32_t inputEventId, nsecs_t gpuCompletedTime,
+ nsecs_t presentTime) {
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::TIMELINE;
+ msg.header.seq = 0;
+ msg.body.timeline.eventId = inputEventId;
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = gpuCompletedTime;
+ msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = presentTime;
+ return msg;
+}
+
+} // namespace
+
+using android::base::Result;
+using android::base::StringPrintf;
+
+// --- InputConsumerNoResampling ---
+
+InputConsumerNoResampling::InputConsumerNoResampling(const std::shared_ptr<InputChannel>& channel,
+ sp<Looper> looper,
+ InputConsumerCallbacks& callbacks)
+ : mChannel(channel), mLooper(looper), mCallbacks(callbacks), mFdEvents(0) {
+ LOG_ALWAYS_FATAL_IF(mLooper == nullptr);
+ mCallback = sp<LooperEventCallback>::make(
+ std::bind(&InputConsumerNoResampling::handleReceiveCallback, this,
+ std::placeholders::_1));
+ // In the beginning, there are no pending outbounds events; we only care about receiving
+ // incoming data.
+ setFdEvents(ALOOPER_EVENT_INPUT);
+}
+
+InputConsumerNoResampling::~InputConsumerNoResampling() {
+ ensureCalledOnLooperThread(__func__);
+ consumeBatchedInputEvents(std::nullopt);
+ while (!mOutboundQueue.empty()) {
+ processOutboundEvents();
+ // This is our last chance to ack the events. If we don't ack them here, we will get an ANR,
+ // so keep trying to send the events as long as they are present in the queue.
+ }
+ setFdEvents(0);
+}
+
+int InputConsumerNoResampling::handleReceiveCallback(int events) {
+ // Allowed return values of this function as documented in LooperCallback::handleEvent
+ constexpr int REMOVE_CALLBACK = 0;
+ constexpr int KEEP_CALLBACK = 1;
+
+ if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+ // This error typically occurs when the publisher has closed the input channel
+ // as part of removing a window or finishing an IME session, in which case
+ // the consumer will soon be disposed as well.
+ if (DEBUG_TRANSPORT_CONSUMER) {
+ LOG(INFO) << "The channel was hung up or an error occurred: " << mChannel->getName();
+ }
+ return REMOVE_CALLBACK;
+ }
+
+ int handledEvents = 0;
+ if (events & ALOOPER_EVENT_INPUT) {
+ std::vector<InputMessage> messages = readAllMessages();
+ handleMessages(std::move(messages));
+ handledEvents |= ALOOPER_EVENT_INPUT;
+ }
+
+ if (events & ALOOPER_EVENT_OUTPUT) {
+ processOutboundEvents();
+ handledEvents |= ALOOPER_EVENT_OUTPUT;
+ }
+ if (handledEvents != events) {
+ LOG(FATAL) << "Mismatch: handledEvents=" << handledEvents << ", events=" << events;
+ }
+ return KEEP_CALLBACK;
+}
+
+void InputConsumerNoResampling::processOutboundEvents() {
+ while (!mOutboundQueue.empty()) {
+ const InputMessage& outboundMsg = mOutboundQueue.front();
+
+ const status_t result = mChannel->sendMessage(&outboundMsg);
+ if (result == OK) {
+ if (outboundMsg.header.type == InputMessage::Type::FINISHED) {
+ ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/outboundMsg.header.seq);
+ }
+ // Successful send. Erase the entry and keep trying to send more
+ mOutboundQueue.pop();
+ continue;
+ }
+
+ // Publisher is busy, try again later. Keep this entry (do not erase)
+ if (result == WOULD_BLOCK) {
+ setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
+ return; // try again later
+ }
+
+ // Some other error. Give up
+ LOG(FATAL) << "Failed to send outbound event on channel '" << mChannel->getName()
+ << "'. status=" << statusToString(result) << "(" << result << ")";
+ }
+
+ // The queue is now empty. Tell looper there's no more output to expect.
+ setFdEvents(ALOOPER_EVENT_INPUT);
+}
+
+void InputConsumerNoResampling::finishInputEvent(uint32_t seq, bool handled) {
+ ensureCalledOnLooperThread(__func__);
+ mOutboundQueue.push(createFinishedMessage(seq, handled, popConsumeTime(seq)));
+ // also produce finish events for all batches for this seq (if any)
+ const auto it = mBatchedSequenceNumbers.find(seq);
+ if (it != mBatchedSequenceNumbers.end()) {
+ for (uint32_t subSeq : it->second) {
+ mOutboundQueue.push(createFinishedMessage(subSeq, handled, popConsumeTime(subSeq)));
+ }
+ mBatchedSequenceNumbers.erase(it);
+ }
+ processOutboundEvents();
+}
+
+bool InputConsumerNoResampling::probablyHasInput() const {
+ // Ideally, this would only be allowed to run on the looper thread, and in production, it will.
+ // However, for testing, it's convenient to call this while the looper thread is blocked, so
+ // we do not call ensureCalledOnLooperThread here.
+ return (!mBatches.empty()) || mChannel->probablyHasInput();
+}
+
+void InputConsumerNoResampling::reportTimeline(int32_t inputEventId, nsecs_t gpuCompletedTime,
+ nsecs_t presentTime) {
+ ensureCalledOnLooperThread(__func__);
+ mOutboundQueue.push(createTimelineMessage(inputEventId, gpuCompletedTime, presentTime));
+ processOutboundEvents();
+}
+
+nsecs_t InputConsumerNoResampling::popConsumeTime(uint32_t seq) {
+ auto it = mConsumeTimes.find(seq);
+ // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
+ // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
+ LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
+ seq);
+ nsecs_t consumeTime = it->second;
+ mConsumeTimes.erase(it);
+ return consumeTime;
+}
+
+void InputConsumerNoResampling::setFdEvents(int events) {
+ if (mFdEvents != events) {
+ mFdEvents = events;
+ if (events != 0) {
+ mLooper->addFd(mChannel->getFd(), 0, events, mCallback, nullptr);
+ } else {
+ mLooper->removeFd(mChannel->getFd());
+ }
+ }
+}
+
+void InputConsumerNoResampling::handleMessages(std::vector<InputMessage>&& messages) {
+ // TODO(b/297226446) : add resampling
+ for (const InputMessage& msg : messages) {
+ if (msg.header.type == InputMessage::Type::MOTION) {
+ const int32_t action = msg.body.motion.action;
+ const DeviceId deviceId = msg.body.motion.deviceId;
+ const int32_t source = msg.body.motion.source;
+ const bool batchableEvent = (action == AMOTION_EVENT_ACTION_MOVE ||
+ action == AMOTION_EVENT_ACTION_HOVER_MOVE) &&
+ (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER) ||
+ isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK));
+ if (batchableEvent) {
+ // add it to batch
+ mBatches[deviceId].emplace(msg);
+ } else {
+ // consume all pending batches for this event immediately
+ // TODO(b/329776327): figure out if this could be smarter by limiting the
+ // consumption only to the current device.
+ consumeBatchedInputEvents(std::nullopt);
+ handleMessage(msg);
+ }
+ } else {
+ // Non-motion events shouldn't force the consumption of pending batched events
+ handleMessage(msg);
+ }
+ }
+ // At the end of this, if we still have pending batches, notify the receiver about it.
+
+ // We need to carefully notify the InputConsumerCallbacks about the pending batch. The receiver
+ // could choose to consume all events when notified about the batch. That means that the
+ // "mBatches" variable could change when 'InputConsumerCallbacks::onBatchedInputEventPending' is
+ // invoked. We also can't notify the InputConsumerCallbacks in a while loop until mBatches is
+ // empty, because the receiver could choose to not consume the batch immediately.
+ std::set<int32_t> pendingBatchSources;
+ for (const auto& [_, pendingMessages] : mBatches) {
+ // Assume that all messages for a given device has the same source.
+ pendingBatchSources.insert(pendingMessages.front().body.motion.source);
+ }
+ for (const int32_t source : pendingBatchSources) {
+ const bool sourceStillRemaining =
+ std::any_of(mBatches.begin(), mBatches.end(), [=](const auto& pair) {
+ return pair.second.front().body.motion.source == source;
+ });
+ if (sourceStillRemaining) {
+ mCallbacks.onBatchedInputEventPending(source);
+ }
+ }
+}
+
+std::vector<InputMessage> InputConsumerNoResampling::readAllMessages() {
+ std::vector<InputMessage> messages;
+ while (true) {
+ InputMessage msg;
+ status_t result = mChannel->receiveMessage(&msg);
+ switch (result) {
+ case OK: {
+ const auto [_, inserted] =
+ mConsumeTimes.emplace(msg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
+ LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32,
+ msg.header.seq);
+
+ // Trace the event processing timeline - event was just read from the socket
+ // TODO(b/329777420): distinguish between multiple instances of InputConsumer
+ // in the same process.
+ ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/msg.header.seq);
+ messages.push_back(msg);
+ break;
+ }
+ case WOULD_BLOCK: {
+ return messages;
+ }
+ case DEAD_OBJECT: {
+ LOG(FATAL) << "Got a dead object for " << mChannel->getName();
+ break;
+ }
+ case BAD_VALUE: {
+ LOG(FATAL) << "Got a bad value for " << mChannel->getName();
+ break;
+ }
+ default: {
+ LOG(FATAL) << "Unexpected error: " << result;
+ break;
+ }
+ }
+ }
+}
+
+void InputConsumerNoResampling::handleMessage(const InputMessage& msg) const {
+ switch (msg.header.type) {
+ case InputMessage::Type::KEY: {
+ std::unique_ptr<KeyEvent> keyEvent = createKeyEvent(msg);
+ mCallbacks.onKeyEvent(std::move(keyEvent), msg.header.seq);
+ break;
+ }
+
+ case InputMessage::Type::MOTION: {
+ std::unique_ptr<MotionEvent> motionEvent = createMotionEvent(msg);
+ mCallbacks.onMotionEvent(std::move(motionEvent), msg.header.seq);
+ break;
+ }
+
+ case InputMessage::Type::FINISHED:
+ case InputMessage::Type::TIMELINE: {
+ LOG(FATAL) << "Consumed a " << ftl::enum_string(msg.header.type)
+ << " message, which should never be seen by InputConsumer on "
+ << mChannel->getName();
+ break;
+ }
+
+ case InputMessage::Type::FOCUS: {
+ std::unique_ptr<FocusEvent> focusEvent = createFocusEvent(msg);
+ mCallbacks.onFocusEvent(std::move(focusEvent), msg.header.seq);
+ break;
+ }
+
+ case InputMessage::Type::CAPTURE: {
+ std::unique_ptr<CaptureEvent> captureEvent = createCaptureEvent(msg);
+ mCallbacks.onCaptureEvent(std::move(captureEvent), msg.header.seq);
+ break;
+ }
+
+ case InputMessage::Type::DRAG: {
+ std::unique_ptr<DragEvent> dragEvent = createDragEvent(msg);
+ mCallbacks.onDragEvent(std::move(dragEvent), msg.header.seq);
+ break;
+ }
+
+ case InputMessage::Type::TOUCH_MODE: {
+ std::unique_ptr<TouchModeEvent> touchModeEvent = createTouchModeEvent(msg);
+ mCallbacks.onTouchModeEvent(std::move(touchModeEvent), msg.header.seq);
+ break;
+ }
+ }
+}
+
+bool InputConsumerNoResampling::consumeBatchedInputEvents(
+ std::optional<nsecs_t> requestedFrameTime) {
+ ensureCalledOnLooperThread(__func__);
+ // When batching is not enabled, we want to consume all events. That's equivalent to having an
+ // infinite frameTime.
+ const nsecs_t frameTime = requestedFrameTime.value_or(std::numeric_limits<nsecs_t>::max());
+ bool producedEvents = false;
+ for (auto& [deviceId, messages] : mBatches) {
+ std::unique_ptr<MotionEvent> motion;
+ std::optional<uint32_t> firstSeqForBatch;
+ std::vector<uint32_t> sequences;
+ while (!messages.empty()) {
+ const InputMessage& msg = messages.front();
+ if (msg.body.motion.eventTime > frameTime) {
+ break;
+ }
+ if (motion == nullptr) {
+ motion = createMotionEvent(msg);
+ firstSeqForBatch = msg.header.seq;
+ const auto [_, inserted] = mBatchedSequenceNumbers.insert({*firstSeqForBatch, {}});
+ if (!inserted) {
+ LOG(FATAL) << "The sequence " << msg.header.seq << " was already present!";
+ }
+ } else {
+ addSample(*motion, msg);
+ mBatchedSequenceNumbers[*firstSeqForBatch].push_back(msg.header.seq);
+ }
+ messages.pop();
+ }
+ if (motion != nullptr) {
+ LOG_ALWAYS_FATAL_IF(!firstSeqForBatch.has_value());
+ mCallbacks.onMotionEvent(std::move(motion), *firstSeqForBatch);
+ producedEvents = true;
+ } else {
+ // This is OK, it just means that the frameTime is too old (all events that we have
+ // pending are in the future of the frametime). Maybe print a
+ // warning? If there are multiple devices active though, this might be normal and can
+ // just be ignored, unless none of them resulted in any consumption (in that case, this
+ // function would already return "false" so we could just leave it up to the caller).
+ }
+ }
+ std::erase_if(mBatches, [](const auto& pair) { return pair.second.empty(); });
+ return producedEvents;
+}
+
+void InputConsumerNoResampling::ensureCalledOnLooperThread(const char* func) const {
+ sp<Looper> callingThreadLooper = Looper::getForThread();
+ if (callingThreadLooper != mLooper) {
+ LOG(FATAL) << "The function " << func << " can only be called on the looper thread";
+ }
+}
+
+std::string InputConsumerNoResampling::dump() const {
+ ensureCalledOnLooperThread(__func__);
+ std::string out;
+ if (mOutboundQueue.empty()) {
+ out += "mOutboundQueue: <empty>\n";
+ } else {
+ out += "mOutboundQueue:\n";
+ // Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue
+ // doesn't provide a good way to iterate over the entire container.
+ std::queue<InputMessage> tmpQueue = mOutboundQueue;
+ while (!tmpQueue.empty()) {
+ out += std::string(" ") + outboundMessageToString(tmpQueue.front()) + "\n";
+ tmpQueue.pop();
+ }
+ }
+
+ if (mBatches.empty()) {
+ out += "mBatches: <empty>\n";
+ } else {
+ out += "mBatches:\n";
+ for (const auto& [deviceId, messages] : mBatches) {
+ out += " Device id ";
+ out += std::to_string(deviceId);
+ out += ":\n";
+ // Make a copy of mOutboundQueue for printing destructively. Unfortunately std::queue
+ // doesn't provide a good way to iterate over the entire container.
+ std::queue<InputMessage> tmpQueue = messages;
+ while (!tmpQueue.empty()) {
+ LOG_ALWAYS_FATAL_IF(tmpQueue.front().header.type != InputMessage::Type::MOTION);
+ std::unique_ptr<MotionEvent> motion = createMotionEvent(tmpQueue.front());
+ out += std::string(" ") + streamableToString(*motion) + "\n";
+ tmpQueue.pop();
+ }
+ }
+ }
+
+ return out;
+}
+
+} // namespace android
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index c348833..9333ab8 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::LogicalDisplayId::INVALID);
}
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;
@@ -271,10 +273,7 @@
}
void InputDeviceInfo::setKeyboardType(int32_t keyboardType) {
- static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
- static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- // There can be multiple subdevices with different keyboard types, set it to the highest type
- mKeyboardType = std::max(mKeyboardType, keyboardType);
+ mKeyboardType = keyboardType;
}
void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) {
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index e49f4eb..47b4228 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -26,10 +26,13 @@
#include <com_android_input_flags.h>
#include <input/InputTransport.h>
+#include <input/PrintTools.h>
#include <input/TraceTools.h>
namespace input_flags = com::android::input::flags;
+namespace android {
+
namespace {
/**
@@ -48,14 +51,6 @@
const bool DEBUG_CHANNEL_LIFECYCLE =
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Lifecycle", ANDROID_LOG_INFO);
-/**
- * Log debug messages relating to the consumer end of the transport channel.
- * Enable this via "adb shell setprop log.tag.InputTransportConsumer DEBUG" (requires restart)
- */
-
-const bool DEBUG_TRANSPORT_CONSUMER =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Consumer", ANDROID_LOG_INFO);
-
const bool IS_DEBUGGABLE_BUILD =
#if defined(__ANDROID__)
android::base::GetBoolProperty("ro.debuggable", false);
@@ -78,23 +73,6 @@
return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Publisher", ANDROID_LOG_INFO);
}
-/**
- * Log debug messages about touch event resampling.
- *
- * Enable this via "adb shell setprop log.tag.InputTransportResampling DEBUG".
- * This requires a restart on non-debuggable (e.g. user) builds, but should take effect immediately
- * on debuggable builds (e.g. userdebug).
- */
-bool debugResampling() {
- if (!IS_DEBUGGABLE_BUILD) {
- static const bool DEBUG_TRANSPORT_RESAMPLING =
- __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling",
- ANDROID_LOG_INFO);
- return DEBUG_TRANSPORT_RESAMPLING;
- }
- return __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Resampling", ANDROID_LOG_INFO);
-}
-
android::base::unique_fd dupChannelFd(int fd) {
android::base::unique_fd newFd(::dup(fd));
if (!newFd.ok()) {
@@ -110,78 +88,25 @@
return newFd;
}
-} // namespace
-
-using android::base::Result;
-using android::base::StringPrintf;
-
-namespace android {
-
// Socket buffer size. The default is typically about 128KB, which is much larger than
// we really need. So we make it smaller. It just needs to be big enough to hold
// a few dozen large multi-finger motion events in the case where an application gets
// behind processing touches.
-static const size_t SOCKET_BUFFER_SIZE = 32 * 1024;
-
-// Nanoseconds per milliseconds.
-static const nsecs_t NANOS_PER_MS = 1000000;
-
-// Latency added during resampling. A few milliseconds doesn't hurt much but
-// reduces the impact of mispredicted touch positions.
-const std::chrono::duration RESAMPLE_LATENCY = 5ms;
-
-// Minimum time difference between consecutive samples before attempting to resample.
-static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS;
-
-// Maximum time difference between consecutive samples before attempting to resample
-// by extrapolation.
-static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS;
-
-// Maximum time to predict forward from the last known state, to avoid predicting too
-// far into the future. This time is further bounded by 50% of the last time delta.
-static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS;
-
-/**
- * System property for enabling / disabling touch resampling.
- * Resampling extrapolates / interpolates the reported touch event coordinates to better
- * align them to the VSYNC signal, thus resulting in smoother scrolling performance.
- * Resampling is not needed (and should be disabled) on hardware that already
- * has touch events triggered by VSYNC.
- * Set to "1" to enable resampling (default).
- * Set to "0" to disable resampling.
- * Resampling is enabled by default.
- */
-static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling";
+constexpr size_t SOCKET_BUFFER_SIZE = 32 * 1024;
/**
* Crash if the events that are getting sent to the InputPublisher are inconsistent.
* Enable this via "adb shell setprop log.tag.InputTransportVerifyEvents DEBUG"
*/
-static bool verifyEvents() {
+bool verifyEvents() {
return input_flags::enable_outbound_event_verification() ||
__android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "VerifyEvents", ANDROID_LOG_INFO);
}
-template<typename T>
-inline static T min(const T& a, const T& b) {
- return a < b ? a : b;
-}
+} // namespace
-inline static float lerp(float a, float b, float alpha) {
- return a + alpha * (b - a);
-}
-
-inline static bool isPointerEvent(int32_t source) {
- return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
-}
-
-inline static const char* toString(bool value) {
- return value ? "true" : "false";
-}
-
-static bool shouldResampleTool(ToolType toolType) {
- return toolType == ToolType::FINGER || toolType == ToolType::UNKNOWN;
-}
+using android::base::Result;
+using android::base::StringPrintf;
// --- InputMessage ---
@@ -462,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);
@@ -533,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;
@@ -604,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,
@@ -632,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;
@@ -646,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) {
@@ -670,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());
@@ -699,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;
@@ -838,819 +764,4 @@
return android::base::Error(UNKNOWN_ERROR);
}
-// --- InputConsumer ---
-
-InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel)
- : InputConsumer(channel, isTouchResamplingEnabled()) {}
-
-InputConsumer::InputConsumer(const std::shared_ptr<InputChannel>& channel,
- bool enableTouchResampling)
- : mResampleTouch(enableTouchResampling), mChannel(channel), mMsgDeferred(false) {}
-
-InputConsumer::~InputConsumer() {
-}
-
-bool InputConsumer::isTouchResamplingEnabled() {
- return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
-}
-
-status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), toString(consumeBatches), frameTime);
-
- *outSeq = 0;
- *outEvent = nullptr;
-
- // Fetch the next input message.
- // Loop until an event can be returned or no additional events are received.
- while (!*outEvent) {
- if (mMsgDeferred) {
- // mMsg contains a valid input message from the previous call to consume
- // that has not yet been processed.
- mMsgDeferred = false;
- } else {
- // Receive a fresh message.
- status_t result = mChannel->receiveMessage(&mMsg);
- if (result == OK) {
- const auto [_, inserted] =
- mConsumeTimes.emplace(mMsg.header.seq, systemTime(SYSTEM_TIME_MONOTONIC));
- LOG_ALWAYS_FATAL_IF(!inserted, "Already have a consume time for seq=%" PRIu32,
- mMsg.header.seq);
-
- // Trace the event processing timeline - event was just read from the socket
- ATRACE_ASYNC_BEGIN("InputConsumer processing", /*cookie=*/mMsg.header.seq);
- }
- if (result) {
- // Consume the next batched event unless batches are being held for later.
- if (consumeBatches || result != WOULD_BLOCK) {
- result = consumeBatch(factory, frameTime, outSeq, outEvent);
- if (*outEvent) {
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- break;
- }
- }
- return result;
- }
- }
-
- switch (mMsg.header.type) {
- case InputMessage::Type::KEY: {
- KeyEvent* keyEvent = factory->createKeyEvent();
- if (!keyEvent) return NO_MEMORY;
-
- initializeKeyEvent(keyEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = keyEvent;
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- break;
- }
-
- case InputMessage::Type::MOTION: {
- ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source);
- if (batchIndex >= 0) {
- Batch& batch = mBatches[batchIndex];
- if (canAddSample(batch, &mMsg)) {
- batch.samples.push_back(mMsg);
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
- break;
- } else if (isPointerEvent(mMsg.body.motion.source) &&
- mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
- // No need to process events that we are going to cancel anyways
- const size_t count = batch.samples.size();
- for (size_t i = 0; i < count; i++) {
- const InputMessage& msg = batch.samples[i];
- sendFinishedSignal(msg.header.seq, false);
- }
- batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
- mBatches.erase(mBatches.begin() + batchIndex);
- } else {
- // We cannot append to the batch in progress, so we need to consume
- // the previous batch right now and defer the new message until later.
- mMsgDeferred = true;
- status_t result = consumeSamples(factory, batch, batch.samples.size(),
- outSeq, outEvent);
- mBatches.erase(mBatches.begin() + batchIndex);
- if (result) {
- return result;
- }
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- break;
- }
- }
-
- // Start a new batch if needed.
- if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
- mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- Batch batch;
- batch.samples.push_back(mMsg);
- mBatches.push_back(batch);
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
- break;
- }
-
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (!motionEvent) return NO_MEMORY;
-
- updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = motionEvent;
-
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
- break;
- }
-
- 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());
- break;
- }
-
- case InputMessage::Type::FOCUS: {
- FocusEvent* focusEvent = factory->createFocusEvent();
- if (!focusEvent) return NO_MEMORY;
-
- initializeFocusEvent(focusEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = focusEvent;
- break;
- }
-
- case InputMessage::Type::CAPTURE: {
- CaptureEvent* captureEvent = factory->createCaptureEvent();
- if (!captureEvent) return NO_MEMORY;
-
- initializeCaptureEvent(captureEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = captureEvent;
- break;
- }
-
- case InputMessage::Type::DRAG: {
- DragEvent* dragEvent = factory->createDragEvent();
- if (!dragEvent) return NO_MEMORY;
-
- initializeDragEvent(dragEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = dragEvent;
- break;
- }
-
- case InputMessage::Type::TOUCH_MODE: {
- TouchModeEvent* touchModeEvent = factory->createTouchModeEvent();
- if (!touchModeEvent) return NO_MEMORY;
-
- initializeTouchModeEvent(touchModeEvent, &mMsg);
- *outSeq = mMsg.header.seq;
- *outEvent = touchModeEvent;
- break;
- }
- }
- }
- return OK;
-}
-
-status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
- status_t result;
- for (size_t i = mBatches.size(); i > 0; ) {
- i--;
- Batch& batch = mBatches[i];
- if (frameTime < 0) {
- result = consumeSamples(factory, batch, batch.samples.size(), outSeq, outEvent);
- mBatches.erase(mBatches.begin() + i);
- return result;
- }
-
- nsecs_t sampleTime = frameTime;
- if (mResampleTouch) {
- sampleTime -= std::chrono::nanoseconds(RESAMPLE_LATENCY).count();
- }
- ssize_t split = findSampleNoLaterThan(batch, sampleTime);
- if (split < 0) {
- continue;
- }
-
- result = consumeSamples(factory, batch, split + 1, outSeq, outEvent);
- const InputMessage* next;
- if (batch.samples.empty()) {
- mBatches.erase(mBatches.begin() + i);
- next = nullptr;
- } else {
- next = &batch.samples[0];
- }
- if (!result && mResampleTouch) {
- resampleTouchState(sampleTime, static_cast<MotionEvent*>(*outEvent), next);
- }
- return result;
- }
-
- return WOULD_BLOCK;
-}
-
-status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory,
- Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) {
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
-
- uint32_t chain = 0;
- for (size_t i = 0; i < count; i++) {
- InputMessage& msg = batch.samples[i];
- updateTouchState(msg);
- if (i) {
- SeqChain seqChain;
- seqChain.seq = msg.header.seq;
- seqChain.chain = chain;
- mSeqChains.push_back(seqChain);
- addSample(motionEvent, &msg);
- } else {
- initializeMotionEvent(motionEvent, &msg);
- }
- chain = msg.header.seq;
- }
- batch.samples.erase(batch.samples.begin(), batch.samples.begin() + count);
-
- *outSeq = chain;
- *outEvent = motionEvent;
- return OK;
-}
-
-void InputConsumer::updateTouchState(InputMessage& msg) {
- if (!mResampleTouch || !isPointerEvent(msg.body.motion.source)) {
- return;
- }
-
- int32_t deviceId = msg.body.motion.deviceId;
- int32_t source = msg.body.motion.source;
-
- // Update the touch state history to incorporate the new input message.
- // If the message is in the past relative to the most recently produced resampled
- // touch, then use the resampled time and coordinates instead.
- switch (msg.body.motion.action & AMOTION_EVENT_ACTION_MASK) {
- case AMOTION_EVENT_ACTION_DOWN: {
- ssize_t index = findTouchState(deviceId, source);
- if (index < 0) {
- mTouchStates.push_back({});
- index = mTouchStates.size() - 1;
- }
- TouchState& touchState = mTouchStates[index];
- touchState.initialize(deviceId, source);
- touchState.addHistory(msg);
- break;
- }
-
- case AMOTION_EVENT_ACTION_MOVE: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates[index];
- touchState.addHistory(msg);
- rewriteMessage(touchState, msg);
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_DOWN: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates[index];
- touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
- rewriteMessage(touchState, msg);
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_POINTER_UP: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates[index];
- rewriteMessage(touchState, msg);
- touchState.lastResample.idBits.clearBit(msg.body.motion.getActionId());
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_SCROLL: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates[index];
- rewriteMessage(touchState, msg);
- }
- break;
- }
-
- case AMOTION_EVENT_ACTION_UP:
- case AMOTION_EVENT_ACTION_CANCEL: {
- ssize_t index = findTouchState(deviceId, source);
- if (index >= 0) {
- TouchState& touchState = mTouchStates[index];
- rewriteMessage(touchState, msg);
- mTouchStates.erase(mTouchStates.begin() + index);
- }
- break;
- }
- }
-}
-
-/**
- * Replace the coordinates in msg with the coordinates in lastResample, if necessary.
- *
- * If lastResample is no longer valid for a specific pointer (i.e. the lastResample time
- * is in the past relative to msg and the past two events do not contain identical coordinates),
- * then invalidate the lastResample data for that pointer.
- * If the two past events have identical coordinates, then lastResample data for that pointer will
- * remain valid, and will be used to replace these coordinates. Thus, if a certain coordinate x0 is
- * resampled to the new value x1, then x1 will always be used to replace x0 until some new value
- * not equal to x0 is received.
- */
-void InputConsumer::rewriteMessage(TouchState& state, InputMessage& msg) {
- nsecs_t eventTime = msg.body.motion.eventTime;
- for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
- uint32_t id = msg.body.motion.pointers[i].properties.id;
- if (state.lastResample.idBits.hasBit(id)) {
- if (eventTime < state.lastResample.eventTime ||
- state.recentCoordinatesAreIdentical(id)) {
- PointerCoords& msgCoords = msg.body.motion.pointers[i].coords;
- const PointerCoords& resampleCoords = state.lastResample.getPointerById(id);
- ALOGD_IF(debugResampling(), "[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id,
- resampleCoords.getX(), resampleCoords.getY(), msgCoords.getX(),
- msgCoords.getY());
- msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX());
- msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY());
- msgCoords.isResampled = true;
- } else {
- state.lastResample.idBits.clearBit(id);
- }
- }
- }
-}
-
-void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event,
- const InputMessage* next) {
- if (!mResampleTouch
- || !(isPointerEvent(event->getSource()))
- || event->getAction() != AMOTION_EVENT_ACTION_MOVE) {
- return;
- }
-
- ssize_t index = findTouchState(event->getDeviceId(), event->getSource());
- if (index < 0) {
- ALOGD_IF(debugResampling(), "Not resampled, no touch state for device.");
- return;
- }
-
- TouchState& touchState = mTouchStates[index];
- if (touchState.historySize < 1) {
- ALOGD_IF(debugResampling(), "Not resampled, no history for device.");
- return;
- }
-
- // Ensure that the current sample has all of the pointers that need to be reported.
- const History* current = touchState.getHistory(0);
- size_t pointerCount = event->getPointerCount();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t id = event->getPointerId(i);
- if (!current->idBits.hasBit(id)) {
- ALOGD_IF(debugResampling(), "Not resampled, missing id %d", id);
- return;
- }
- }
-
- // Find the data to use for resampling.
- const History* other;
- History future;
- float alpha;
- if (next) {
- // Interpolate between current sample and future sample.
- // So current->eventTime <= sampleTime <= future.eventTime.
- future.initializeFrom(*next);
- other = &future;
- nsecs_t delta = future.eventTime - current->eventTime;
- if (delta < RESAMPLE_MIN_DELTA) {
- ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
- delta);
- return;
- }
- alpha = float(sampleTime - current->eventTime) / delta;
- } else if (touchState.historySize >= 2) {
- // Extrapolate future sample using current sample and past sample.
- // So other->eventTime <= current->eventTime <= sampleTime.
- other = touchState.getHistory(1);
- nsecs_t delta = current->eventTime - other->eventTime;
- if (delta < RESAMPLE_MIN_DELTA) {
- ALOGD_IF(debugResampling(), "Not resampled, delta time is too small: %" PRId64 " ns.",
- delta);
- return;
- } else if (delta > RESAMPLE_MAX_DELTA) {
- ALOGD_IF(debugResampling(), "Not resampled, delta time is too large: %" PRId64 " ns.",
- delta);
- return;
- }
- nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION);
- if (sampleTime > maxPredict) {
- ALOGD_IF(debugResampling(),
- "Sample time is too far in the future, adjusting prediction "
- "from %" PRId64 " to %" PRId64 " ns.",
- sampleTime - current->eventTime, maxPredict - current->eventTime);
- sampleTime = maxPredict;
- }
- alpha = float(current->eventTime - sampleTime) / delta;
- } else {
- ALOGD_IF(debugResampling(), "Not resampled, insufficient data.");
- return;
- }
-
- if (current->eventTime == sampleTime) {
- // Prevents having 2 events with identical times and coordinates.
- return;
- }
-
- // Resample touch coordinates.
- History oldLastResample;
- oldLastResample.initializeFrom(touchState.lastResample);
- touchState.lastResample.eventTime = sampleTime;
- touchState.lastResample.idBits.clear();
- for (size_t i = 0; i < pointerCount; i++) {
- uint32_t id = event->getPointerId(i);
- touchState.lastResample.idToIndex[id] = i;
- touchState.lastResample.idBits.markBit(id);
- if (oldLastResample.hasPointerId(id) && touchState.recentCoordinatesAreIdentical(id)) {
- // We maintain the previously resampled value for this pointer (stored in
- // oldLastResample) when the coordinates for this pointer haven't changed since then.
- // This way we don't introduce artificial jitter when pointers haven't actually moved.
- // The isResampled flag isn't cleared as the values don't reflect what the device is
- // actually reporting.
-
- // We know here that the coordinates for the pointer haven't changed because we
- // would've cleared the resampled bit in rewriteMessage if they had. We can't modify
- // lastResample in place becasue the mapping from pointer ID to index may have changed.
- touchState.lastResample.pointers[i] = oldLastResample.getPointerById(id);
- continue;
- }
-
- PointerCoords& resampledCoords = touchState.lastResample.pointers[i];
- const PointerCoords& currentCoords = current->getPointerById(id);
- resampledCoords = currentCoords;
- resampledCoords.isResampled = true;
- if (other->idBits.hasBit(id) && shouldResampleTool(event->getToolType(i))) {
- const PointerCoords& otherCoords = other->getPointerById(id);
- resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X,
- lerp(currentCoords.getX(), otherCoords.getX(), alpha));
- resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y,
- lerp(currentCoords.getY(), otherCoords.getY(), alpha));
- ALOGD_IF(debugResampling(),
- "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), "
- "other (%0.3f, %0.3f), alpha %0.3f",
- id, resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
- currentCoords.getY(), otherCoords.getX(), otherCoords.getY(), alpha);
- } else {
- ALOGD_IF(debugResampling(), "[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", id,
- resampledCoords.getX(), resampledCoords.getY(), currentCoords.getX(),
- currentCoords.getY());
- }
- }
-
- event->addSample(sampleTime, touchState.lastResample.pointers);
-}
-
-status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, toString(handled));
-
- if (!seq) {
- ALOGE("Attempted to send a finished signal with sequence number 0.");
- return BAD_VALUE;
- }
-
- // Send finished signals for the batch sequence chain first.
- size_t seqChainCount = mSeqChains.size();
- if (seqChainCount) {
- uint32_t currentSeq = seq;
- uint32_t chainSeqs[seqChainCount];
- size_t chainIndex = 0;
- for (size_t i = seqChainCount; i > 0; ) {
- i--;
- const SeqChain& seqChain = mSeqChains[i];
- if (seqChain.seq == currentSeq) {
- currentSeq = seqChain.chain;
- chainSeqs[chainIndex++] = currentSeq;
- mSeqChains.erase(mSeqChains.begin() + i);
- }
- }
- status_t status = OK;
- while (!status && chainIndex > 0) {
- chainIndex--;
- status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled);
- }
- if (status) {
- // An error occurred so at least one signal was not sent, reconstruct the chain.
- for (;;) {
- SeqChain seqChain;
- seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq;
- seqChain.chain = chainSeqs[chainIndex];
- mSeqChains.push_back(seqChain);
- if (!chainIndex) break;
- chainIndex--;
- }
- return status;
- }
- }
-
- // Send finished signal for the last message in the batch.
- return sendUnchainedFinishedSignal(seq, handled);
-}
-
-status_t InputConsumer::sendTimeline(int32_t inputEventId,
- std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
- ALOGD_IF(DEBUG_TRANSPORT_CONSUMER,
- "channel '%s' consumer ~ sendTimeline: inputEventId=%" PRId32
- ", gpuCompletedTime=%" PRId64 ", presentTime=%" PRId64,
- mChannel->getName().c_str(), inputEventId,
- graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME],
- graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
-
- InputMessage msg;
- msg.header.type = InputMessage::Type::TIMELINE;
- msg.header.seq = 0;
- msg.body.timeline.eventId = inputEventId;
- msg.body.timeline.graphicsTimeline = std::move(graphicsTimeline);
- return mChannel->sendMessage(&msg);
-}
-
-nsecs_t InputConsumer::getConsumeTime(uint32_t seq) const {
- auto it = mConsumeTimes.find(seq);
- // Consume time will be missing if either 'finishInputEvent' is called twice, or if it was
- // called for the wrong (synthetic?) input event. Either way, it is a bug that should be fixed.
- LOG_ALWAYS_FATAL_IF(it == mConsumeTimes.end(), "Could not find consume time for seq=%" PRIu32,
- seq);
- return it->second;
-}
-
-void InputConsumer::popConsumeTime(uint32_t seq) {
- mConsumeTimes.erase(seq);
-}
-
-status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
- InputMessage msg;
- msg.header.type = InputMessage::Type::FINISHED;
- msg.header.seq = seq;
- msg.body.finished.handled = handled;
- msg.body.finished.consumeTime = getConsumeTime(seq);
- status_t result = mChannel->sendMessage(&msg);
- if (result == OK) {
- // Remove the consume time if the socket write succeeded. We will not need to ack this
- // message anymore. If the socket write did not succeed, we will try again and will still
- // need consume time.
- popConsumeTime(seq);
-
- // Trace the event processing timeline - event was just finished
- ATRACE_ASYNC_END("InputConsumer processing", /*cookie=*/seq);
- }
- return result;
-}
-
-bool InputConsumer::hasPendingBatch() const {
- return !mBatches.empty();
-}
-
-int32_t InputConsumer::getPendingBatchSource() const {
- if (mBatches.empty()) {
- return AINPUT_SOURCE_CLASS_NONE;
- }
-
- const Batch& batch = mBatches[0];
- const InputMessage& head = batch.samples[0];
- return head.body.motion.source;
-}
-
-bool InputConsumer::probablyHasInput() const {
- return hasPendingBatch() || mChannel->probablyHasInput();
-}
-
-ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
- for (size_t i = 0; i < mBatches.size(); i++) {
- const Batch& batch = mBatches[i];
- const InputMessage& head = batch.samples[0];
- if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) {
- return i;
- }
- }
- return -1;
-}
-
-ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const {
- for (size_t i = 0; i < mTouchStates.size(); i++) {
- const TouchState& touchState = mTouchStates[i];
- if (touchState.deviceId == deviceId && touchState.source == source) {
- return i;
- }
- }
- return -1;
-}
-
-void InputConsumer::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);
-}
-
-void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
-}
-
-void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.capture.eventId, msg->body.capture.pointerCaptureEnabled);
-}
-
-void InputConsumer::initializeDragEvent(DragEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.drag.eventId, msg->body.drag.x, msg->body.drag.y,
- msg->body.drag.isExiting);
-}
-
-void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
- uint32_t pointerCount = msg->body.motion.pointerCount;
- PointerProperties pointerProperties[pointerCount];
- PointerCoords pointerCoords[pointerCount];
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerProperties[i] = msg->body.motion.pointers[i].properties;
- pointerCoords[i] = msg->body.motion.pointers[i].coords;
- }
-
- ui::Transform transform;
- transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx,
- msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1});
- ui::Transform displayTransform;
- displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw,
- msg->body.motion.txRaw, 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);
-}
-
-void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.touchMode.eventId, msg->body.touchMode.isInTouchMode);
-}
-
-void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) {
- uint32_t pointerCount = msg->body.motion.pointerCount;
- PointerCoords pointerCoords[pointerCount];
- for (uint32_t i = 0; i < pointerCount; i++) {
- pointerCoords[i] = msg->body.motion.pointers[i].coords;
- }
-
- event->setMetaState(event->getMetaState() | msg->body.motion.metaState);
- event->addSample(msg->body.motion.eventTime, pointerCoords);
-}
-
-bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) {
- const InputMessage& head = batch.samples[0];
- uint32_t pointerCount = msg->body.motion.pointerCount;
- if (head.body.motion.pointerCount != pointerCount
- || head.body.motion.action != msg->body.motion.action) {
- return false;
- }
- for (size_t i = 0; i < pointerCount; i++) {
- if (head.body.motion.pointers[i].properties
- != msg->body.motion.pointers[i].properties) {
- return false;
- }
- }
- return true;
-}
-
-ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) {
- size_t numSamples = batch.samples.size();
- size_t index = 0;
- while (index < numSamples && batch.samples[index].body.motion.eventTime <= time) {
- index += 1;
- }
- return ssize_t(index) - 1;
-}
-
-std::string InputConsumer::dump() const {
- std::string out;
- out = out + "mResampleTouch = " + toString(mResampleTouch) + "\n";
- out = out + "mChannel = " + mChannel->getName() + "\n";
- out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n";
- if (mMsgDeferred) {
- out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n";
- }
- out += "Batches:\n";
- for (const Batch& batch : mBatches) {
- out += " Batch:\n";
- for (const InputMessage& msg : batch.samples) {
- out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq,
- ftl::enum_string(msg.header.type).c_str());
- switch (msg.header.type) {
- case InputMessage::Type::KEY: {
- out += android::base::StringPrintf("action=%s keycode=%" PRId32,
- KeyEvent::actionToString(
- msg.body.key.action),
- msg.body.key.keyCode);
- break;
- }
- case InputMessage::Type::MOTION: {
- out = out + "action=" + MotionEvent::actionToString(msg.body.motion.action);
- for (uint32_t i = 0; i < msg.body.motion.pointerCount; i++) {
- const float x = msg.body.motion.pointers[i].coords.getX();
- const float y = msg.body.motion.pointers[i].coords.getY();
- out += android::base::StringPrintf("\n Pointer %" PRIu32
- " : x=%.1f y=%.1f",
- i, x, y);
- }
- break;
- }
- case InputMessage::Type::FINISHED: {
- out += android::base::StringPrintf("handled=%s, consumeTime=%" PRId64,
- toString(msg.body.finished.handled),
- msg.body.finished.consumeTime);
- break;
- }
- case InputMessage::Type::FOCUS: {
- out += android::base::StringPrintf("hasFocus=%s",
- toString(msg.body.focus.hasFocus));
- break;
- }
- case InputMessage::Type::CAPTURE: {
- out += android::base::StringPrintf("hasCapture=%s",
- toString(msg.body.capture
- .pointerCaptureEnabled));
- break;
- }
- case InputMessage::Type::DRAG: {
- out += android::base::StringPrintf("x=%.1f y=%.1f, isExiting=%s",
- msg.body.drag.x, msg.body.drag.y,
- toString(msg.body.drag.isExiting));
- break;
- }
- case InputMessage::Type::TIMELINE: {
- const nsecs_t gpuCompletedTime =
- msg.body.timeline
- .graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME];
- const nsecs_t presentTime =
- msg.body.timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME];
- out += android::base::StringPrintf("inputEventId=%" PRId32
- ", gpuCompletedTime=%" PRId64
- ", presentTime=%" PRId64,
- msg.body.timeline.eventId, gpuCompletedTime,
- presentTime);
- break;
- }
- case InputMessage::Type::TOUCH_MODE: {
- out += android::base::StringPrintf("isInTouchMode=%s",
- toString(msg.body.touchMode.isInTouchMode));
- break;
- }
- }
- out += "\n";
- }
- }
- if (mBatches.empty()) {
- out += " <empty>\n";
- }
- out += "mSeqChains:\n";
- for (const SeqChain& chain : mSeqChains) {
- out += android::base::StringPrintf(" chain: seq = %" PRIu32 " chain=%" PRIu32, chain.seq,
- chain.chain);
- }
- if (mSeqChains.empty()) {
- out += " <empty>\n";
- }
- out += "mConsumeTimes:\n";
- for (const auto& [seq, consumeTime] : mConsumeTimes) {
- out += android::base::StringPrintf(" seq = %" PRIu32 " consumeTime = %" PRId64, seq,
- consumeTime);
- }
- if (mConsumeTimes.empty()) {
- out += " <empty>\n";
- }
- return out;
-}
-
} // namespace android
diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp
index e2feabc..1cf5612 100644
--- a/libs/input/KeyCharacterMap.cpp
+++ b/libs/input/KeyCharacterMap.cpp
@@ -19,16 +19,13 @@
#include <stdlib.h>
#include <string.h>
-#ifdef __linux__
-#include <binder/Parcel.h>
-#endif
#include <android/keycodes.h>
#include <attestation/HmacKeyManager.h>
+#include <binder/Parcel.h>
#include <input/InputEventLabels.h>
#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,13 +493,14 @@
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,
- INVALID_HMAC, down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode,
- 0, metaState, 0, time, time);
+ event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID, INVALID_HMAC,
+ down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, 0, keyCode, 0, metaState,
+ 0, time, time);
}
void KeyCharacterMap::addMetaKeys(Vector<KeyEvent>& outEvents,
@@ -612,7 +610,6 @@
}
}
-#ifdef __linux__
std::unique_ptr<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
if (parcel == nullptr) {
ALOGE("%s: Null parcel", __func__);
@@ -745,7 +742,6 @@
parcel->writeInt32(toAndroidKeyCode);
}
}
-#endif // __linux__
// --- KeyCharacterMap::Parser ---
diff --git a/libs/input/KeyboardClassifier.cpp b/libs/input/KeyboardClassifier.cpp
new file mode 100644
index 0000000..0c2c7be
--- /dev/null
+++ b/libs/input/KeyboardClassifier.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "KeyboardClassifier"
+
+#include <android-base/logging.h>
+#include <com_android_input_flags.h>
+#include <ftl/flags.h>
+#include <input/KeyboardClassifier.h>
+
+#include "input_cxx_bridge.rs.h"
+
+namespace input_flags = com::android::input::flags;
+
+using android::input::RustInputDeviceIdentifier;
+
+namespace android {
+
+KeyboardClassifier::KeyboardClassifier() {
+ if (input_flags::enable_keyboard_classifier()) {
+ mRustClassifier = android::input::keyboardClassifier::create();
+ }
+}
+
+KeyboardType KeyboardClassifier::getKeyboardType(DeviceId deviceId) {
+ if (mRustClassifier) {
+ return static_cast<KeyboardType>(
+ android::input::keyboardClassifier::getKeyboardType(**mRustClassifier, deviceId));
+ } else {
+ auto it = mKeyboardTypeMap.find(deviceId);
+ if (it == mKeyboardTypeMap.end()) {
+ return KeyboardType::NONE;
+ }
+ return it->second;
+ }
+}
+
+// Copied from EventHub.h
+const uint32_t DEVICE_CLASS_KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD;
+const uint32_t DEVICE_CLASS_ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY;
+
+void KeyboardClassifier::notifyKeyboardChanged(DeviceId deviceId,
+ const InputDeviceIdentifier& identifier,
+ uint32_t deviceClasses) {
+ if (mRustClassifier) {
+ RustInputDeviceIdentifier rustIdentifier;
+ rustIdentifier.name = identifier.name;
+ rustIdentifier.location = identifier.location;
+ rustIdentifier.unique_id = identifier.uniqueId;
+ rustIdentifier.bus = identifier.bus;
+ rustIdentifier.vendor = identifier.vendor;
+ rustIdentifier.product = identifier.product;
+ rustIdentifier.version = identifier.version;
+ rustIdentifier.descriptor = identifier.descriptor;
+ android::input::keyboardClassifier::notifyKeyboardChanged(**mRustClassifier, deviceId,
+ rustIdentifier, deviceClasses);
+ } else {
+ bool isKeyboard = (deviceClasses & DEVICE_CLASS_KEYBOARD) != 0;
+ bool hasAlphabeticKey = (deviceClasses & DEVICE_CLASS_ALPHAKEY) != 0;
+ mKeyboardTypeMap.insert_or_assign(deviceId,
+ isKeyboard ? (hasAlphabeticKey
+ ? KeyboardType::ALPHABETIC
+ : KeyboardType::NON_ALPHABETIC)
+ : KeyboardType::NONE);
+ }
+}
+
+void KeyboardClassifier::processKey(DeviceId deviceId, int32_t evdevCode, uint32_t metaState) {
+ if (mRustClassifier &&
+ !android::input::keyboardClassifier::isFinalized(**mRustClassifier, deviceId)) {
+ android::input::keyboardClassifier::processKey(**mRustClassifier, deviceId, evdevCode,
+ metaState);
+ }
+}
+
+} // namespace android
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 0412d08..ccf018e 100644
--- a/libs/input/MotionPredictorMetricsManager.cpp
+++ b/libs/input/MotionPredictorMetricsManager.cpp
@@ -21,14 +21,13 @@
#include <algorithm>
#include <android-base/logging.h>
+#ifdef __ANDROID__
+#include <statslog_libinput.h>
+#endif // __ANDROID__
#include "Eigen/Core"
#include "Eigen/Geometry"
-#ifdef __ANDROID__
-#include <statslog_libinput.h>
-#endif
-
namespace android {
namespace {
@@ -48,22 +47,20 @@
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
+ android::libinput::stats_write(android::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 // __ANDROID__
}
MotionPredictorMetricsManager::MotionPredictorMetricsManager(
@@ -113,7 +110,12 @@
// Adds new predictions to mRecentPredictions and maintains the invariant that elements are
// sorted in ascending order of targetTimestamp.
void MotionPredictorMetricsManager::onPredict(const MotionEvent& predictionEvent) {
- for (size_t i = 0; i < predictionEvent.getHistorySize() + 1; ++i) {
+ const size_t numPredictions = predictionEvent.getHistorySize() + 1;
+ if (numPredictions > mMaxNumPredictions) {
+ LOG(WARNING) << "numPredictions (" << numPredictions << ") > mMaxNumPredictions ("
+ << mMaxNumPredictions << "). Ignoring extra predictions in metrics.";
+ }
+ for (size_t i = 0; (i < numPredictions) && (i < mMaxNumPredictions); ++i) {
// Convert MotionEvent to PredictionPoint.
const PointerCoords* coords =
predictionEvent.getHistoricalRawPointerCoords(/*pointerIndex=*/0, i);
@@ -325,42 +327,44 @@
mAtomFields[i].highVelocityOffTrajectoryRmse =
static_cast<int>(offTrajectoryRmse * 1000);
}
+ }
- // Scale-invariant errors: reported only for the last time bucket, where the values
- // represent an average across all time buckets.
- if (i + 1 == mMaxNumPredictions) {
- // Compute error averages.
- float alongTrajectoryRmseSum = 0;
- float offTrajectoryRmseSum = 0;
- for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
- // If we have general errors (checked above), we should always also have
- // scale-invariant errors.
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantErrorsCount == 0,
- "mAggregatedMetrics[%zu].scaleInvariantErrorsCount is 0", j);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
- alongTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
-
- LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
- "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
- "should not be negative",
- j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
- offTrajectoryRmseSum +=
- std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
- mAggregatedMetrics[j].scaleInvariantErrorsCount);
+ // Scale-invariant errors: the average scale-invariant error across all time buckets
+ // is reported in the last time bucket.
+ {
+ // Compute error averages.
+ float alongTrajectoryRmseSum = 0;
+ float offTrajectoryRmseSum = 0;
+ int bucket_count = 0;
+ for (size_t j = 0; j < mAggregatedMetrics.size(); ++j) {
+ if (mAggregatedMetrics[j].scaleInvariantErrorsCount == 0) {
+ continue;
}
- const float averageAlongTrajectoryRmse =
- alongTrajectoryRmseSum / mAggregatedMetrics.size();
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantAlongTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse);
+ alongTrajectoryRmseSum +=
+ std::sqrt(mAggregatedMetrics[j].scaleInvariantAlongTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ LOG_ALWAYS_FATAL_IF(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse < 0,
+ "mAggregatedMetrics[%zu].scaleInvariantOffTrajectorySse = %f "
+ "should not be negative",
+ j, mAggregatedMetrics[j].scaleInvariantOffTrajectorySse);
+ offTrajectoryRmseSum += std::sqrt(mAggregatedMetrics[j].scaleInvariantOffTrajectorySse /
+ mAggregatedMetrics[j].scaleInvariantErrorsCount);
+
+ ++bucket_count;
+ }
+
+ if (bucket_count > 0) {
+ const float averageAlongTrajectoryRmse = alongTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantAlongTrajectoryRmse =
static_cast<int>(averageAlongTrajectoryRmse * 1000);
- const float averageOffTrajectoryRmse = offTrajectoryRmseSum / mAggregatedMetrics.size();
+ const float averageOffTrajectoryRmse = offTrajectoryRmseSum / bucket_count;
mAtomFields.back().scaleInvariantOffTrajectoryRmse =
static_cast<int>(averageOffTrajectoryRmse * 1000);
}
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..a77dfa5 100644
--- a/libs/input/android/os/IInputConstants.aidl
+++ b/libs/input/android/os/IInputConstants.aidl
@@ -49,12 +49,130 @@
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;
+
+ /**
+ * This flag indicates that the event has a valid value for AXIS_ORIENTATION.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80;
+
+ /**
+ * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine
+ * the direction in which the tool is pointing. The value of the orientation axis will be in
+ * the range [-pi, pi], which represents a full circle. This is usually supported by devices
+ * like styluses.
+ *
+ * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing
+ * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2],
+ * which represents half a circle. This is usually the case for devices like touchscreens and
+ * touchpads, for which it is difficult to tell which direction along the major axis of the
+ * touch ellipse the finger is pointing.
+ *
+ * This is a private flag that is not used in Java.
+ * @hide
+ */
+ const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100;
+
+ /**
* 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;
@@ -141,4 +259,157 @@
* time to adjust to changes in direction.
*/
const int VELOCITY_TRACKER_STRATEGY_LEGACY = 9;
+
+
+ /*
+ * Input device class: Keyboard
+ * The input device is a keyboard or has buttons.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_KEYBOARD = 0x00000001;
+
+ /*
+ * Input device class: Alphakey
+ * The input device is an alpha-numeric keyboard (not just a dial pad).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_ALPHAKEY = 0x00000002;
+
+ /*
+ * Input device class: Touch
+ * The input device is a touchscreen or a touchpad (either single-touch or multi-touch).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_TOUCH = 0x00000004;
+
+ /*
+ * Input device class: Cursor
+ * The input device is a cursor device such as a trackball or mouse.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_CURSOR = 0x00000008;
+
+ /*
+ * Input device class: Multi-touch
+ * The input device is a multi-touch touchscreen or touchpad.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_TOUCH_MT = 0x00000010;
+
+ /*
+ * Input device class: Dpad
+ * The input device is a directional pad (implies keyboard, has DPAD keys).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_DPAD = 0x00000020;
+
+ /*
+ * Input device class: Gamepad
+ * The input device is a gamepad (implies keyboard, has BUTTON keys).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_GAMEPAD = 0x00000040;
+
+ /*
+ * Input device class: Switch
+ * The input device has switches.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_SWITCH = 0x00000080;
+
+ /*
+ * Input device class: Joystick
+ * The input device is a joystick (implies gamepad, has joystick absolute axes).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_JOYSTICK = 0x00000100;
+
+ /*
+ * Input device class: Vibrator
+ * The input device has a vibrator (supports FF_RUMBLE).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_VIBRATOR = 0x00000200;
+
+ /*
+ * Input device class: Mic
+ * The input device has a microphone.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_MIC = 0x00000400;
+
+ /*
+ * Input device class: External Stylus
+ * The input device is an external stylus (has data we want to fuse with touch data).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_EXTERNAL_STYLUS = 0x00000800;
+
+ /*
+ * Input device class: Rotary Encoder
+ * The input device has a rotary encoder.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_ROTARY_ENCODER = 0x00001000;
+
+ /*
+ * Input device class: Sensor
+ * The input device has a sensor like accelerometer, gyro, etc.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_SENSOR = 0x00002000;
+
+ /*
+ * Input device class: Battery
+ * The input device has a battery.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_BATTERY = 0x00004000;
+
+ /*
+ * Input device class: Light
+ * The input device has sysfs controllable lights.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_LIGHT = 0x00008000;
+
+ /*
+ * Input device class: Touchpad
+ * The input device is a touchpad, requiring an on-screen cursor.
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_TOUCHPAD = 0x00010000;
+
+ /*
+ * Input device class: Virtual
+ * The input device is virtual (not a real device, not part of UI configuration).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_VIRTUAL = 0x20000000;
+
+ /*
+ * Input device class: External
+ * The input device is external (not built-in).
+ *
+ * @hide
+ */
+ const int DEVICE_CLASS_EXTERNAL = 0x40000000;
}
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 bdec5c3..a2192cb 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"
@@ -87,6 +80,7 @@
flag {
name: "override_key_behavior_permission_apis"
+ is_exported: true
namespace: "input"
description: "enable override key behavior permission APIs"
bug: "309018874"
@@ -115,6 +109,7 @@
flag {
name: "input_device_view_behavior_api"
+ is_exported: true
namespace: "input"
description: "Controls the API to provide InputDevice view behavior."
bug: "246946631"
@@ -126,3 +121,39 @@
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"
+}
+
+flag {
+ name: "enable_keyboard_classifier"
+ namespace: "input"
+ description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic"
+ bug: "263559234"
+}
diff --git a/libs/input/rust/input.rs b/libs/input/rust/input.rs
index 705c959..564d94d 100644
--- a/libs/input/rust/input.rs
+++ b/libs/input/rust/input.rs
@@ -16,13 +16,26 @@
//! Common definitions of the Android Input Framework in rust.
+use crate::ffi::RustInputDeviceIdentifier;
use bitflags::bitflags;
+use inputconstants::aidl::android::os::IInputConstants;
use std::fmt;
/// The InputDevice ID.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct DeviceId(pub i32);
+/// The InputDevice equivalent for Rust inputflinger
+#[derive(Debug)]
+pub struct InputDevice {
+ /// InputDevice ID
+ pub device_id: DeviceId,
+ /// InputDevice unique identifier
+ pub identifier: RustInputDeviceIdentifier,
+ /// InputDevice classes (equivalent to EventHub InputDeviceClass)
+ pub classes: DeviceClass,
+}
+
#[repr(u32)]
pub enum SourceClass {
None = input_bindgen::AINPUT_SOURCE_CLASS_NONE,
@@ -182,18 +195,29 @@
/// 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 = IInputConstants::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 = IInputConstants::MOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED as u32;
+ /// FLAG_HOVER_EXIT_PENDING
+ const HOVER_EXIT_PENDING = IInputConstants::MOTION_EVENT_FLAG_HOVER_EXIT_PENDING as u32;
+ /// FLAG_IS_GENERATED_GESTURE
+ const IS_GENERATED_GESTURE = IInputConstants::MOTION_EVENT_FLAG_IS_GENERATED_GESTURE as u32;
+ /// FLAG_CANCELED
+ const CANCELED = IInputConstants::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 = IInputConstants::MOTION_EVENT_FLAG_NO_FOCUS_CHANGE as u32;
+ /// PRIVATE_FLAG_SUPPORTS_ORIENTATION
+ const PRIVATE_SUPPORTS_ORIENTATION = IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION as u32;
+ /// PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION
+ const PRIVATE_SUPPORTS_DIRECTIONAL_ORIENTATION =
+ IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION as u32;
+ /// FLAG_IS_ACCESSIBILITY_EVENT
+ const IS_ACCESSIBILITY_EVENT = IInputConstants::INPUT_EVENT_FLAG_IS_ACCESSIBILITY_EVENT as u32;
+ /// FLAG_TAINTED
+ const TAINTED = IInputConstants::INPUT_EVENT_FLAG_TAINTED as u32;
+ /// FLAG_TARGET_ACCESSIBILITY_FOCUS
+ const TARGET_ACCESSIBILITY_FOCUS = IInputConstants::MOTION_EVENT_FLAG_TARGET_ACCESSIBILITY_FOCUS as u32;
}
}
@@ -205,6 +229,107 @@
}
}
+bitflags! {
+ /// Device class of the input device. These are duplicated from Eventhub.h
+ /// We need to make sure the two version remain in sync when adding new classes.
+ #[derive(Debug, PartialEq)]
+ pub struct DeviceClass: u32 {
+ /// The input device is a keyboard or has buttons
+ const Keyboard = IInputConstants::DEVICE_CLASS_KEYBOARD as u32;
+ /// The input device is an alpha-numeric keyboard (not just a dial pad)
+ const AlphabeticKey = IInputConstants::DEVICE_CLASS_ALPHAKEY as u32;
+ /// The input device is a touchscreen or a touchpad (either single-touch or multi-touch)
+ const Touch = IInputConstants::DEVICE_CLASS_TOUCH as u32;
+ /// The input device is a cursor device such as a trackball or mouse.
+ const Cursor = IInputConstants::DEVICE_CLASS_CURSOR as u32;
+ /// The input device is a multi-touch touchscreen or touchpad.
+ const MultiTouch = IInputConstants::DEVICE_CLASS_TOUCH_MT as u32;
+ /// The input device is a directional pad (implies keyboard, has DPAD keys).
+ const Dpad = IInputConstants::DEVICE_CLASS_DPAD as u32;
+ /// The input device is a gamepad (implies keyboard, has BUTTON keys).
+ const Gamepad = IInputConstants::DEVICE_CLASS_GAMEPAD as u32;
+ /// The input device has switches.
+ const Switch = IInputConstants::DEVICE_CLASS_SWITCH as u32;
+ /// The input device is a joystick (implies gamepad, has joystick absolute axes).
+ const Joystick = IInputConstants::DEVICE_CLASS_JOYSTICK as u32;
+ /// The input device has a vibrator (supports FF_RUMBLE).
+ const Vibrator = IInputConstants::DEVICE_CLASS_VIBRATOR as u32;
+ /// The input device has a microphone.
+ const Mic = IInputConstants::DEVICE_CLASS_MIC as u32;
+ /// The input device is an external stylus (has data we want to fuse with touch data).
+ const ExternalStylus = IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS as u32;
+ /// The input device has a rotary encoder
+ const RotaryEncoder = IInputConstants::DEVICE_CLASS_ROTARY_ENCODER as u32;
+ /// The input device has a sensor like accelerometer, gyro, etc
+ const Sensor = IInputConstants::DEVICE_CLASS_SENSOR as u32;
+ /// The input device has a battery
+ const Battery = IInputConstants::DEVICE_CLASS_BATTERY as u32;
+ /// The input device has sysfs controllable lights
+ const Light = IInputConstants::DEVICE_CLASS_LIGHT as u32;
+ /// The input device is a touchpad, requiring an on-screen cursor.
+ const Touchpad = IInputConstants::DEVICE_CLASS_TOUCHPAD as u32;
+ /// The input device is virtual (not a real device, not part of UI configuration).
+ const Virtual = IInputConstants::DEVICE_CLASS_VIRTUAL as u32;
+ /// The input device is external (not built-in).
+ const External = IInputConstants::DEVICE_CLASS_EXTERNAL as u32;
+ }
+}
+
+bitflags! {
+ /// Modifier state flags
+ #[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ModifierState: u32 {
+ /// No meta keys are pressed
+ const None = input_bindgen::AMETA_NONE;
+ /// This mask is used to check whether one of the ALT meta keys is pressed
+ const AltOn = input_bindgen::AMETA_ALT_ON;
+ /// This mask is used to check whether the left ALT meta key is pressed
+ const AltLeftOn = input_bindgen::AMETA_ALT_LEFT_ON;
+ /// This mask is used to check whether the right ALT meta key is pressed
+ const AltRightOn = input_bindgen::AMETA_ALT_RIGHT_ON;
+ /// This mask is used to check whether one of the SHIFT meta keys is pressed
+ const ShiftOn = input_bindgen::AMETA_SHIFT_ON;
+ /// This mask is used to check whether the left SHIFT meta key is pressed
+ const ShiftLeftOn = input_bindgen::AMETA_SHIFT_LEFT_ON;
+ /// This mask is used to check whether the right SHIFT meta key is pressed
+ const ShiftRightOn = input_bindgen::AMETA_SHIFT_RIGHT_ON;
+ /// This mask is used to check whether the SYM meta key is pressed
+ const SymOn = input_bindgen::AMETA_SYM_ON;
+ /// This mask is used to check whether the FUNCTION meta key is pressed
+ const FunctionOn = input_bindgen::AMETA_FUNCTION_ON;
+ /// This mask is used to check whether one of the CTRL meta keys is pressed
+ const CtrlOn = input_bindgen::AMETA_CTRL_ON;
+ /// This mask is used to check whether the left CTRL meta key is pressed
+ const CtrlLeftOn = input_bindgen::AMETA_CTRL_LEFT_ON;
+ /// This mask is used to check whether the right CTRL meta key is pressed
+ const CtrlRightOn = input_bindgen::AMETA_CTRL_RIGHT_ON;
+ /// This mask is used to check whether one of the META meta keys is pressed
+ const MetaOn = input_bindgen::AMETA_META_ON;
+ /// This mask is used to check whether the left META meta key is pressed
+ const MetaLeftOn = input_bindgen::AMETA_META_LEFT_ON;
+ /// This mask is used to check whether the right META meta key is pressed
+ const MetaRightOn = input_bindgen::AMETA_META_RIGHT_ON;
+ /// This mask is used to check whether the CAPS LOCK meta key is on
+ const CapsLockOn = input_bindgen::AMETA_CAPS_LOCK_ON;
+ /// This mask is used to check whether the NUM LOCK meta key is on
+ const NumLockOn = input_bindgen::AMETA_NUM_LOCK_ON;
+ /// This mask is used to check whether the SCROLL LOCK meta key is on
+ const ScrollLockOn = input_bindgen::AMETA_SCROLL_LOCK_ON;
+ }
+}
+
+/// A rust enum representation of a Keyboard type.
+#[repr(u32)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub enum KeyboardType {
+ /// KEYBOARD_TYPE_NONE
+ None = input_bindgen::AINPUT_KEYBOARD_TYPE_NONE,
+ /// KEYBOARD_TYPE_NON_ALPHABETIC
+ NonAlphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+ /// KEYBOARD_TYPE_ALPHABETIC
+ Alphabetic = input_bindgen::AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+}
+
#[cfg(test)]
mod tests {
use crate::input::SourceClass;
diff --git a/libs/input/rust/keyboard_classifier.rs b/libs/input/rust/keyboard_classifier.rs
new file mode 100644
index 0000000..1063fac
--- /dev/null
+++ b/libs/input/rust/keyboard_classifier.rs
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+//! Contains the KeyboardClassifier, that tries to identify whether an Input device is an
+//! alphabetic or non-alphabetic keyboard. It also tracks the KeyEvents produced by the device
+//! in order to verify/change the inferred keyboard type.
+//!
+//! Initial classification:
+//! - If DeviceClass includes Dpad, Touch, Cursor, MultiTouch, ExternalStylus, Touchpad, Dpad,
+//! Gamepad, Switch, Joystick, RotaryEncoder => KeyboardType::NonAlphabetic
+//! - Otherwise if DeviceClass has Keyboard and not AlphabeticKey => KeyboardType::NonAlphabetic
+//! - Otherwise if DeviceClass has both Keyboard and AlphabeticKey => KeyboardType::Alphabetic
+//!
+//! On process keys:
+//! - If KeyboardType::NonAlphabetic and we receive alphabetic key event, then change type to
+//! KeyboardType::Alphabetic. Once changed, no further changes. (i.e. verified = true)
+//! - TODO(b/263559234): If KeyboardType::Alphabetic and we don't receive any alphabetic key event
+//! across multiple device connections in a time period, then change type to
+//! KeyboardType::NonAlphabetic. Once changed, it can still change back to Alphabetic
+//! (i.e. verified = false).
+//!
+//! TODO(b/263559234): Data store implementation to store information about past classification
+
+use crate::input::{DeviceId, InputDevice, KeyboardType};
+use crate::{DeviceClass, ModifierState};
+use std::collections::HashMap;
+
+/// The KeyboardClassifier is used to classify a keyboard device into non-keyboard, alphabetic
+/// keyboard or non-alphabetic keyboard
+#[derive(Default)]
+pub struct KeyboardClassifier {
+ device_map: HashMap<DeviceId, KeyboardInfo>,
+}
+
+struct KeyboardInfo {
+ _device: InputDevice,
+ keyboard_type: KeyboardType,
+ is_finalized: bool,
+}
+
+impl KeyboardClassifier {
+ /// Create a new KeyboardClassifier
+ pub fn new() -> Self {
+ Default::default()
+ }
+
+ /// Adds keyboard to KeyboardClassifier
+ pub fn notify_keyboard_changed(&mut self, device: InputDevice) {
+ let (keyboard_type, is_finalized) = self.classify_keyboard(&device);
+ self.device_map.insert(
+ device.device_id,
+ KeyboardInfo { _device: device, keyboard_type, is_finalized },
+ );
+ }
+
+ /// Get keyboard type for a tracked keyboard in KeyboardClassifier
+ pub fn get_keyboard_type(&self, device_id: DeviceId) -> KeyboardType {
+ return if let Some(keyboard) = self.device_map.get(&device_id) {
+ keyboard.keyboard_type
+ } else {
+ KeyboardType::None
+ };
+ }
+
+ /// Tells if keyboard type classification is finalized. Once finalized the classification can't
+ /// change until device is reconnected again.
+ ///
+ /// Finalized devices are either "alphabetic" keyboards or keyboards in blocklist or
+ /// allowlist that are explicitly categorized and won't change with future key events
+ pub fn is_finalized(&self, device_id: DeviceId) -> bool {
+ return if let Some(keyboard) = self.device_map.get(&device_id) {
+ keyboard.is_finalized
+ } else {
+ false
+ };
+ }
+
+ /// Process a key event and change keyboard type if required.
+ /// - If any key event occurs, the keyboard type will change from None to NonAlphabetic
+ /// - If an alphabetic key occurs, the keyboard type will change to Alphabetic
+ pub fn process_key(
+ &mut self,
+ device_id: DeviceId,
+ evdev_code: i32,
+ modifier_state: ModifierState,
+ ) {
+ if let Some(keyboard) = self.device_map.get_mut(&device_id) {
+ // Ignore all key events with modifier state since they can be macro shortcuts used by
+ // some non-keyboard peripherals like TV remotes, game controllers, etc.
+ if modifier_state.bits() != 0 {
+ return;
+ }
+ if Self::is_alphabetic_key(&evdev_code) {
+ keyboard.keyboard_type = KeyboardType::Alphabetic;
+ keyboard.is_finalized = true;
+ }
+ }
+ }
+
+ fn classify_keyboard(&self, device: &InputDevice) -> (KeyboardType, bool) {
+ // This should never happen but having keyboard device class is necessary to be classified
+ // as any type of keyboard.
+ if !device.classes.contains(DeviceClass::Keyboard) {
+ return (KeyboardType::None, true);
+ }
+ // Normal classification for internal and virtual keyboards
+ if !device.classes.contains(DeviceClass::External)
+ || device.classes.contains(DeviceClass::Virtual)
+ {
+ return if device.classes.contains(DeviceClass::AlphabeticKey) {
+ (KeyboardType::Alphabetic, true)
+ } else {
+ (KeyboardType::NonAlphabetic, true)
+ };
+ }
+ // Any composite device with multiple device classes should be categorized as non-alphabetic
+ // keyboard initially
+ if device.classes.contains(DeviceClass::Touch)
+ || device.classes.contains(DeviceClass::Cursor)
+ || device.classes.contains(DeviceClass::MultiTouch)
+ || device.classes.contains(DeviceClass::ExternalStylus)
+ || device.classes.contains(DeviceClass::Touchpad)
+ || device.classes.contains(DeviceClass::Dpad)
+ || device.classes.contains(DeviceClass::Gamepad)
+ || device.classes.contains(DeviceClass::Switch)
+ || device.classes.contains(DeviceClass::Joystick)
+ || device.classes.contains(DeviceClass::RotaryEncoder)
+ {
+ // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the
+ // kernel, we no longer need to process key events to verify.
+ return (
+ KeyboardType::NonAlphabetic,
+ !device.classes.contains(DeviceClass::AlphabeticKey),
+ );
+ }
+ // Only devices with "Keyboard" and "AlphabeticKey" should be classified as full keyboard
+ if device.classes.contains(DeviceClass::AlphabeticKey) {
+ (KeyboardType::Alphabetic, true)
+ } else {
+ // If categorized as NonAlphabetic and no device class AlphabeticKey reported by the
+ // kernel, we no longer need to process key events to verify.
+ (KeyboardType::NonAlphabetic, true)
+ }
+ }
+
+ fn is_alphabetic_key(evdev_code: &i32) -> bool {
+ // Keyboard alphabetic row 1 (Q W E R T Y U I O P [ ])
+ (16..=27).contains(evdev_code)
+ // Keyboard alphabetic row 2 (A S D F G H J K L ; ' `)
+ || (30..=41).contains(evdev_code)
+ // Keyboard alphabetic row 3 (\ Z X C V B N M , . /)
+ || (43..=53).contains(evdev_code)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::input::{DeviceId, InputDevice, KeyboardType};
+ use crate::keyboard_classifier::KeyboardClassifier;
+ use crate::{DeviceClass, ModifierState, RustInputDeviceIdentifier};
+
+ static DEVICE_ID: DeviceId = DeviceId(1);
+ static KEY_A: i32 = 30;
+ static KEY_1: i32 = 2;
+
+ #[test]
+ fn classify_external_alphabetic_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard | DeviceClass::AlphabeticKey | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic);
+ assert!(classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_external_non_alphabetic_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier
+ .notify_keyboard_changed(create_device(DeviceClass::Keyboard | DeviceClass::External));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_mouse_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Cursor
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_touchpad_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Touchpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_stylus_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::ExternalStylus
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_dpad_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_joystick_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Joystick
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn classify_gamepad_pretending_as_keyboard() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Gamepad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn reclassify_keyboard_on_alphabetic_key_event() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+
+ // on alphabetic key event
+ classifier.process_key(DEVICE_ID, KEY_A, ModifierState::None);
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::Alphabetic);
+ assert!(classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn dont_reclassify_keyboard_on_non_alphabetic_key_event() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+
+ // on number key event
+ classifier.process_key(DEVICE_ID, KEY_1, ModifierState::None);
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ #[test]
+ fn dont_reclassify_keyboard_on_alphabetic_key_event_with_modifiers() {
+ let mut classifier = KeyboardClassifier::new();
+ classifier.notify_keyboard_changed(create_device(
+ DeviceClass::Keyboard
+ | DeviceClass::Dpad
+ | DeviceClass::AlphabeticKey
+ | DeviceClass::External,
+ ));
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+
+ classifier.process_key(DEVICE_ID, KEY_A, ModifierState::CtrlOn);
+ assert_eq!(classifier.get_keyboard_type(DEVICE_ID), KeyboardType::NonAlphabetic);
+ assert!(!classifier.is_finalized(DEVICE_ID));
+ }
+
+ fn create_device(classes: DeviceClass) -> InputDevice {
+ InputDevice {
+ device_id: DEVICE_ID,
+ identifier: RustInputDeviceIdentifier {
+ name: "test_device".to_string(),
+ location: "location".to_string(),
+ unique_id: "unique_id".to_string(),
+ bus: 123,
+ vendor: 234,
+ product: 345,
+ version: 567,
+ descriptor: "descriptor".to_string(),
+ },
+ classes,
+ }
+ }
+}
diff --git a/libs/input/rust/lib.rs b/libs/input/rust/lib.rs
index 01d9599..5010475 100644
--- a/libs/input/rust/lib.rs
+++ b/libs/input/rust/lib.rs
@@ -18,9 +18,13 @@
mod input;
mod input_verifier;
+mod keyboard_classifier;
-pub use input::{DeviceId, MotionAction, MotionFlags, Source};
+pub use input::{
+ DeviceClass, DeviceId, InputDevice, ModifierState, MotionAction, MotionFlags, Source,
+};
pub use input_verifier::InputVerifier;
+pub use keyboard_classifier::KeyboardClassifier;
#[cxx::bridge(namespace = "android::input")]
#[allow(unsafe_op_in_unsafe_fn)]
@@ -47,7 +51,8 @@
/// }
/// ```
type InputVerifier;
- fn create(name: String) -> Box<InputVerifier>;
+ #[cxx_name = create]
+ fn create_input_verifier(name: String) -> Box<InputVerifier>;
fn process_movement(
verifier: &mut InputVerifier,
device_id: i32,
@@ -59,15 +64,53 @@
fn reset_device(verifier: &mut InputVerifier, device_id: i32);
}
+ #[namespace = "android::input::keyboardClassifier"]
+ extern "Rust" {
+ /// Used to classify a keyboard into alphabetic and non-alphabetic
+ type KeyboardClassifier;
+ #[cxx_name = create]
+ fn create_keyboard_classifier() -> Box<KeyboardClassifier>;
+ #[cxx_name = notifyKeyboardChanged]
+ fn notify_keyboard_changed(
+ classifier: &mut KeyboardClassifier,
+ device_id: i32,
+ identifier: RustInputDeviceIdentifier,
+ device_classes: u32,
+ );
+ #[cxx_name = getKeyboardType]
+ fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32;
+ #[cxx_name = isFinalized]
+ fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool;
+ #[cxx_name = processKey]
+ fn process_key(
+ classifier: &mut KeyboardClassifier,
+ device_id: i32,
+ evdev_code: i32,
+ modifier_state: u32,
+ );
+ }
+
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct RustPointerProperties {
pub id: i32,
}
+
+ #[derive(Debug)]
+ pub struct RustInputDeviceIdentifier {
+ pub name: String,
+ pub location: String,
+ pub unique_id: String,
+ pub bus: u16,
+ pub vendor: u16,
+ pub product: u16,
+ pub version: u16,
+ pub descriptor: String,
+ }
}
-use crate::ffi::RustPointerProperties;
+use crate::ffi::{RustInputDeviceIdentifier, RustPointerProperties};
-fn create(name: String) -> Box<InputVerifier> {
+fn create_input_verifier(name: String) -> Box<InputVerifier> {
Box::new(InputVerifier::new(&name, ffi::shouldLog("InputVerifierLogEvents")))
}
@@ -79,12 +122,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(),
@@ -95,3 +146,53 @@
fn reset_device(verifier: &mut InputVerifier, device_id: i32) {
verifier.reset_device(DeviceId(device_id));
}
+
+fn create_keyboard_classifier() -> Box<KeyboardClassifier> {
+ Box::new(KeyboardClassifier::new())
+}
+
+fn notify_keyboard_changed(
+ classifier: &mut KeyboardClassifier,
+ device_id: i32,
+ identifier: RustInputDeviceIdentifier,
+ device_classes: u32,
+) {
+ let classes = DeviceClass::from_bits(device_classes);
+ if classes.is_none() {
+ panic!(
+ "The conversion of device class 0x{:08x} failed, please check if some device classes
+ have not been added to DeviceClass.",
+ device_classes
+ );
+ }
+ classifier.notify_keyboard_changed(InputDevice {
+ device_id: DeviceId(device_id),
+ identifier,
+ classes: classes.unwrap(),
+ });
+}
+
+fn get_keyboard_type(classifier: &mut KeyboardClassifier, device_id: i32) -> u32 {
+ classifier.get_keyboard_type(DeviceId(device_id)) as u32
+}
+
+fn is_finalized(classifier: &mut KeyboardClassifier, device_id: i32) -> bool {
+ classifier.is_finalized(DeviceId(device_id))
+}
+
+fn process_key(
+ classifier: &mut KeyboardClassifier,
+ device_id: i32,
+ evdev_code: i32,
+ meta_state: u32,
+) {
+ let modifier_state = ModifierState::from_bits(meta_state);
+ if modifier_state.is_none() {
+ panic!(
+ "The conversion of meta state 0x{:08x} failed, please check if some meta state
+ have not been added to ModifierState.",
+ meta_state
+ );
+ }
+ classifier.process_key(DeviceId(device_id), evdev_code, modifier_state.unwrap());
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 93af4c2..e9d799e 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -19,6 +19,7 @@
"InputDevice_test.cpp",
"InputEvent_test.cpp",
"InputPublisherAndConsumer_test.cpp",
+ "InputPublisherAndConsumerNoResampling_test.cpp",
"InputVerifier_test.cpp",
"MotionPredictor_test.cpp",
"MotionPredictorMetricsManager_test.cpp",
@@ -35,6 +36,7 @@
"tensorflow_headers",
],
static_libs: [
+ "libflagtest",
"libgmock",
"libgui_window_info_static",
"libinput",
@@ -63,6 +65,7 @@
"libcutils",
"liblog",
"libPlatformProperties",
+ "libstatslog",
"libtinyxml2",
"libutils",
"server_configurable_flags",
@@ -76,19 +79,17 @@
},
test_suites: ["device-tests"],
target: {
+ android: {
+ static_libs: [
+ "libstatslog_libinput",
+ "libstatssocket_lazy",
+ ],
+ },
host: {
sanitize: {
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/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 60feb53..02d4c07 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -16,8 +16,6 @@
#include <array>
-#include "TestHelpers.h"
-
#include <unistd.h>
#include <time.h>
#include <errno.h>
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index a965573..3717f49 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -21,15 +21,43 @@
#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;
+namespace {
-static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+// Default display id.
+constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
+
+constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+
+constexpr auto POINTER_0_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+constexpr auto POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+constexpr auto POINTER_0_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+constexpr auto POINTER_1_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+std::array<float, 9> asFloat9(const ui::Transform& t) {
+ std::array<float, 9> mat{};
+ mat[0] = t[0][0];
+ mat[1] = t[1][0];
+ mat[2] = t[2][0];
+ mat[3] = t[0][1];
+ mat[4] = t[1][1];
+ mat[5] = t[2][1];
+ mat[6] = t[0][2];
+ mat[7] = t[1][2];
+ mat[8] = t[2][2];
+ return mat;
+}
class BaseTest : public testing::Test {
protected:
@@ -38,6 +66,8 @@
22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
};
+} // namespace
+
// --- PointerCoordsTest ---
class PointerCoordsTest : public BaseTest {
@@ -216,7 +246,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());
}
@@ -332,13 +362,15 @@
}
void MotionEventTest::initializeEventWithHistory(MotionEvent* event) {
+ const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC,
- AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED,
- AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY,
- MotionClassification::NONE, mTransform, 2.0f, 2.1f,
- AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION,
- mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2,
- mPointerProperties, mSamples[0].pointerCoords);
+ AMOTION_EVENT_ACTION_MOVE, 0, flags, AMOTION_EVENT_EDGE_FLAG_TOP,
+ AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE,
+ mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, mRawTransform, ARBITRARY_DOWN_TIME,
+ ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords);
event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords);
}
@@ -352,14 +384,19 @@
ASSERT_EQ(DISPLAY_ID, event->getDisplayId());
EXPECT_EQ(HMAC, event->getHmac());
ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction());
- ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags());
+ ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION,
+ event->getFlags());
ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags());
ASSERT_EQ(AMETA_ALT_ON, event->getMetaState());
ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState());
ASSERT_EQ(MotionClassification::NONE, event->getClassification());
EXPECT_EQ(mTransform, event->getTransform());
- ASSERT_EQ(X_OFFSET, event->getXOffset());
- ASSERT_EQ(Y_OFFSET, event->getYOffset());
+ ASSERT_NEAR((-RAW_X_OFFSET / RAW_X_SCALE) * X_SCALE + X_OFFSET, event->getRawXOffset(),
+ EPSILON);
+ ASSERT_NEAR((-RAW_Y_OFFSET / RAW_Y_SCALE) * Y_SCALE + Y_OFFSET, event->getRawYOffset(),
+ EPSILON);
ASSERT_EQ(2.0f, event->getXPrecision());
ASSERT_EQ(2.1f, event->getYPrecision());
ASSERT_EQ(ARBITRARY_DOWN_TIME, event->getDownTime());
@@ -513,7 +550,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());
@@ -554,25 +591,168 @@
ASSERT_EQ(event.getX(0), copy.getX(0));
}
+TEST_F(MotionEventTest, SplitPointerDown) {
+ MotionEvent event = MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .build();
+
+ MotionEvent splitDown;
+ std::bitset<MAX_POINTER_ID + 1> splitDownIds{};
+ splitDownIds.set(6, true);
+ splitDown.splitFrom(event, splitDownIds, /*eventId=*/42);
+ ASSERT_EQ(splitDown.getAction(), AMOTION_EVENT_ACTION_DOWN);
+ ASSERT_EQ(splitDown.getPointerCount(), 1u);
+ ASSERT_EQ(splitDown.getPointerId(0), 6);
+ ASSERT_EQ(splitDown.getX(0), 6);
+ ASSERT_EQ(splitDown.getY(0), 6);
+
+ MotionEvent splitPointerDown;
+ std::bitset<MAX_POINTER_ID + 1> splitPointerDownIds{};
+ splitPointerDownIds.set(6, true);
+ splitPointerDownIds.set(8, true);
+ splitPointerDown.splitFrom(event, splitPointerDownIds, /*eventId=*/42);
+ ASSERT_EQ(splitPointerDown.getAction(), POINTER_0_DOWN);
+ ASSERT_EQ(splitPointerDown.getPointerCount(), 2u);
+ ASSERT_EQ(splitPointerDown.getPointerId(0), 6);
+ ASSERT_EQ(splitPointerDown.getX(0), 6);
+ ASSERT_EQ(splitPointerDown.getY(0), 6);
+ ASSERT_EQ(splitPointerDown.getPointerId(1), 8);
+ ASSERT_EQ(splitPointerDown.getX(1), 8);
+ ASSERT_EQ(splitPointerDown.getY(1), 8);
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(4, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 1u);
+ ASSERT_EQ(splitMove.getPointerId(0), 4);
+ ASSERT_EQ(splitMove.getX(0), 4);
+ ASSERT_EQ(splitMove.getY(0), 4);
+}
+
+TEST_F(MotionEventTest, SplitPointerUp) {
+ MotionEvent event = MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .build();
+
+ MotionEvent splitUp;
+ std::bitset<MAX_POINTER_ID + 1> splitUpIds{};
+ splitUpIds.set(4, true);
+ splitUp.splitFrom(event, splitUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_UP);
+ ASSERT_EQ(splitUp.getPointerCount(), 1u);
+ ASSERT_EQ(splitUp.getPointerId(0), 4);
+ ASSERT_EQ(splitUp.getX(0), 4);
+ ASSERT_EQ(splitUp.getY(0), 4);
+
+ MotionEvent splitPointerUp;
+ std::bitset<MAX_POINTER_ID + 1> splitPointerUpIds{};
+ splitPointerUpIds.set(4, true);
+ splitPointerUpIds.set(8, true);
+ splitPointerUp.splitFrom(event, splitPointerUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitPointerUp.getAction(), POINTER_0_UP);
+ ASSERT_EQ(splitPointerUp.getPointerCount(), 2u);
+ ASSERT_EQ(splitPointerUp.getPointerId(0), 4);
+ ASSERT_EQ(splitPointerUp.getX(0), 4);
+ ASSERT_EQ(splitPointerUp.getY(0), 4);
+ ASSERT_EQ(splitPointerUp.getPointerId(1), 8);
+ ASSERT_EQ(splitPointerUp.getX(1), 8);
+ ASSERT_EQ(splitPointerUp.getY(1), 8);
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(6, true);
+ splitMoveIds.set(8, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/43);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 2u);
+ ASSERT_EQ(splitMove.getPointerId(0), 6);
+ ASSERT_EQ(splitMove.getX(0), 6);
+ ASSERT_EQ(splitMove.getY(0), 6);
+ ASSERT_EQ(splitMove.getPointerId(1), 8);
+ ASSERT_EQ(splitMove.getX(1), 8);
+ ASSERT_EQ(splitMove.getY(1), 8);
+}
+
+TEST_F(MotionEventTest, SplitPointerUpCancel) {
+ MotionEvent event = MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .addFlag(AMOTION_EVENT_FLAG_CANCELED)
+ .build();
+
+ MotionEvent splitUp;
+ std::bitset<MAX_POINTER_ID + 1> splitUpIds{};
+ splitUpIds.set(6, true);
+ splitUp.splitFrom(event, splitUpIds, /*eventId=*/42);
+ ASSERT_EQ(splitUp.getAction(), AMOTION_EVENT_ACTION_CANCEL);
+ ASSERT_EQ(splitUp.getPointerCount(), 1u);
+ ASSERT_EQ(splitUp.getPointerId(0), 6);
+ ASSERT_EQ(splitUp.getX(0), 6);
+ ASSERT_EQ(splitUp.getY(0), 6);
+}
+
+TEST_F(MotionEventTest, SplitPointerMove) {
+ MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .pointer(PointerBuilder(/*id=*/6, ToolType::FINGER).x(6).y(6))
+ .pointer(PointerBuilder(/*id=*/8, ToolType::FINGER).x(8).y(8))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .build();
+
+ MotionEvent splitMove;
+ std::bitset<MAX_POINTER_ID + 1> splitMoveIds{};
+ splitMoveIds.set(4, true);
+ splitMoveIds.set(8, true);
+ splitMove.splitFrom(event, splitMoveIds, /*eventId=*/42);
+ ASSERT_EQ(splitMove.getAction(), AMOTION_EVENT_ACTION_MOVE);
+ ASSERT_EQ(splitMove.getPointerCount(), 2u);
+ ASSERT_EQ(splitMove.getPointerId(0), 4);
+ ASSERT_EQ(splitMove.getX(0), event.getX(0));
+ ASSERT_EQ(splitMove.getY(0), event.getY(0));
+ ASSERT_EQ(splitMove.getRawX(0), event.getRawX(0));
+ ASSERT_EQ(splitMove.getRawY(0), event.getRawY(0));
+ ASSERT_EQ(splitMove.getPointerId(1), 8);
+ ASSERT_EQ(splitMove.getX(1), event.getX(2));
+ ASSERT_EQ(splitMove.getY(1), event.getY(2));
+ ASSERT_EQ(splitMove.getRawX(1), event.getRawX(2));
+ ASSERT_EQ(splitMove.getRawY(1), event.getRawY(2));
+}
+
TEST_F(MotionEventTest, OffsetLocation) {
MotionEvent event;
initializeEventWithHistory(&event);
+ const float xOffset = event.getRawXOffset();
+ const float yOffset = event.getRawYOffset();
event.offsetLocation(5.0f, -2.0f);
- ASSERT_EQ(X_OFFSET + 5.0f, event.getXOffset());
- ASSERT_EQ(Y_OFFSET - 2.0f, event.getYOffset());
+ ASSERT_EQ(xOffset + 5.0f, event.getRawXOffset());
+ ASSERT_EQ(yOffset - 2.0f, event.getRawYOffset());
}
TEST_F(MotionEventTest, Scale) {
MotionEvent event;
initializeEventWithHistory(&event);
const float unscaledOrientation = event.getOrientation(0);
+ const float unscaledXOffset = event.getRawXOffset();
+ const float unscaledYOffset = event.getRawYOffset();
event.scale(2.0f);
- ASSERT_EQ(X_OFFSET * 2, event.getXOffset());
- ASSERT_EQ(Y_OFFSET * 2, event.getYOffset());
+ ASSERT_EQ(unscaledXOffset * 2, event.getRawXOffset());
+ ASSERT_EQ(unscaledYOffset * 2, event.getRawYOffset());
ASSERT_NEAR((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0), EPSILON);
ASSERT_NEAR((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0), EPSILON);
@@ -642,8 +822,10 @@
}
MotionEvent event;
ui::Transform identityTransform;
+ const int32_t flags = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
- INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0,
+ INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, flags,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0,
MotionClassification::NONE, identityTransform, /*xPrecision=*/0,
/*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2,
@@ -701,11 +883,10 @@
pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
- /* displayId */ 0, 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,
+ event.initialize(InputEvent::nextId(), /*deviceId=*/1, source, ui::LogicalDisplayId::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,
AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime,
pointerCoords.size(), pointerProperties.data(), pointerCoords.data());
return event;
@@ -931,4 +1112,90 @@
ASSERT_EQ(EXPECTED.y, event.getYCursorPosition());
}
+TEST_F(MotionEventTest, InvalidOrientationNotRotated) {
+ // This touch event does not have a value for AXIS_ORIENTATION, and the flags are implicitly
+ // set to 0. The transform is set to a 90-degree rotation.
+ MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .build();
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+ event.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f);
+}
+
+TEST_F(MotionEventTest, ValidZeroOrientationRotated) {
+ // This touch events will implicitly have a value of 0 for its AXIS_ORIENTATION.
+ auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+ MotionEvent nonDirectionalEvent = builder.build();
+ MotionEvent directionalEvent =
+ builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+ // The angle is rotated by the initial transform, a 90-degree rotation.
+ ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), M_PI_2, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+ ASSERT_NEAR(fabs(directionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON);
+
+ nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), -M_PI_2, EPSILON);
+}
+
+TEST_F(MotionEventTest, ValidNonZeroOrientationRotated) {
+ const float initial = 1.f;
+ auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .downTime(ARBITRARY_DOWN_TIME)
+ .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER)
+ .x(4)
+ .y(4)
+ .axis(AMOTION_EVENT_AXIS_ORIENTATION, initial))
+ .transform(ui::Transform(ui::Transform::ROT_90, 100, 100))
+ .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50))
+ .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
+
+ MotionEvent nonDirectionalEvent = builder.build();
+ MotionEvent directionalEvent =
+ builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build();
+
+ // The angle is rotated by the initial transform, a 90-degree rotation.
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial + M_PI_2, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI, EPSILON);
+
+ nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON);
+
+ nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100)));
+ ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+ ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON);
+}
+
} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
new file mode 100644
index 0000000..f49469c
--- /dev/null
+++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp
@@ -0,0 +1,817 @@
+/*
+ * 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 <android-base/logging.h>
+#include <attestation/HmacKeyManager.h>
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+#include <input/BlockingQueue.h>
+#include <input/InputConsumerNoResampling.h>
+#include <input/InputTransport.h>
+
+using android::base::Result;
+
+namespace android {
+
+namespace {
+
+static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION;
+static constexpr int32_t ACTION_MOVE = AMOTION_EVENT_ACTION_MOVE;
+static constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t POINTER_2_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+static auto constexpr TIMEOUT = 5s;
+
+struct Pointer {
+ int32_t id;
+ float x;
+ float y;
+ bool isResampled = false;
+};
+
+// A collection of arguments to be sent as publishMotionEvent(). The saved members of this struct
+// allow to check the expectations against the event acquired from the InputConsumerCallbacks. To
+// help simplify expectation checking it carries members not present in MotionEvent, like
+// |rawXScale|.
+struct PublishMotionArgs {
+ const int32_t action;
+ const nsecs_t downTime;
+ const uint32_t seq;
+ const int32_t eventId;
+ const int32_t deviceId = 1;
+ const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN;
+ const ui::LogicalDisplayId displayId = ui::LogicalDisplayId::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;
+ const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY;
+ const MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE;
+ const float xScale = 2;
+ const float yScale = 3;
+ const float xOffset = -10;
+ const float yOffset = -20;
+ const float rawXScale = 4;
+ const float rawYScale = -5;
+ const float rawXOffset = -11;
+ const float rawYOffset = 42;
+ const float xPrecision = 0.25;
+ const float yPrecision = 0.5;
+ const float xCursorPosition = 1.3;
+ const float yCursorPosition = 50.6;
+ std::array<uint8_t, 32> hmac;
+ int32_t flags;
+ ui::Transform transform;
+ ui::Transform rawTransform;
+ const nsecs_t eventTime;
+ size_t pointerCount;
+ std::vector<PointerProperties> pointerProperties;
+ std::vector<PointerCoords> pointerCoords;
+
+ PublishMotionArgs(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers,
+ const uint32_t seq);
+};
+
+PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime,
+ const std::vector<Pointer>& pointers, const uint32_t inSeq)
+ : action(inAction),
+ downTime(inDownTime),
+ seq(inSeq),
+ eventId(InputEvent::nextId()),
+ eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) {
+ hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
+
+ flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
+ if (action == AMOTION_EVENT_ACTION_CANCEL) {
+ flags |= AMOTION_EVENT_FLAG_CANCELED;
+ }
+ pointerCount = pointers.size();
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties.push_back({});
+ pointerProperties[i].clear();
+ pointerProperties[i].id = pointers[i].id;
+ pointerProperties[i].toolType = ToolType::FINGER;
+
+ pointerCoords.push_back({});
+ pointerCoords[i].clear();
+ pointerCoords[i].isResampled = pointers[i].isResampled;
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i);
+ }
+ transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1});
+ rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1});
+}
+
+// Checks expectations against |motionEvent| acquired from an InputConsumer. Floating point
+// comparisons limit precision to EPSILON.
+void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& motionEvent) {
+ EXPECT_EQ(args.eventId, motionEvent.getId());
+ EXPECT_EQ(args.deviceId, motionEvent.getDeviceId());
+ EXPECT_EQ(args.source, motionEvent.getSource());
+ EXPECT_EQ(args.displayId, motionEvent.getDisplayId());
+ EXPECT_EQ(args.hmac, motionEvent.getHmac());
+ EXPECT_EQ(args.action, motionEvent.getAction());
+ EXPECT_EQ(args.downTime, motionEvent.getDownTime());
+ EXPECT_EQ(args.flags, motionEvent.getFlags());
+ EXPECT_EQ(args.edgeFlags, motionEvent.getEdgeFlags());
+ EXPECT_EQ(args.metaState, motionEvent.getMetaState());
+ EXPECT_EQ(args.buttonState, motionEvent.getButtonState());
+ EXPECT_EQ(args.classification, motionEvent.getClassification());
+ EXPECT_EQ(args.transform, motionEvent.getTransform());
+ EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset,
+ motionEvent.getRawXOffset(), EPSILON);
+ EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset,
+ motionEvent.getRawYOffset(), EPSILON);
+ EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision());
+ EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision());
+ EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON);
+ EXPECT_NEAR(args.yCursorPosition, motionEvent.getRawYCursorPosition(), EPSILON);
+ EXPECT_NEAR(args.xCursorPosition * args.xScale + args.xOffset, motionEvent.getXCursorPosition(),
+ EPSILON);
+ EXPECT_NEAR(args.yCursorPosition * args.yScale + args.yOffset, motionEvent.getYCursorPosition(),
+ EPSILON);
+ EXPECT_EQ(args.rawTransform, motionEvent.getRawTransform());
+ EXPECT_EQ(args.eventTime, motionEvent.getEventTime());
+ EXPECT_EQ(args.pointerCount, motionEvent.getPointerCount());
+ EXPECT_EQ(0U, motionEvent.getHistorySize());
+
+ for (size_t i = 0; i < args.pointerCount; i++) {
+ SCOPED_TRACE(i);
+ EXPECT_EQ(args.pointerProperties[i].id, motionEvent.getPointerId(i));
+ EXPECT_EQ(args.pointerProperties[i].toolType, motionEvent.getToolType(i));
+
+ const auto& pc = args.pointerCoords[i];
+ EXPECT_EQ(pc, motionEvent.getSamplePointerCoords()[i]);
+
+ EXPECT_NEAR(pc.getX() * args.rawXScale + args.rawXOffset, motionEvent.getRawX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * args.rawYScale + args.rawYOffset, motionEvent.getRawY(i), EPSILON);
+ EXPECT_NEAR(pc.getX() * args.xScale + args.xOffset, motionEvent.getX(i), EPSILON);
+ EXPECT_NEAR(pc.getY() * args.yScale + args.yOffset, motionEvent.getY(i), EPSILON);
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent.getPressure(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent.getSize(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent.getTouchMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent.getTouchMinor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent.getToolMajor(i));
+ EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent.getToolMinor(i));
+
+ // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is
+ // "up", and the positive y direction is "down".
+ const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ const float x = sinf(unscaledOrientation) * args.xScale;
+ const float y = -cosf(unscaledOrientation) * args.yScale;
+ EXPECT_EQ(atan2f(x, -y), motionEvent.getOrientation(i));
+ }
+}
+
+void publishMotionEvent(InputPublisher& publisher, const PublishMotionArgs& a) {
+ status_t status =
+ publisher.publishMotionEvent(a.seq, a.eventId, a.deviceId, a.source, a.displayId,
+ a.hmac, a.action, a.actionButton, a.flags, a.edgeFlags,
+ a.metaState, a.buttonState, a.classification, a.transform,
+ a.xPrecision, a.yPrecision, a.xCursorPosition,
+ a.yCursorPosition, a.rawTransform, a.downTime, a.eventTime,
+ a.pointerCount, a.pointerProperties.data(),
+ a.pointerCoords.data());
+ ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK";
+}
+
+Result<InputPublisher::ConsumerResponse> receiveConsumerResponse(
+ InputPublisher& publisher, std::chrono::milliseconds timeout) {
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+
+ while (true) {
+ Result<InputPublisher::ConsumerResponse> result = publisher.receiveConsumerResponse();
+ if (result.ok()) {
+ return result;
+ }
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
+ if (waited > timeout) {
+ return result;
+ }
+ }
+}
+
+void verifyFinishedSignal(InputPublisher& publisher, uint32_t seq, nsecs_t publishTime) {
+ Result<InputPublisher::ConsumerResponse> result = receiveConsumerResponse(publisher, TIMEOUT);
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse returned " << result.error().message();
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result));
+ const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result);
+ ASSERT_EQ(seq, finish.seq)
+ << "receiveConsumerResponse should have returned the original sequence number";
+ ASSERT_TRUE(finish.handled)
+ << "receiveConsumerResponse should have set handled to consumer's reply";
+ ASSERT_GE(finish.consumeTime, publishTime)
+ << "finished signal's consume time should be greater than publish time";
+}
+
+} // namespace
+
+class InputConsumerMessageHandler : public MessageHandler {
+public:
+ InputConsumerMessageHandler(std::function<void(const Message&)> function)
+ : mFunction(function) {}
+
+private:
+ void handleMessage(const Message& message) override { mFunction(message); }
+
+ std::function<void(const Message&)> mFunction;
+};
+
+class InputPublisherAndConsumerNoResamplingTest : public testing::Test,
+ public InputConsumerCallbacks {
+protected:
+ std::unique_ptr<InputChannel> mClientChannel;
+ std::unique_ptr<InputPublisher> mPublisher;
+ std::unique_ptr<InputConsumerNoResampling> mConsumer;
+
+ std::thread mLooperThread;
+ sp<Looper> mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
+
+ // LOOPER CONTROL
+ // Set to false when you want the looper to exit
+ std::atomic<bool> mExitLooper = false;
+ std::mutex mLock;
+
+ // Used by test to notify looper that the value of "mLooperMayProceed" has changed
+ std::condition_variable mNotifyLooperMayProceed;
+ bool mLooperMayProceed GUARDED_BY(mLock){true};
+ // Used by looper to notify the test that it's about to block on "mLooperMayProceed" -> true
+ std::condition_variable mNotifyLooperWaiting;
+ bool mLooperIsBlocked GUARDED_BY(mLock){false};
+
+ std::condition_variable mNotifyConsumerDestroyed;
+ bool mConsumerDestroyed GUARDED_BY(mLock){false};
+
+ void runLooper() {
+ static constexpr int LOOP_INDEFINITELY = -1;
+ Looper::setForThread(mLooper);
+ // Loop forever -- this thread is dedicated to servicing the looper callbacks.
+ while (!mExitLooper) {
+ mLooper->pollOnce(/*timeoutMillis=*/LOOP_INDEFINITELY);
+ }
+ }
+
+ void SetUp() override {
+ std::unique_ptr<InputChannel> serverChannel;
+ status_t result =
+ InputChannel::openInputChannelPair("channel name", serverChannel, mClientChannel);
+ ASSERT_EQ(OK, result);
+
+ mPublisher = std::make_unique<InputPublisher>(std::move(serverChannel));
+ mMessageHandler = sp<InputConsumerMessageHandler>::make(
+ [this](const Message& message) { handleMessage(message); });
+ mLooperThread = std::thread([this] { runLooper(); });
+ sendMessage(LooperMessage::CREATE_CONSUMER);
+ }
+
+ void publishAndConsumeKeyEvent();
+ void publishAndConsumeMotionStream();
+ void publishAndConsumeMotionDown(nsecs_t downTime);
+ void publishAndConsumeBatchedMotionMove(nsecs_t downTime);
+ void publishAndConsumeFocusEvent();
+ void publishAndConsumeCaptureEvent();
+ void publishAndConsumeDragEvent();
+ void publishAndConsumeTouchModeEvent();
+ void publishAndConsumeMotionEvent(int32_t action, nsecs_t downTime,
+ const std::vector<Pointer>& pointers);
+ void TearDown() override {
+ // Destroy the consumer, flushing any of the pending ack's.
+ sendMessage(LooperMessage::DESTROY_CONSUMER);
+ {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ mNotifyConsumerDestroyed.wait(lock, [this] { return mConsumerDestroyed; });
+ }
+ // Stop the looper thread so that we can destroy the object.
+ mExitLooper = true;
+ mLooper->wake();
+ mLooperThread.join();
+ }
+
+protected:
+ // Interaction with the looper thread
+ enum class LooperMessage : int {
+ CALL_PROBABLY_HAS_INPUT,
+ CREATE_CONSUMER,
+ DESTROY_CONSUMER,
+ CALL_REPORT_TIMELINE,
+ BLOCK_LOOPER,
+ };
+ void sendMessage(LooperMessage message);
+ struct ReportTimelineArgs {
+ int32_t inputEventId;
+ nsecs_t gpuCompletedTime;
+ nsecs_t presentTime;
+ };
+ // The input to the function "InputConsumer::reportTimeline". Populated on the test thread and
+ // accessed on the looper thread.
+ BlockingQueue<ReportTimelineArgs> mReportTimelineArgs;
+ // The output of calling "InputConsumer::probablyHasInput()". Populated on the looper thread and
+ // accessed on the test thread.
+ BlockingQueue<bool> mProbablyHasInputResponses;
+
+private:
+ sp<MessageHandler> mMessageHandler;
+ void handleMessage(const Message& message);
+
+ static auto constexpr NO_EVENT_TIMEOUT = 10ms;
+ // The sequence number to use when publishing the next event
+ uint32_t mSeq = 1;
+
+ BlockingQueue<std::unique_ptr<KeyEvent>> mKeyEvents;
+ BlockingQueue<std::unique_ptr<MotionEvent>> mMotionEvents;
+ BlockingQueue<std::unique_ptr<FocusEvent>> mFocusEvents;
+ BlockingQueue<std::unique_ptr<CaptureEvent>> mCaptureEvents;
+ BlockingQueue<std::unique_ptr<DragEvent>> mDragEvents;
+ BlockingQueue<std::unique_ptr<TouchModeEvent>> mTouchModeEvents;
+
+ // InputConsumerCallbacks interface
+ void onKeyEvent(std::unique_ptr<KeyEvent> event, uint32_t seq) override {
+ mKeyEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onMotionEvent(std::unique_ptr<MotionEvent> event, uint32_t seq) override {
+ mMotionEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onBatchedInputEventPending(int32_t pendingBatchSource) override {
+ if (!mConsumer->probablyHasInput()) {
+ ADD_FAILURE() << "should deterministically have input because there is a batch";
+ }
+ mConsumer->consumeBatchedInputEvents(std::nullopt);
+ };
+ void onFocusEvent(std::unique_ptr<FocusEvent> event, uint32_t seq) override {
+ mFocusEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+ void onCaptureEvent(std::unique_ptr<CaptureEvent> event, uint32_t seq) override {
+ mCaptureEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+ void onDragEvent(std::unique_ptr<DragEvent> event, uint32_t seq) override {
+ mDragEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ }
+ void onTouchModeEvent(std::unique_ptr<TouchModeEvent> event, uint32_t seq) override {
+ mTouchModeEvents.push(std::move(event));
+ mConsumer->finishInputEvent(seq, true);
+ };
+};
+
+void InputPublisherAndConsumerNoResamplingTest::sendMessage(LooperMessage message) {
+ Message msg{ftl::to_underlying(message)};
+ mLooper->sendMessage(mMessageHandler, msg);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::handleMessage(const Message& message) {
+ switch (static_cast<LooperMessage>(message.what)) {
+ case LooperMessage::CALL_PROBABLY_HAS_INPUT: {
+ mProbablyHasInputResponses.push(mConsumer->probablyHasInput());
+ break;
+ }
+ case LooperMessage::CREATE_CONSUMER: {
+ mConsumer = std::make_unique<InputConsumerNoResampling>(std::move(mClientChannel),
+ mLooper, *this);
+ break;
+ }
+ case LooperMessage::DESTROY_CONSUMER: {
+ mConsumer = nullptr;
+ {
+ std::unique_lock lock(mLock);
+ mConsumerDestroyed = true;
+ }
+ mNotifyConsumerDestroyed.notify_all();
+ break;
+ }
+ case LooperMessage::CALL_REPORT_TIMELINE: {
+ std::optional<ReportTimelineArgs> args = mReportTimelineArgs.pop();
+ if (!args.has_value()) {
+ ADD_FAILURE() << "Couldn't get the 'reportTimeline' args in time";
+ return;
+ }
+ mConsumer->reportTimeline(args->inputEventId, args->gpuCompletedTime,
+ args->presentTime);
+ break;
+ }
+ case LooperMessage::BLOCK_LOOPER: {
+ {
+ std::unique_lock lock(mLock);
+ mLooperIsBlocked = true;
+ }
+ mNotifyLooperWaiting.notify_all();
+
+ {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ mNotifyLooperMayProceed.wait(lock, [this] { return mLooperMayProceed; });
+ }
+
+ {
+ std::unique_lock lock(mLock);
+ mLooperIsBlocked = false;
+ }
+ mNotifyLooperWaiting.notify_all();
+ break;
+ }
+ }
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeKeyEvent() {
+ status_t status;
+
+ const uint32_t seq = mSeq++;
+ int32_t eventId = InputEvent::nextId();
+ constexpr int32_t deviceId = 1;
+ constexpr uint32_t source = AINPUT_SOURCE_KEYBOARD;
+ constexpr ui::LogicalDisplayId displayId = ui::LogicalDisplayId::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};
+ constexpr int32_t action = AKEY_EVENT_ACTION_DOWN;
+ constexpr int32_t flags = AKEY_EVENT_FLAG_FROM_SYSTEM;
+ constexpr int32_t keyCode = AKEYCODE_ENTER;
+ constexpr int32_t scanCode = 13;
+ constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON;
+ constexpr int32_t repeatCount = 1;
+ constexpr nsecs_t downTime = 3;
+ constexpr nsecs_t eventTime = 4;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishKeyEvent(seq, eventId, deviceId, source, displayId, hmac, action,
+ flags, keyCode, scanCode, metaState, repeatCount, downTime,
+ eventTime);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ std::optional<std::unique_ptr<KeyEvent>> optKeyEvent = mKeyEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optKeyEvent.has_value()) << "consumer should have returned non-NULL event";
+ std::unique_ptr<KeyEvent> keyEvent = std::move(*optKeyEvent);
+
+ sendMessage(LooperMessage::CALL_PROBABLY_HAS_INPUT);
+ std::optional<bool> probablyHasInput = mProbablyHasInputResponses.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(probablyHasInput.has_value());
+ ASSERT_FALSE(probablyHasInput.value()) << "no events should be waiting after being consumed";
+
+ EXPECT_EQ(eventId, keyEvent->getId());
+ EXPECT_EQ(deviceId, keyEvent->getDeviceId());
+ EXPECT_EQ(source, keyEvent->getSource());
+ EXPECT_EQ(displayId, keyEvent->getDisplayId());
+ EXPECT_EQ(hmac, keyEvent->getHmac());
+ EXPECT_EQ(action, keyEvent->getAction());
+ EXPECT_EQ(flags, keyEvent->getFlags());
+ EXPECT_EQ(keyCode, keyEvent->getKeyCode());
+ EXPECT_EQ(scanCode, keyEvent->getScanCode());
+ EXPECT_EQ(metaState, keyEvent->getMetaState());
+ EXPECT_EQ(repeatCount, keyEvent->getRepeatCount());
+ EXPECT_EQ(downTime, keyEvent->getDownTime());
+ EXPECT_EQ(eventTime, keyEvent->getEventTime());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionStream() {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 300, .y = 400}});
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionDown(nsecs_t downTime) {
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeBatchedMotionMove(
+ nsecs_t downTime) {
+ uint32_t seq = mSeq++;
+ const std::vector<Pointer> pointers = {Pointer{.id = 0, .x = 20, .y = 30}};
+ PublishMotionArgs args(AMOTION_EVENT_ACTION_MOVE, downTime, pointers, seq);
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ // Block the looper thread, preventing it from being able to service any of the fd callbacks.
+
+ {
+ std::scoped_lock lock(mLock);
+ mLooperMayProceed = false;
+ }
+ sendMessage(LooperMessage::BLOCK_LOOPER);
+ {
+ std::unique_lock lock(mLock);
+ mNotifyLooperWaiting.wait(lock, [this] { return mLooperIsBlocked; });
+ }
+
+ publishMotionEvent(*mPublisher, args);
+
+ // Ensure no event arrives because the UI thread is blocked
+ std::optional<std::unique_ptr<MotionEvent>> noEvent =
+ mMotionEvents.popWithTimeout(NO_EVENT_TIMEOUT);
+ ASSERT_FALSE(noEvent.has_value()) << "Got unexpected event: " << *noEvent;
+
+ Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse();
+ ASSERT_FALSE(result.ok());
+ ASSERT_EQ(WOULD_BLOCK, result.error().code());
+
+ // We shouldn't be calling mConsumer on the UI thread, but in this situation, the looper
+ // thread is locked, so this should be safe to do.
+ ASSERT_TRUE(mConsumer->probablyHasInput())
+ << "should deterministically have input because there is a batch";
+
+ // Now, unblock the looper thread, so that the event can arrive.
+ {
+ std::scoped_lock lock(mLock);
+ mLooperMayProceed = true;
+ }
+ mNotifyLooperMayProceed.notify_all();
+
+ std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optMotion.has_value());
+ std::unique_ptr<MotionEvent> motion = std::move(*optMotion);
+ ASSERT_EQ(ACTION_MOVE, motion->getAction());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeMotionEvent(
+ int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) {
+ uint32_t seq = mSeq++;
+ PublishMotionArgs args(action, downTime, pointers, seq);
+ nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ publishMotionEvent(*mPublisher, args);
+
+ std::optional<std::unique_ptr<MotionEvent>> optMotion = mMotionEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optMotion.has_value());
+ std::unique_ptr<MotionEvent> event = std::move(*optMotion);
+
+ verifyArgsEqualToEvent(args, *event);
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeFocusEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool hasFocus = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
+ ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
+
+ std::optional<std::unique_ptr<FocusEvent>> optFocusEvent = mFocusEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optFocusEvent.has_value()) << "consumer should have returned non-NULL event";
+ std::unique_ptr<FocusEvent> focusEvent = std::move(*optFocusEvent);
+ EXPECT_EQ(eventId, focusEvent->getId());
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeCaptureEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 42;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool captureEnabled = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishCaptureEvent(seq, eventId, captureEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishCaptureEvent should return OK";
+
+ std::optional<std::unique_ptr<CaptureEvent>> optEvent = mCaptureEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event";
+ std::unique_ptr<CaptureEvent> event = std::move(*optEvent);
+
+ const CaptureEvent& captureEvent = *event;
+ EXPECT_EQ(eventId, captureEvent.getId());
+ EXPECT_EQ(captureEnabled, captureEvent.getPointerCaptureEnabled());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeDragEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool isExiting = false;
+ constexpr float x = 10;
+ constexpr float y = 15;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishDragEvent(seq, eventId, x, y, isExiting);
+ ASSERT_EQ(OK, status) << "publisher publishDragEvent should return OK";
+
+ std::optional<std::unique_ptr<DragEvent>> optEvent = mDragEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optEvent.has_value()) << "consumer should have returned non-NULL event";
+ std::unique_ptr<DragEvent> event = std::move(*optEvent);
+
+ const DragEvent& dragEvent = *event;
+ EXPECT_EQ(eventId, dragEvent.getId());
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+void InputPublisherAndConsumerNoResamplingTest::publishAndConsumeTouchModeEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ int32_t eventId = InputEvent::nextId();
+ constexpr bool touchModeEnabled = true;
+ const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ status = mPublisher->publishTouchModeEvent(seq, eventId, touchModeEnabled);
+ ASSERT_EQ(OK, status) << "publisher publishTouchModeEvent should return OK";
+
+ std::optional<std::unique_ptr<TouchModeEvent>> optEvent =
+ mTouchModeEvents.popWithTimeout(TIMEOUT);
+ ASSERT_TRUE(optEvent.has_value());
+ std::unique_ptr<TouchModeEvent> event = std::move(*optEvent);
+
+ const TouchModeEvent& touchModeEvent = *event;
+ EXPECT_EQ(eventId, touchModeEvent.getId());
+ EXPECT_EQ(touchModeEnabled, touchModeEvent.isInTouchMode());
+
+ verifyFinishedSignal(*mPublisher, seq, publishTime);
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, SendTimeline) {
+ const int32_t inputEventId = 20;
+ const nsecs_t gpuCompletedTime = 30;
+ const nsecs_t presentTime = 40;
+
+ mReportTimelineArgs.emplace(inputEventId, gpuCompletedTime, presentTime);
+ sendMessage(LooperMessage::CALL_REPORT_TIMELINE);
+
+ Result<InputPublisher::ConsumerResponse> result = receiveConsumerResponse(*mPublisher, TIMEOUT);
+ ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK";
+ ASSERT_TRUE(std::holds_alternative<InputPublisher::Timeline>(*result));
+ const InputPublisher::Timeline& timeline = std::get<InputPublisher::Timeline>(*result);
+ ASSERT_EQ(inputEventId, timeline.inputEventId);
+ ASSERT_EQ(gpuCompletedTime, timeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]);
+ ASSERT_EQ(presentTime, timeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]);
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishKeyEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMotionEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMotionMoveEvent_EndToEnd) {
+ // Publish a DOWN event before MOVE to pass the InputVerifier checks.
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionDown(downTime));
+
+ // Publish the MOVE event and check expectations.
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeBatchedMotionMove(downTime));
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishFocusEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishCaptureEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishDragEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishTouchModeEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest,
+ PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
+ status_t status;
+ const size_t pointerCount = 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerCoords[i].clear();
+ }
+
+ ui::Transform identityTransform;
+ status =
+ mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0,
+ ui::LogicalDisplayId::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";
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest,
+ PublishMotionEvent_WhenPointerCountLessThan1_ReturnsError) {
+ status_t status;
+ const size_t pointerCount = 0;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+
+ ui::Transform identityTransform;
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0,
+ ui::LogicalDisplayId::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";
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest,
+ PublishMotionEvent_WhenPointerCountGreaterThanMax_ReturnsError) {
+ status_t status;
+ const size_t pointerCount = MAX_POINTERS + 1;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerCoords[i].clear();
+ }
+
+ ui::Transform identityTransform;
+ status =
+ mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0,
+ ui::LogicalDisplayId::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";
+}
+
+TEST_F(InputPublisherAndConsumerNoResamplingTest, PublishMultipleEvents_EndToEnd) {
+ const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC);
+
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ publishAndConsumeMotionEvent(POINTER_1_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent());
+ publishAndConsumeMotionEvent(POINTER_2_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeCaptureEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeDragEvent());
+ // Provide a consistent input stream - cancel the gesture that was started above
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_CANCEL, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30},
+ Pointer{.id = 1, .x = 200, .y = 300},
+ Pointer{.id = 2, .x = 200, .y = 300}});
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeKeyEvent());
+ ASSERT_NO_FATAL_FAILURE(publishAndConsumeTouchModeEvent());
+}
+
+} // namespace android
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 3543020..e65a919 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -14,11 +14,9 @@
* limitations under the License.
*/
-#include "TestHelpers.h"
-
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
-#include <gui/constants.h>
+#include <input/InputConsumer.h>
#include <input/InputTransport.h>
using android::base::Result;
@@ -50,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::LogicalDisplayId::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;
@@ -91,7 +89,9 @@
hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31};
- flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+ flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
if (action == AMOTION_EVENT_ACTION_CANCEL) {
flags |= AMOTION_EVENT_FLAG_CANCELED;
}
@@ -135,8 +135,10 @@
EXPECT_EQ(args.buttonState, motionEvent.getButtonState());
EXPECT_EQ(args.classification, motionEvent.getClassification());
EXPECT_EQ(args.transform, motionEvent.getTransform());
- EXPECT_EQ(args.xOffset, motionEvent.getXOffset());
- EXPECT_EQ(args.yOffset, motionEvent.getYOffset());
+ EXPECT_NEAR((-args.rawXOffset / args.rawXScale) * args.xScale + args.xOffset,
+ motionEvent.getRawXOffset(), EPSILON);
+ EXPECT_NEAR((-args.rawYOffset / args.rawYScale) * args.yScale + args.yOffset,
+ motionEvent.getRawYOffset(), EPSILON);
EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision());
EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision());
EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON);
@@ -262,7 +264,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::LogicalDisplayId::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};
@@ -622,13 +624,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::LogicalDisplayId::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) {
@@ -639,17 +641,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::LogicalDisplayId::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];
@@ -661,13 +663,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::LogicalDisplayId::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/MotionPredictorMetricsManager_test.cpp b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
index 31cc145..cc41eeb 100644
--- a/libs/input/tests/MotionPredictorMetricsManager_test.cpp
+++ b/libs/input/tests/MotionPredictorMetricsManager_test.cpp
@@ -238,14 +238,17 @@
// --- Ground-truth-generation helper functions. ---
+// Generates numPoints ground truth points with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given inputInterval.
std::vector<GroundTruthPoint> generateConstantGroundTruthPoints(
- const GroundTruthPoint& groundTruthPoint, size_t numPoints) {
+ const GroundTruthPoint& groundTruthPoint, size_t numPoints,
+ nsecs_t inputInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<GroundTruthPoint> groundTruthPoints;
nsecs_t timestamp = groundTruthPoint.timestamp;
for (size_t i = 0; i < numPoints; ++i) {
groundTruthPoints.emplace_back(groundTruthPoint);
groundTruthPoints.back().timestamp = timestamp;
- timestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ timestamp += inputInterval;
}
return groundTruthPoints;
}
@@ -280,7 +283,8 @@
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
const std::vector<GroundTruthPoint> groundTruthPoints =
- generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3);
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/3,
+ /*inputInterval=*/10);
ASSERT_EQ(3u, groundTruthPoints.size());
// First point.
@@ -290,11 +294,11 @@
// Second point.
EXPECT_EQ(groundTruthPoints[1].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[1].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[1].timestamp, groundTruthPoints[0].timestamp);
+ EXPECT_EQ(groundTruthPoints[1].timestamp, groundTruthPoint.timestamp + 10);
// Third point.
EXPECT_EQ(groundTruthPoints[2].position, groundTruthPoint.position);
EXPECT_EQ(groundTruthPoints[2].pressure, groundTruthPoint.pressure);
- EXPECT_GT(groundTruthPoints[2].timestamp, groundTruthPoints[1].timestamp);
+ EXPECT_EQ(groundTruthPoints[2].timestamp, groundTruthPoint.timestamp + 20);
}
TEST(GenerateCircularArcGroundTruthTest, StraightLineUpwards) {
@@ -333,16 +337,19 @@
// --- Prediction-generation helper functions. ---
-// Creates a sequence of predictions with values equal to those of the given GroundTruthPoint.
-std::vector<PredictionPoint> generateConstantPredictions(const GroundTruthPoint& groundTruthPoint) {
+// Generates TEST_MAX_NUM_PREDICTIONS predictions with values equal to those of the given
+// GroundTruthPoint, and with consecutive timestamps separated by the given predictionInterval.
+std::vector<PredictionPoint> generateConstantPredictions(
+ const GroundTruthPoint& groundTruthPoint,
+ nsecs_t predictionInterval = TEST_PREDICTION_INTERVAL_NANOS) {
std::vector<PredictionPoint> predictions;
- nsecs_t predictionTimestamp = groundTruthPoint.timestamp + TEST_PREDICTION_INTERVAL_NANOS;
+ nsecs_t predictionTimestamp = groundTruthPoint.timestamp + predictionInterval;
for (size_t j = 0; j < TEST_MAX_NUM_PREDICTIONS; ++j) {
predictions.push_back(PredictionPoint{{.position = groundTruthPoint.position,
.pressure = groundTruthPoint.pressure},
.originTimestamp = groundTruthPoint.timestamp,
.targetTimestamp = predictionTimestamp});
- predictionTimestamp += TEST_PREDICTION_INTERVAL_NANOS;
+ predictionTimestamp += predictionInterval;
}
return predictions;
}
@@ -375,8 +382,9 @@
TEST(GeneratePredictionsTest, GenerateConstantPredictions) {
const GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10, 20), .pressure = 0.3f},
.timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t predictionInterval = 10;
const std::vector<PredictionPoint> predictionPoints =
- generateConstantPredictions(groundTruthPoint);
+ generateConstantPredictions(groundTruthPoint, predictionInterval);
ASSERT_EQ(TEST_MAX_NUM_PREDICTIONS, predictionPoints.size());
for (size_t i = 0; i < predictionPoints.size(); ++i) {
@@ -385,8 +393,7 @@
EXPECT_THAT(predictionPoints[i].pressure, FloatNear(groundTruthPoint.pressure, 1e-6));
EXPECT_EQ(predictionPoints[i].originTimestamp, groundTruthPoint.timestamp);
EXPECT_EQ(predictionPoints[i].targetTimestamp,
- groundTruthPoint.timestamp +
- static_cast<nsecs_t>(i + 1) * TEST_PREDICTION_INTERVAL_NANOS);
+ TEST_INITIAL_TIMESTAMP + static_cast<nsecs_t>(i + 1) * predictionInterval);
}
}
@@ -678,12 +685,9 @@
// • groundTruthPoints: chronologically-ordered ground truth points, with at least 2 elements.
// • predictionPoints: the first index points to a vector of predictions corresponding to the
// source ground truth point with the same index.
-// - The first element should be empty, because there are not expected to be predictions until
-// we have received 2 ground truth points.
-// - The last element may be empty, because there will be no future ground truth points to
-// associate with those predictions (if not empty, it will be ignored).
+// - For empty prediction vectors, MetricsManager::onPredict will not be called.
// - To test all prediction buckets, there should be at least TEST_MAX_NUM_PREDICTIONS non-empty
-// prediction sets (that is, excluding the first and last). Thus, groundTruthPoints and
+// prediction vectors (that is, excluding the first and last). Thus, groundTruthPoints and
// predictionPoints should have size at least TEST_MAX_NUM_PREDICTIONS + 2.
//
// When the function returns, outReportedAtomFields will contain the reported AtomFields.
@@ -697,19 +701,12 @@
createMockReportAtomFunction(
outReportedAtomFields));
- // Validate structure of groundTruthPoints and predictionPoints.
- ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
ASSERT_GE(groundTruthPoints.size(), 2u);
- ASSERT_EQ(predictionPoints[0].size(), 0u);
- for (size_t i = 1; i + 1 < predictionPoints.size(); ++i) {
- SCOPED_TRACE(testing::Message() << "i = " << i);
- ASSERT_EQ(predictionPoints[i].size(), TEST_MAX_NUM_PREDICTIONS);
- }
+ ASSERT_EQ(predictionPoints.size(), groundTruthPoints.size());
- // Pass ground truth points and predictions (for all except first and last ground truth).
for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
metricsManager.onRecord(makeMotionEvent(groundTruthPoints[i]));
- if ((i > 0) && (i + 1 < predictionPoints.size())) {
+ if (!predictionPoints[i].empty()) {
metricsManager.onPredict(makeMotionEvent(predictionPoints[i]));
}
}
@@ -738,7 +735,7 @@
// Perfect predictions test:
// • Input: constant input events, perfect predictions matching the input events.
// • Expectation: all error metrics should be zero, or NO_DATA_SENTINEL for "unreported" metrics.
-// (For example, scale-invariant errors are only reported for the final time bucket.)
+// (For example, scale-invariant errors are only reported for the last time bucket.)
TEST(MotionPredictorMetricsManagerTest, ConstantGroundTruthPerfectPredictions) {
GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(10.0f, 20.0f), .pressure = 0.6f},
.timestamp = TEST_INITIAL_TIMESTAMP};
@@ -977,5 +974,35 @@
}
}
+// Robustness test:
+// • Input: input events separated by a significantly greater time interval than the interval
+// between predictions.
+// • Expectation: the MetricsManager should not crash in this case. (No assertions are made about
+// the resulting metrics.)
+//
+// In practice, this scenario could arise either if the input and prediction intervals are
+// mismatched, or if input events are missing (dropped or skipped for some reason).
+TEST(MotionPredictorMetricsManagerTest, MismatchedInputAndPredictionInterval) {
+ // Create two ground truth points separated by MAX_NUM_PREDICTIONS * PREDICTION_INTERVAL,
+ // so that the second ground truth point corresponds to the last prediction bucket. This
+ // ensures that the scale-invariant error codepath will be run, giving full code coverage.
+ GroundTruthPoint groundTruthPoint{{.position = Eigen::Vector2f(0.0f, 0.0f), .pressure = 0.5f},
+ .timestamp = TEST_INITIAL_TIMESTAMP};
+ const nsecs_t inputInterval = TEST_MAX_NUM_PREDICTIONS * TEST_PREDICTION_INTERVAL_NANOS;
+ const std::vector<GroundTruthPoint> groundTruthPoints =
+ generateConstantGroundTruthPoints(groundTruthPoint, /*numPoints=*/2, inputInterval);
+
+ // Create predictions separated by the prediction interval.
+ std::vector<std::vector<PredictionPoint>> predictionPoints;
+ for (size_t i = 0; i < groundTruthPoints.size(); ++i) {
+ predictionPoints.push_back(
+ generateConstantPredictions(groundTruthPoints[i], TEST_PREDICTION_INTERVAL_NANOS));
+ }
+
+ // Test that we can run the MetricsManager without crashing.
+ std::vector<AtomFields> reportedAtomFields;
+ runMetricsManager(groundTruthPoints, predictionPoints, reportedAtomFields);
+}
+
} // namespace
} // namespace android
diff --git a/libs/input/tests/MotionPredictor_test.cpp b/libs/input/tests/MotionPredictor_test.cpp
index 3343114..d077760 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,9 +58,10 @@
}
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,
- AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, identityTransform,
+ event.initialize(InputEvent::nextId(), deviceId, AINPUT_SOURCE_STYLUS,
+ ui::LogicalDisplayId::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,
identityTransform, /*downTime=*/100, eventTime.count(), pointerCount,
@@ -65,6 +69,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 +200,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 +249,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/TestHelpers.h b/libs/input/tests/TestHelpers.h
deleted file mode 100644
index 343d81f..0000000
--- a/libs/input/tests/TestHelpers.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TESTHELPERS_H
-#define TESTHELPERS_H
-
-#include <unistd.h>
-
-#include <utils/threads.h>
-
-namespace android {
-
-class Pipe {
-public:
- int sendFd;
- int receiveFd;
-
- Pipe() {
- int fds[2];
- ::pipe(fds);
-
- receiveFd = fds[0];
- sendFd = fds[1];
- }
-
- ~Pipe() {
- if (sendFd != -1) {
- ::close(sendFd);
- }
-
- if (receiveFd != -1) {
- ::close(receiveFd);
- }
- }
-
- status_t writeSignal() {
- ssize_t nWritten = ::write(sendFd, "*", 1);
- return nWritten == 1 ? 0 : -errno;
- }
-
- status_t readSignal() {
- char buf[1];
- ssize_t nRead = ::read(receiveFd, buf, 1);
- return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;
- }
-};
-
-class DelayedTask : public Thread {
- int mDelayMillis;
-
-public:
- explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }
-
-protected:
- virtual ~DelayedTask() { }
-
- virtual void doTask() = 0;
-
- virtual bool threadLoop() {
- usleep(mDelayMillis * 1000);
- doTask();
- return false;
- }
-};
-
-} // namespace android
-
-#endif // TESTHELPERS_H
diff --git a/libs/input/tests/TouchResampling_test.cpp b/libs/input/tests/TouchResampling_test.cpp
index 1cb7f7b..8d8b530 100644
--- a/libs/input/tests/TouchResampling_test.cpp
+++ b/libs/input/tests/TouchResampling_test.cpp
@@ -14,13 +14,12 @@
* limitations under the License.
*/
-#include "TestHelpers.h"
-
#include <chrono>
#include <vector>
#include <attestation/HmacKeyManager.h>
#include <gtest/gtest.h>
+#include <input/InputConsumer.h>
#include <input/InputTransport.h>
using namespace std::chrono_literals;
@@ -85,10 +84,11 @@
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,
- action, /*actionButton=*/0, /*flags=*/0, /*edgeFlags=*/0,
- AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE,
- identityTransform, /*xPrecision=*/0, /*yPrecision=*/0,
+ AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
+ INVALID_HMAC, action, /*actionButton=*/0, /*flags=*/0,
+ /*edgeFlags=*/0, AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE, identityTransform,
+ /*xPrecision=*/0, /*yPrecision=*/0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform,
downTime, eventTime, properties.size(), properties.data(),
@@ -297,10 +297,9 @@
}
/**
- * Stylus pointer coordinates are not resampled, but an event is still generated for the batch with
- * a resampled timestamp and should be marked as such.
+ * Stylus pointer coordinates are resampled.
*/
-TEST_F(TouchResamplingTest, StylusCoordinatesNotResampledFor) {
+TEST_F(TouchResamplingTest, StylusEventIsResampled) {
std::chrono::nanoseconds frameTime;
std::vector<InputEventEntry> entries, expectedEntries;
@@ -330,15 +329,91 @@
// id x y
{10ms, {{0, 20, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
{20ms, {{0, 30, 30, .toolType = ToolType::STYLUS}}, AMOTION_EVENT_ACTION_MOVE},
- // A resampled event is generated, but the stylus coordinates are not resampled.
{25ms,
- {{0, 30, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
+ {{0, 35, 30, .toolType = ToolType::STYLUS, .isResampled = true}},
AMOTION_EVENT_ACTION_MOVE},
};
consumeInputEventEntries(expectedEntries, frameTime);
}
/**
+ * Mouse pointer coordinates are resampled.
+ */
+TEST_F(TouchResamplingTest, MouseEventIsResampled) {
+ std::chrono::nanoseconds frameTime;
+ std::vector<InputEventEntry> entries, expectedEntries;
+
+ // Initial ACTION_DOWN should be separate, because the first consume event will only return
+ // InputEvent with a single action.
+ entries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 5ms;
+ expectedEntries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ entries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 35ms;
+ expectedEntries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::MOUSE}}, AMOTION_EVENT_ACTION_MOVE},
+ {25ms,
+ {{0, 35, 30, .toolType = ToolType::MOUSE, .isResampled = true}},
+ AMOTION_EVENT_ACTION_MOVE},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
+ * Motion events with palm tool type are not resampled.
+ */
+TEST_F(TouchResamplingTest, PalmEventIsNotResampled) {
+ std::chrono::nanoseconds frameTime;
+ std::vector<InputEventEntry> entries, expectedEntries;
+
+ // Initial ACTION_DOWN should be separate, because the first consume event will only return
+ // InputEvent with a single action.
+ entries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 5ms;
+ expectedEntries = {
+ // id x y
+ {0ms, {{0, 10, 20, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_DOWN},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+
+ // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+ entries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+ };
+ publishInputEventEntries(entries);
+ frameTime = 35ms;
+ expectedEntries = {
+ // id x y
+ {10ms, {{0, 20, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+ {20ms, {{0, 30, 30, .toolType = ToolType::PALM}}, AMOTION_EVENT_ACTION_MOVE},
+ };
+ consumeInputEventEntries(expectedEntries, frameTime);
+}
+
+/**
* Event should not be resampled when sample time is equal to event time.
*/
TEST_F(TouchResamplingTest, SampleTimeEqualsEventTime) {
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index f9ca280..f50a3cd 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::LogicalDisplayId::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::LogicalDisplayId::INVALID, 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..df5fe9d 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::LogicalDisplayId::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::LogicalDisplayId::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/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 8f005a5..bed31e2 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -148,29 +148,31 @@
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0);
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
long delayMillis) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis));
+ ->postFrameCallbackDelayed(callback, nullptr, nullptr, data, ms2ns(delayMillis),
+ CALLBACK_ANIMATION);
}
void AChoreographer_postVsyncCallback(AChoreographer* choreographer,
AChoreographer_vsyncCallback callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0);
+ ->postFrameCallbackDelayed(nullptr, nullptr, callback, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallback64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0);
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, 0, CALLBACK_ANIMATION);
}
void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
AChoreographer_frameCallback64 callback, void* data,
uint32_t delayMillis) {
AChoreographer_to_Choreographer(choreographer)
- ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis));
+ ->postFrameCallbackDelayed(nullptr, callback, nullptr, data, ms2ns(delayMillis),
+ CALLBACK_ANIMATION);
}
void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer,
AChoreographer_refreshRateCallback callback,
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 5261287..dd78049 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -300,7 +300,9 @@
if (result == 0) {
outPlanes->planeCount = 3;
outPlanes->planes[0].data = yuvData.y;
- if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010) {
+ // P010 is word-aligned 10-bit semiplaner, and YCbCr_422_I is a single interleaved plane
+ if (format == AHARDWAREBUFFER_FORMAT_YCbCr_P010 ||
+ format == AHARDWAREBUFFER_FORMAT_YCbCr_422_I) {
outPlanes->planes[0].pixelStride = 2;
} else {
outPlanes->planes[0].pixelStride = 1;
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/renderengine/Android.bp b/libs/renderengine/Android.bp
index b501d40..4a04467 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -50,6 +50,7 @@
"libshaders",
"libtonemap",
"libsurfaceflinger_common",
+ "libsurfaceflingerflags",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -83,10 +84,17 @@
"skia/AutoBackendTexture.cpp",
"skia/Cache.cpp",
"skia/ColorSpaces.cpp",
+ "skia/GaneshVkRenderEngine.cpp",
+ "skia/GraphiteVkRenderEngine.cpp",
"skia/GLExtensions.cpp",
"skia/SkiaRenderEngine.cpp",
"skia/SkiaGLRenderEngine.cpp",
"skia/SkiaVkRenderEngine.cpp",
+ "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",
@@ -95,6 +103,7 @@
"skia/filters/GaussianBlurFilter.cpp",
"skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
+ "skia/filters/MouriMap.cpp",
"skia/filters/StretchShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 233134d..bc3976d 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -16,40 +16,70 @@
#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 <com_android_graphics_surfaceflinger_flags.h>
+#include <cutils/properties.h>
+#include <log/log.h>
+
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \
+ COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE)
+#define COMPILE_GRAPHITE_RENDERENGINE 1
+#else
+#define COMPILE_GRAPHITE_RENDERENGINE 0
+#endif
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;
+
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
+ const RenderEngine::SkiaBackend actualSkiaBackend = args.skiaBackend;
+#else
+ if (args.skiaBackend == RenderEngine::SkiaBackend::GRAPHITE) {
+ ALOGE("RenderEngine with Graphite Skia backend was requested, but Graphite was not "
+ "included in the build. Falling back to Ganesh (%s)",
+ args.graphicsApi == RenderEngine::GraphicsApi::GL ? "GL" : "Vulkan");
+ }
+ const RenderEngine::SkiaBackend actualSkiaBackend = RenderEngine::SkiaBackend::GANESH;
+#endif
+
+ ALOGD("%sRenderEngine with %s Backend (%s)", args.threaded == Threaded::YES ? "Threaded " : "",
+ args.graphicsApi == GraphicsApi::GL ? "SkiaGL" : "SkiaVK",
+ actualSkiaBackend == SkiaBackend::GANESH ? "Ganesh" : "Graphite");
+
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
+ if (actualSkiaBackend == SkiaBackend::GRAPHITE) {
+ createInstanceFactory = [args]() {
+ return android::renderengine::skia::GraphiteVkRenderEngine::create(args);
+ };
+ } else
+#endif
+ { // 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..b640983 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>
@@ -88,7 +87,21 @@
aidl::android::hardware::graphics::composer3::RenderIntent renderIntent =
aidl::android::hardware::graphics::composer3::RenderIntent::TONE_MAP_COLORIMETRIC;
- std::vector<renderengine::BorderRenderInfo> borderInfoList;
+ // Tonemapping strategy to use for each layer. This is only used for tonemapping HDR source
+ // content
+ enum class TonemapStrategy {
+ // Use a tonemapper defined by libtonemap. This may be OEM-defined as of Android 13, aka
+ // undefined.
+ // This is typically a global tonemapper, designed to match what is on screen.
+ Libtonemap,
+ // Use a local tonemapper. Because local tonemapping uses large intermediate allocations,
+ // this
+ // method is primarily recommended for infrequent rendering that does not need to exactly
+ // match
+ // pixels that are on-screen.
+ Local,
+ };
+ TonemapStrategy tonemapStrategy = TonemapStrategy::Libtonemap;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
@@ -100,8 +113,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..7207394 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"
@@ -83,6 +88,21 @@
PROTECTED = 2,
};
+// Toggles for skipping or enabling priming of particular shaders.
+struct PrimeCacheConfig {
+ bool cacheHolePunchLayer = true;
+ bool cacheSolidLayers = true;
+ bool cacheSolidDimmedLayers = true;
+ bool cacheImageLayers = true;
+ bool cacheImageDimmedLayers = true;
+ bool cacheClippedLayers = true;
+ bool cacheShadowLayers = true;
+ bool cachePIPImageLayers = true;
+ bool cacheTransparentImageDimmedLayers = true;
+ bool cacheClippedDimmedImageLayers = true;
+ bool cacheUltraHDR = true;
+};
+
class RenderEngine {
public:
enum class ContextPriority {
@@ -102,9 +122,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;
@@ -112,7 +160,7 @@
// This interface, while still in use until a suitable replacement is built,
// should be considered deprecated, minus some methods which still may be
// used to support legacy behavior.
- virtual std::future<void> primeCache(bool shouldPrimeUltraHDR) = 0;
+ virtual std::future<void> primeCache(PrimeCacheConfig config) = 0;
// dump the extension strings. always call the base class.
virtual void dump(std::string& result) = 0;
@@ -253,10 +301,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 +313,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 +349,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 +365,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 +381,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/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h
index a58a65c..a8c242a 100644
--- a/libs/renderengine/include/renderengine/mock/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h
@@ -33,7 +33,7 @@
RenderEngine();
~RenderEngine() override;
- MOCK_METHOD1(primeCache, std::future<void>(bool));
+ MOCK_METHOD1(primeCache, std::future<void>(PrimeCacheConfig));
MOCK_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getMaxTextureSize, size_t());
MOCK_CONST_METHOD0(getMaxViewportDims, size_t());
diff --git a/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index ee95e59..8aeef9f 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -20,81 +20,21 @@
#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <SkImage.h>
-#include <include/gpu/ganesh/SkImageGanesh.h>
-#include <include/gpu/ganesh/SkSurfaceGanesh.h>
-#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
-#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
-#include <include/gpu/vk/GrVkTypes.h>
-#include <android/hardware_buffer.h>
-#include "ColorSpaces.h"
-#include "log/log_main.h"
-#include "utils/Trace.h"
+#include <include/core/SkImage.h>
+#include <include/core/SkSurface.h>
+
+#include "compat/SkiaBackendTexture.h"
+
+#include <log/log_main.h>
+#include <utils/Trace.h>
namespace android {
namespace renderengine {
namespace skia {
-AutoBackendTexture::AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer,
- bool isOutputBuffer, CleanupManager& cleanupMgr)
- : mCleanupMgr(cleanupMgr), mIsOutputBuffer(isOutputBuffer) {
- ATRACE_CALL();
- AHardwareBuffer_Desc desc;
- AHardwareBuffer_describe(buffer, &desc);
- bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
- GrBackendFormat backendFormat;
-
- GrBackendApi backend = context->backend();
- if (backend == GrBackendApi::kOpenGL) {
- backendFormat =
- GrAHardwareBufferUtils::GetGLBackendFormat(context, desc.format, false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeGLBackendTexture(context,
- buffer,
- desc.width,
- desc.height,
- &mDeleteProc,
- &mUpdateProc,
- &mImageCtx,
- createProtectedImage,
- backendFormat,
- isOutputBuffer);
- } else if (backend == GrBackendApi::kVulkan) {
- backendFormat =
- GrAHardwareBufferUtils::GetVulkanBackendFormat(context,
- buffer,
- desc.format,
- false);
- mBackendTexture =
- GrAHardwareBufferUtils::MakeVulkanBackendTexture(context,
- buffer,
- desc.width,
- desc.height,
- &mDeleteProc,
- &mUpdateProc,
- &mImageCtx,
- createProtectedImage,
- backendFormat,
- isOutputBuffer);
- } else {
- LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend));
- }
-
- mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
- if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
- LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
- "isWriteable:%d format:%d",
- this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
- desc.format);
- }
-}
-
-AutoBackendTexture::~AutoBackendTexture() {
- if (mBackendTexture.isValid()) {
- mDeleteProc(mImageCtx);
- mBackendTexture = {};
- }
-}
+AutoBackendTexture::AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture,
+ CleanupManager& cleanupMgr)
+ : mCleanupMgr(cleanupMgr), mBackendTexture(std::move(backendTexture)) {}
void AutoBackendTexture::unref(bool releaseLocalResources) {
if (releaseLocalResources) {
@@ -122,95 +62,32 @@
textureRelease->unref(false);
}
-void logFatalTexture(const char* msg, const GrBackendTexture& tex, ui::Dataspace dataspace,
- SkColorType colorType) {
- switch (tex.backend()) {
- case GrBackendApi::kOpenGL: {
- GrGLTextureInfo textureInfo;
- bool retrievedTextureInfo = GrBackendTextures::GetGLTextureInfo(tex, &textureInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
- "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
- " colorType %i",
- msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
- tex.height(), tex.hasMipmaps(), tex.isProtected(),
- static_cast<int>(tex.textureType()), retrievedTextureInfo,
- textureInfo.fTarget, textureInfo.fFormat, colorType);
- break;
- }
- case GrBackendApi::kVulkan: {
- GrVkImageInfo imageInfo;
- bool retrievedImageInfo = GrBackendTextures::GetVkImageInfo(tex, &imageInfo);
- LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
- "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
- "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
- "fSampleCount: %u fLevelCount: %u colorType %i",
- msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
- tex.height(), tex.hasMipmaps(), tex.isProtected(),
- static_cast<int>(tex.textureType()), retrievedImageInfo,
- imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
- colorType);
- break;
- }
- default:
- LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg, static_cast<unsigned>(tex.backend()));
- break;
- }
-}
-
-sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
- GrDirectContext* context) {
+sk_sp<SkImage> AutoBackendTexture::makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) {
ATRACE_CALL();
- if (mBackendTexture.isValid()) {
- mUpdateProc(mImageCtx, context);
- }
-
- auto colorType = mColorType;
- if (alphaType == kOpaque_SkAlphaType) {
- if (colorType == kRGBA_8888_SkColorType) {
- colorType = kRGB_888x_SkColorType;
- }
- }
-
- sk_sp<SkImage> image =
- SkImages::BorrowTextureFrom(context, mBackendTexture, kTopLeft_GrSurfaceOrigin,
- colorType, alphaType, toSkColorSpace(dataspace),
- releaseImageProc, this);
- if (image.get()) {
- // The following ref will be counteracted by releaseProc, when SkImage is discarded.
- ref();
- }
+ sk_sp<SkImage> image = mBackendTexture->makeImage(alphaType, dataspace, releaseImageProc, this);
+ // The following ref will be counteracted by releaseProc, when SkImage is discarded.
+ ref();
mImage = image;
mDataspace = dataspace;
- if (!mImage) {
- logFatalTexture("Unable to generate SkImage.", mBackendTexture, dataspace, colorType);
- }
return mImage;
}
-sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace,
- GrDirectContext* context) {
+sk_sp<SkSurface> AutoBackendTexture::getOrCreateSurface(ui::Dataspace dataspace) {
ATRACE_CALL();
- LOG_ALWAYS_FATAL_IF(!mIsOutputBuffer, "You can't generate a SkSurface for a read-only texture");
+ LOG_ALWAYS_FATAL_IF(!mBackendTexture->isOutputBuffer(),
+ "You can't generate an SkSurface for a read-only texture");
if (!mSurface.get() || mDataspace != dataspace) {
sk_sp<SkSurface> surface =
- SkSurfaces::WrapBackendTexture(context, mBackendTexture,
- kTopLeft_GrSurfaceOrigin, 0, mColorType,
- toSkColorSpace(dataspace), nullptr,
- releaseSurfaceProc, this);
- if (surface.get()) {
- // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
- ref();
- }
+ mBackendTexture->makeSurface(dataspace, releaseSurfaceProc, this);
+ // The following ref will be counteracted by releaseProc, when SkSurface is discarded.
+ ref();
+
mSurface = surface;
}
mDataspace = dataspace;
- if (!mSurface) {
- logFatalTexture("Unable to generate SkSurface.", mBackendTexture, dataspace, mColorType);
- }
return mSurface;
}
diff --git a/libs/renderengine/skia/AutoBackendTexture.h b/libs/renderengine/skia/AutoBackendTexture.h
index 509ac40..74daf47 100644
--- a/libs/renderengine/skia/AutoBackendTexture.h
+++ b/libs/renderengine/skia/AutoBackendTexture.h
@@ -16,7 +16,6 @@
#pragma once
-#include <GrAHardwareBufferUtils.h>
#include <GrDirectContext.h>
#include <SkImage.h>
#include <SkSurface.h>
@@ -24,8 +23,9 @@
#include <ui/GraphicTypes.h>
#include "android-base/macros.h"
+#include "compat/SkiaBackendTexture.h"
-#include <mutex>
+#include <memory>
#include <vector>
namespace android {
@@ -80,9 +80,8 @@
// of shared ownership with Skia objects, so we wrap it here instead.
class LocalRef {
public:
- LocalRef(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
- CleanupManager& cleanupMgr) {
- mTexture = new AutoBackendTexture(context, buffer, isOutputBuffer, cleanupMgr);
+ LocalRef(std::unique_ptr<SkiaBackendTexture> backendTexture, CleanupManager& cleanupMgr) {
+ mTexture = new AutoBackendTexture(std::move(backendTexture), cleanupMgr);
mTexture->ref();
}
@@ -95,17 +94,16 @@
// Makes a new SkImage from the texture content.
// As SkImages are immutable but buffer content is not, we create
// a new SkImage every time.
- sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
- GrDirectContext* context) {
- return mTexture->makeImage(dataspace, alphaType, context);
+ sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType) {
+ return mTexture->makeImage(dataspace, alphaType);
}
// Makes a new SkSurface from the texture content, if needed.
- sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context) {
- return mTexture->getOrCreateSurface(dataspace, context);
+ sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace) {
+ return mTexture->getOrCreateSurface(dataspace);
}
- SkColorType colorType() const { return mTexture->mColorType; }
+ SkColorType colorType() const { return mTexture->mBackendTexture->internalColorType(); }
DISALLOW_COPY_AND_ASSIGN(LocalRef);
@@ -114,12 +112,15 @@
};
private:
- // Creates a GrBackendTexture whose contents come from the provided buffer.
- AutoBackendTexture(GrDirectContext* context, AHardwareBuffer* buffer, bool isOutputBuffer,
+ DISALLOW_COPY_AND_ASSIGN(AutoBackendTexture);
+
+ // Creates an AutoBackendTexture to manage the lifecycle of a given SkiaBackendTexture, which is
+ // in turn backed by an underlying backend-specific texture type.
+ AutoBackendTexture(std::unique_ptr<SkiaBackendTexture> backendTexture,
CleanupManager& cleanupMgr);
// The only way to invoke dtor is with unref, when mUsageCount is 0.
- ~AutoBackendTexture();
+ ~AutoBackendTexture() = default;
void ref() { mUsageCount++; }
@@ -130,29 +131,21 @@
// Makes a new SkImage from the texture content.
// As SkImages are immutable but buffer content is not, we create
// a new SkImage every time.
- sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType,
- GrDirectContext* context);
+ sk_sp<SkImage> makeImage(ui::Dataspace dataspace, SkAlphaType alphaType);
// Makes a new SkSurface from the texture content, if needed.
- sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace, GrDirectContext* context);
-
- GrBackendTexture mBackendTexture;
- GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
- GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
- GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+ sk_sp<SkSurface> getOrCreateSurface(ui::Dataspace dataspace);
CleanupManager& mCleanupMgr;
static void releaseSurfaceProc(SkSurface::ReleaseContext releaseContext);
static void releaseImageProc(SkImages::ReleaseContext releaseContext);
+ std::unique_ptr<SkiaBackendTexture> mBackendTexture;
int mUsageCount = 0;
-
- const bool mIsOutputBuffer;
sk_sp<SkImage> mImage = nullptr;
sk_sp<SkSurface> mSurface = nullptr;
ui::Dataspace mDataspace = ui::Dataspace::UNKNOWN;
- SkColorType mColorType = kUnknown_SkColorType;
};
} // namespace skia
diff --git a/libs/renderengine/skia/Cache.cpp b/libs/renderengine/skia/Cache.cpp
index 7b3b176..59b0656 100644
--- a/libs/renderengine/skia/Cache.cpp
+++ b/libs/renderengine/skia/Cache.cpp
@@ -630,7 +630,7 @@
// kFlushAfterEveryLayer = true
// in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp
// gPrintSKSL = true
-void Cache::primeShaderCache(SkiaRenderEngine* renderengine, bool shouldPrimeUltraHDR) {
+void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) {
const int previousCount = renderengine->reportShadersCompiled();
if (previousCount) {
ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount);
@@ -694,13 +694,24 @@
impl::ExternalTexture>(srcBuffer, *renderengine,
impl::ExternalTexture::Usage::READABLE |
impl::ExternalTexture::Usage::WRITEABLE);
- drawHolePunchLayer(renderengine, display, dstTexture);
- drawSolidLayers(renderengine, display, dstTexture);
- drawSolidLayers(renderengine, p3Display, dstTexture);
- drawSolidDimmedLayers(renderengine, display, dstTexture);
- drawShadowLayers(renderengine, display, srcTexture);
- drawShadowLayers(renderengine, p3Display, srcTexture);
+ if (config.cacheHolePunchLayer) {
+ drawHolePunchLayer(renderengine, display, dstTexture);
+ }
+
+ if (config.cacheSolidLayers) {
+ drawSolidLayers(renderengine, display, dstTexture);
+ drawSolidLayers(renderengine, p3Display, dstTexture);
+ }
+
+ if (config.cacheSolidDimmedLayers) {
+ drawSolidDimmedLayers(renderengine, display, dstTexture);
+ }
+
+ if (config.cacheShadowLayers) {
+ drawShadowLayers(renderengine, display, srcTexture);
+ drawShadowLayers(renderengine, p3Display, srcTexture);
+ }
if (renderengine->supportsBackgroundBlur()) {
drawBlurLayers(renderengine, display, dstTexture);
@@ -736,27 +747,40 @@
}
for (auto texture : textures) {
- drawImageLayers(renderengine, display, dstTexture, texture);
+ if (config.cacheImageLayers) {
+ drawImageLayers(renderengine, display, dstTexture, texture);
+ }
- drawImageDimmedLayers(renderengine, display, dstTexture, texture);
- drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture);
- drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture);
+ if (config.cacheImageDimmedLayers) {
+ drawImageDimmedLayers(renderengine, display, dstTexture, texture);
+ drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture);
+ drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture);
+ }
- // Draw layers for b/185569240.
- drawClippedLayers(renderengine, display, dstTexture, texture);
+ if (config.cacheClippedLayers) {
+ // Draw layers for b/185569240.
+ drawClippedLayers(renderengine, display, dstTexture, texture);
+ }
}
- drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
+ if (config.cachePIPImageLayers) {
+ drawPIPImageLayer(renderengine, display, dstTexture, externalTexture);
+ }
- drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture, externalTexture);
- drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture);
- drawTransparentImageDimmedLayers(renderengine, p3Display, dstTexture, externalTexture);
- drawTransparentImageDimmedLayers(renderengine, p3DisplayEnhance, dstTexture,
- externalTexture);
+ if (config.cacheTransparentImageDimmedLayers) {
+ drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture,
+ externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, p3Display, dstTexture, externalTexture);
+ drawTransparentImageDimmedLayers(renderengine, p3DisplayEnhance, dstTexture,
+ externalTexture);
+ }
- drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+ if (config.cacheClippedDimmedImageLayers) {
+ drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
+ }
- if (shouldPrimeUltraHDR) {
+ if (config.cacheUltraHDR) {
drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture);
diff --git a/libs/renderengine/skia/Cache.h b/libs/renderengine/skia/Cache.h
index 62f6705..259432f 100644
--- a/libs/renderengine/skia/Cache.h
+++ b/libs/renderengine/skia/Cache.h
@@ -16,16 +16,21 @@
#pragma once
-namespace android::renderengine::skia {
+namespace android::renderengine {
+
+struct PrimeCacheConfig;
+
+namespace skia {
class SkiaRenderEngine;
class Cache {
public:
- static void primeShaderCache(SkiaRenderEngine*, bool shouldPrimeUltraHDR);
+ static void primeShaderCache(SkiaRenderEngine*, PrimeCacheConfig config);
private:
Cache() = default;
};
-} // namespace android::renderengine::skia
+} // namespace skia
+} // namespace android::renderengine
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.cpp b/libs/renderengine/skia/GaneshVkRenderEngine.cpp
new file mode 100644
index 0000000..68798bf
--- /dev/null
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.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 "GaneshVkRenderEngine.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
+
+#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 =
+ reinterpret_cast<SkiaVkRenderEngine::DestroySemaphoreInfo*>(semaphore);
+ 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;
+
+ const 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());
+ GrBackendSemaphore beSemaphore = GrBackendSemaphores::MakeVk(waitSemaphore);
+ constexpr bool kDeleteAfterWait = true;
+ context->grDirectContext()->wait(1, &beSemaphore, kDeleteAfterWait);
+}
+
+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;
+ DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
+ if (semaphore != VK_NULL_HANDLE) {
+ destroySemaphoreInfo = new DestroySemaphoreInfo(vi, semaphore);
+ flushInfo.fNumSemaphores = 1;
+ flushInfo.fSignalSemaphores = &backendSemaphore;
+ flushInfo.fFinishedProc = unref_semaphore;
+ flushInfo.fFinishedContext = destroySemaphoreInfo;
+ }
+ GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
+ grContext->submit(GrSyncCpu::kNo);
+ int drawFenceFd = -1;
+ if (semaphore != VK_NULL_HANDLE) {
+ if (GrSemaphoresSubmitted::kYes == submitted) {
+ drawFenceFd = vi.exportSemaphoreSyncFd(semaphore);
+ }
+ // Now that drawFenceFd has been created, we can delete our reference to this semaphore
+ flushInfo.fFinishedProc(destroySemaphoreInfo);
+ }
+ base::unique_fd res(drawFenceFd);
+ return res;
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/GaneshVkRenderEngine.h b/libs/renderengine/skia/GaneshVkRenderEngine.h
new file mode 100644
index 0000000..e6123c2
--- /dev/null
+++ b/libs/renderengine/skia/GaneshVkRenderEngine.h
@@ -0,0 +1,36 @@
+/*
+ * 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 "skia/SkiaVkRenderEngine.h"
+
+namespace android::renderengine::skia {
+
+class GaneshVkRenderEngine : public SkiaVkRenderEngine {
+public:
+ static std::unique_ptr<GaneshVkRenderEngine> 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:
+ 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 fea4129..48270e1 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -21,6 +21,8 @@
#include "SkiaGLRenderEngine.h"
+#include "compat/SkiaGpuContext.h"
+
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GrContextOptions.h>
@@ -207,7 +209,7 @@
std::unique_ptr<SkiaGLRenderEngine> engine(new SkiaGLRenderEngine(args, display, ctxt,
placeholder, protectedContext,
protectedPlaceholder));
- engine->ensureGrContextsCreated();
+ engine->ensureContextsCreated();
ALOGI("OpenGL ES informations:");
ALOGI("vendor : %s", extensions.getVendor());
@@ -269,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),
@@ -277,7 +279,7 @@
mProtectedPlaceholderSurface(protectedPlaceholder) {}
SkiaGLRenderEngine::~SkiaGLRenderEngine() {
- finishRenderingAndAbandonContext();
+ finishRenderingAndAbandonContexts();
if (mPlaceholderSurface != EGL_NO_SURFACE) {
eglDestroySurface(mEGLDisplay, mPlaceholderSurface);
}
@@ -295,9 +297,7 @@
eglReleaseThread();
}
-SkiaRenderEngine::Contexts SkiaGLRenderEngine::createDirectContexts(
- const GrContextOptions& options) {
-
+SkiaRenderEngine::Contexts SkiaGLRenderEngine::createContexts() {
LOG_ALWAYS_FATAL_IF(isProtected(),
"Cannot setup contexts while already in protected mode");
@@ -306,10 +306,10 @@
LOG_ALWAYS_FATAL_IF(!glInterface.get(), "GrGLMakeNativeInterface() failed");
SkiaRenderEngine::Contexts contexts;
- contexts.first = GrDirectContexts::MakeGL(glInterface, options);
+ contexts.first = SkiaGpuContext::MakeGL_Ganesh(glInterface, mSkSLCacheMonitor);
if (supportsProtectedContentImpl()) {
useProtectedContextImpl(GrProtected::kYes);
- contexts.second = GrDirectContexts::MakeGL(glInterface, options);
+ contexts.second = SkiaGpuContext::MakeGL_Ganesh(glInterface, mSkSLCacheMonitor);
useProtectedContextImpl(GrProtected::kNo);
}
@@ -330,15 +330,21 @@
return eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE;
}
-void SkiaGLRenderEngine::waitFence(GrDirectContext*, base::borrowed_fd fenceFd) {
+void SkiaGLRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) {
if (fenceFd.get() >= 0 && !waitGpuFence(fenceFd)) {
ATRACE_NAME("SkiaGLRenderEngine::waitFence");
sync_wait(fenceFd.get(), -1);
}
}
-base::unique_fd SkiaGLRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
- 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 = grContext->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 af33110..bd177e6 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -59,11 +59,11 @@
protected:
// Implementations of abstract SkiaRenderEngine functions specific to
// rendering backend
- virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options);
+ virtual SkiaRenderEngine::Contexts createContexts();
bool supportsProtectedContentImpl() const override;
bool useProtectedContextImpl(GrProtected isProtected) override;
- void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override;
- base::unique_fd flushAndSubmit(GrDirectContext* context) override;
+ void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) 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 6e393f0..e62640e 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaRenderEngine.cpp
@@ -74,11 +74,14 @@
#include "Cache.h"
#include "ColorSpaces.h"
+#include "compat/SkiaGpuContext.h"
#include "filters/BlurFilter.h"
#include "filters/GaussianBlurFilter.h"
#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
+#include "filters/MouriMap.h"
#include "log/log_main.h"
+#include "skia/compat/SkiaBackendTexture.h"
#include "skia/debug/SkiaCapture.h"
#include "skia/debug/SkiaMemoryReporter.h"
#include "skia/filters/StretchShaderFactory.h"
@@ -88,8 +91,7 @@
// Debugging settings
static const bool kPrintLayerSettings = false;
-static const bool kFlushAfterEveryLayer = kPrintLayerSettings;
-static constexpr bool kEnableLayerBrightening = true;
+static const bool kGaneshFlushAfterEveryLayer = kPrintLayerSettings;
} // namespace
@@ -244,8 +246,8 @@
using base::StringAppendF;
-std::future<void> SkiaRenderEngine::primeCache(bool shouldPrimeUltraHDR) {
- Cache::primeShaderCache(this, shouldPrimeUltraHDR);
+std::future<void> SkiaRenderEngine::primeCache(PrimeCacheConfig config) {
+ Cache::primeShaderCache(this, config);
return {};
}
@@ -271,34 +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 (mGrContext) {
- mGrContext->flushAndSubmit(GrSyncCpu::kYes);
- mGrContext->abandonContext();
- }
+ // Leftover textures may hold refs to backend-specific Skia contexts, which must be released
+ // before ~SkiaGpuContext is called.
+ mTextureCleanupMgr.setDeferredStatus(false);
+ mTextureCleanupMgr.cleanup();
- if (mProtectedGrContext) {
- mProtectedGrContext->flushAndSubmit(GrSyncCpu::kYes);
- mProtectedGrContext->abandonContext();
- }
+ // ~SkiaGpuContext must be called before GPU API contexts are torn down.
+ mContext.reset();
+ mProtectedContext.reset();
}
void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) {
@@ -308,24 +323,24 @@
}
// release any scratch resources before switching into a new mode
- if (getActiveGrContext()) {
- getActiveGrContext()->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly);
+ if (getActiveContext()) {
+ getActiveContext()->purgeUnlockedScratchResources();
}
// Backend-specific way to switch to protected context
if (useProtectedContextImpl(
useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) {
mInProtectedContext = useProtectedContext;
- // given that we are sharing the same thread between two GrContexts we need to
+ // given that we are sharing the same thread between two contexts we need to
// make sure that the thread state is reset when switching between the two.
- if (getActiveGrContext()) {
- getActiveGrContext()->resetContext();
+ if (getActiveContext()) {
+ getActiveContext()->resetContextIfApplicable();
}
}
}
-GrDirectContext* SkiaRenderEngine::getActiveGrContext() {
- return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get();
+SkiaGpuContext* SkiaRenderEngine::getActiveContext() {
+ return mInProtectedContext ? mProtectedContext.get() : mContext.get();
}
static float toDegrees(uint32_t transform) {
@@ -374,17 +389,12 @@
sourceTransfer != destTransfer;
}
-void SkiaRenderEngine::ensureGrContextsCreated() {
- if (mGrContext) {
+void SkiaRenderEngine::ensureContextsCreated() {
+ if (mContext) {
return;
}
- GrContextOptions options;
- options.fDisableDriverCorrectnessWorkarounds = true;
- options.fDisableDistanceFieldPaths = true;
- options.fReducedShaderVariations = true;
- options.fPersistentCache = &mSkSLCacheMonitor;
- std::tie(mGrContext, mProtectedGrContext) = createDirectContexts(options);
+ std::tie(mContext, mProtectedContext) = createContexts();
}
void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
@@ -410,10 +420,8 @@
// 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.
- auto grContext = getActiveGrContext();
+ // switch back after the buffer is cached).
+ auto context = getActiveContext();
auto& cache = mTextureCache;
std::lock_guard<std::mutex> lock(mRenderingMutex);
@@ -423,10 +431,11 @@
if (FlagManager::getInstance().renderable_buffer_usage()) {
isRenderable = buffer->getUsage() & GRALLOC_USAGE_HW_RENDER;
}
- std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef =
- std::make_shared<AutoBackendTexture::LocalRef>(grContext,
- buffer->toAHardwareBuffer(),
- isRenderable, mTextureCleanupMgr);
+ std::unique_ptr<SkiaBackendTexture> backendTexture =
+ context->makeBackendTexture(buffer->toAHardwareBuffer(), isRenderable);
+ auto imageTextureRef =
+ std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture),
+ mTextureCleanupMgr);
cache.insert({buffer->getId(), imageTextureRef});
}
}
@@ -477,9 +486,10 @@
return it->second;
}
}
- return std::make_shared<AutoBackendTexture::LocalRef>(getActiveGrContext(),
- buffer->toAHardwareBuffer(),
- isOutputBuffer, mTextureCleanupMgr);
+ std::unique_ptr<SkiaBackendTexture> backendTexture =
+ getActiveContext()->makeBackendTexture(buffer->toAHardwareBuffer(), isOutputBuffer);
+ return std::make_shared<AutoBackendTexture::LocalRef>(std::move(backendTexture),
+ mTextureCleanupMgr);
}
bool SkiaRenderEngine::canSkipPostRenderCleanup() const {
@@ -500,9 +510,9 @@
// Determine later on if we need to leverage the stertch shader within
// surface flinger
const auto& stretchEffect = parameters.layer.stretchEffect;
+ const auto& targetBuffer = parameters.layer.source.buffer.buffer;
auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
@@ -510,6 +520,24 @@
}
if (parameters.requiresLinearEffect) {
+ const auto format = targetBuffer != nullptr
+ ? std::optional<ui::PixelFormat>(
+ static_cast<ui::PixelFormat>(targetBuffer->getPixelFormat()))
+ : std::nullopt;
+
+ if (parameters.display.tonemapStrategy == DisplaySettings::TonemapStrategy::Local) {
+ // TODO: Handle color matrix transforms in linear space.
+ SkImage* image = parameters.shader->isAImage((SkMatrix*)nullptr, (SkTileMode*)nullptr);
+ if (image) {
+ static MouriMap kMapper;
+ const float ratio = getHdrRenderType(parameters.layer.sourceDataspace, format) ==
+ HdrRenderType::GENERIC_HDR
+ ? 1.0f
+ : parameters.layerDimmingRatio;
+ return kMapper.mouriMap(getActiveContext(), parameters.shader, ratio);
+ }
+ }
+
auto effect =
shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace,
.outputDataspace = parameters.outputDataSpace,
@@ -667,9 +695,9 @@
validateOutputBufferUsage(buffer->getBuffer());
- auto grContext = getActiveGrContext();
- LOG_ALWAYS_FATAL_IF(grContext->abandoned(), "GrContext is abandoned/device lost at start of %s",
- __func__);
+ auto context = getActiveContext();
+ 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);
@@ -677,10 +705,9 @@
auto surfaceTextureRef = getOrCreateBackendTexture(buffer->getBuffer(), true);
// wait on the buffer to be ready to use prior to using it
- waitFence(grContext, bufferFence);
+ waitFence(context, bufferFence);
- sk_sp<SkSurface> dstSurface =
- surfaceTextureRef->getOrCreateSurface(display.outputDataspace, grContext);
+ sk_sp<SkSurface> dstSurface = surfaceTextureRef->getOrCreateSurface(display.outputDataspace);
SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get());
if (dstCanvas == nullptr) {
@@ -704,9 +731,7 @@
[&](const auto& l) { return l.whitePointNits; });
// ...and compute the dimming ratio if dimming is requested
- const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
- maxLayerWhitePoint > 0.f &&
- (kEnableLayerBrightening || display.targetLuminanceNits > maxLayerWhitePoint)
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f && maxLayerWhitePoint > 0.f
? maxLayerWhitePoint / display.targetLuminanceNits
: 1.f;
@@ -845,7 +870,7 @@
if (blurRect.width() > 0 && blurRect.height() > 0) {
if (layer.backgroundBlurRadius > 0) {
ATRACE_NAME("BackgroundBlur");
- auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius,
+ auto blurredImage = mBlurFilter->generate(context, layer.backgroundBlurRadius,
blurInput, blurRect);
cachedBlurs[layer.backgroundBlurRadius] = blurredImage;
@@ -859,7 +884,7 @@
if (cachedBlurs[region.blurRadius] == nullptr) {
ATRACE_NAME("BlurRegion");
cachedBlurs[region.blurRadius] =
- mBlurFilter->generate(grContext, region.blurRadius, blurInput,
+ mBlurFilter->generate(context, region.blurRadius, blurInput,
blurRect);
}
@@ -948,7 +973,7 @@
// if the layer's buffer has a fence, then we must must respect the fence prior to using
// the buffer.
if (layer.source.buffer.fence != nullptr) {
- waitFence(grContext, layer.source.buffer.fence->get());
+ waitFence(context, layer.source.buffer.fence->get());
}
// isOpaque means we need to ignore the alpha in the image,
@@ -972,7 +997,7 @@
: item.isOpaque ? kOpaque_SkAlphaType
: item.usePremultipliedAlpha ? kPremul_SkAlphaType
: kUnpremul_SkAlphaType;
- sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext);
+ sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType);
auto texMatrix = getSkM44(item.textureTransform).asM33();
// textureTansform was intended to be passed directly into a shader, so when
@@ -1126,40 +1151,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(grContext));
+ LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface);
+ auto drawFence = sp<Fence>::make(flushAndSubmit(context, dstSurface));
if (ATRACE_ENABLED()) {
static gui::FenceMonitor sMonitor("RE Completion");
@@ -1169,11 +1175,11 @@
}
size_t SkiaRenderEngine::getMaxTextureSize() const {
- return mGrContext->maxTextureSize();
+ return mContext->getMaxTextureSize();
}
size_t SkiaRenderEngine::getMaxViewportDims() const {
- return mGrContext->maxRenderTargetSize();
+ return mContext->getMaxRenderTargetSize();
}
void SkiaRenderEngine::drawShadow(SkCanvas* canvas,
@@ -1199,13 +1205,13 @@
const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER;
// start by resizing the current context
- getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
+ getActiveContext()->setResourceCacheLimit(maxResourceBytes);
// if it is possible to switch contexts then we will resize the other context
const bool originalProtectedState = mInProtectedContext;
useProtectedContext(!mInProtectedContext);
if (mInProtectedContext != originalProtectedState) {
- getActiveGrContext()->setResourceCacheLimit(maxResourceBytes);
+ getActiveContext()->setResourceCacheLimit(maxResourceBytes);
// reset back to the initial context that was active when this method was called
useProtectedContext(originalProtectedState);
}
@@ -1245,7 +1251,7 @@
{"skia", "Other"},
};
SkiaMemoryReporter gpuReporter(gpuResourceMap, true);
- mGrContext->dumpMemoryStatistics(&gpuReporter);
+ mContext->dumpMemoryStatistics(&gpuReporter);
StringAppendF(&result, "Skia's GPU Caches: ");
gpuReporter.logTotals(result);
gpuReporter.logOutput(result);
@@ -1269,8 +1275,8 @@
StringAppendF(&result, "\n");
SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true);
- if (mProtectedGrContext) {
- mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter);
+ if (mProtectedContext) {
+ mProtectedContext->dumpMemoryStatistics(&gpuProtectedReporter);
}
StringAppendF(&result, "Skia's GPU Protected Caches: ");
gpuProtectedReporter.logTotals(result);
diff --git a/libs/renderengine/skia/SkiaRenderEngine.h b/libs/renderengine/skia/SkiaRenderEngine.h
index e88d44c..c8f9241 100644
--- a/libs/renderengine/skia/SkiaRenderEngine.h
+++ b/libs/renderengine/skia/SkiaRenderEngine.h
@@ -21,21 +21,21 @@
#include <sys/types.h>
#include <GrBackendSemaphore.h>
-#include <GrDirectContext.h>
#include <SkSurface.h>
#include <android-base/thread_annotations.h>
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
+#include <memory>
#include <mutex>
#include <unordered_map>
#include "AutoBackendTexture.h"
#include "GrContextOptions.h"
#include "SkImageInfo.h"
-#include "SkiaRenderEngine.h"
#include "android-base/macros.h"
+#include "compat/SkiaGpuContext.h"
#include "debug/SkiaCapture.h"
#include "filters/BlurFilter.h"
#include "filters/LinearEffect.h"
@@ -59,10 +59,10 @@
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;
+ std::future<void> primeCache(PrimeCacheConfig config) override final;
void cleanupPostRender() override final;
bool supportsBackgroundBlur() override final {
return mBlurFilter != nullptr;
@@ -76,24 +76,27 @@
bool supportsProtectedContent() const override {
return supportsProtectedContentImpl();
}
- void ensureGrContextsCreated();
+ 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<sk_sp<GrDirectContext>, sk_sp<GrDirectContext>>;
- virtual Contexts createDirectContexts(const GrContextOptions& options) = 0;
+ using Contexts = std::pair<unique_ptr<SkiaGpuContext>, unique_ptr<SkiaGpuContext>>;
+ virtual Contexts createContexts() = 0;
virtual bool supportsProtectedContentImpl() const = 0;
virtual bool useProtectedContextImpl(GrProtected isProtected) = 0;
- virtual void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) = 0;
- virtual base::unique_fd flushAndSubmit(GrDirectContext* context) = 0;
+ virtual void waitFence(SkiaGpuContext* context, base::borrowed_fd fenceFd) = 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;
size_t getMaxViewportDims() const override final;
- GrDirectContext* getActiveGrContext();
+ // TODO: b/293371537 - Return reference instead of pointer? (Cleanup)
+ SkiaGpuContext* getActiveContext();
bool isProtected() const { return mInProtectedContext; }
@@ -121,6 +124,8 @@
int mTotalShadersCompiled = 0;
};
+ SkSLCacheMonitor mSkSLCacheMonitor;
+
private:
void mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer,
bool isRenderable) override final;
@@ -163,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>
@@ -183,12 +185,11 @@
// Mutex guarding rendering operations, so that internal state related to
// rendering that is potentially modified by multiple threads is guaranteed thread-safe.
mutable std::mutex mRenderingMutex;
- SkSLCacheMonitor mSkSLCacheMonitor;
// Graphics context used for creating surfaces and submitting commands
- sk_sp<GrDirectContext> mGrContext;
+ unique_ptr<SkiaGpuContext> mContext;
// Same as above, but for protected content (eg. DRM)
- sk_sp<GrDirectContext> mProtectedGrContext;
+ unique_ptr<SkiaGpuContext> mProtectedContext;
bool mInProtectedContext = false;
};
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.cpp b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
index eb7a9d5..bd50107 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.cpp
@@ -21,22 +21,24 @@
#include "SkiaVkRenderEngine.h"
+#include "GaneshVkRenderEngine.h"
+#include "compat/SkiaGpuContext.h"
+
#include <GrBackendSemaphore.h>
#include <GrContextOptions.h>
+#include <GrDirectContext.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
+#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <vk/GrVkExtensions.h>
#include <vk/GrVkTypes.h>
-#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
#include <android-base/stringprintf.h>
#include <gui/TraceUtils.h>
#include <sync/sync.h>
#include <utils/Trace.h>
-#include <cstdint>
#include <memory>
-#include <sstream>
#include <string>
-#include <vector>
#include <vulkan/vulkan.h>
#include "log/log_main.h"
@@ -44,619 +46,19 @@
namespace android {
namespace renderengine {
-struct VulkanFuncs {
- PFN_vkCreateSemaphore vkCreateSemaphore = nullptr;
- PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;
- PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr;
- PFN_vkDestroySemaphore vkDestroySemaphore = nullptr;
-
- PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr;
- PFN_vkDestroyDevice vkDestroyDevice = nullptr;
- PFN_vkDestroyInstance vkDestroyInstance = nullptr;
-};
-
-// Ref-Count a semaphore
-struct DestroySemaphoreInfo {
- VkSemaphore mSemaphore;
- // We need to make sure we don't delete the VkSemaphore until it is done being used by both Skia
- // (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two refs, one
- // owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented each time
- // delete_semaphore* is called with this object. Skia will call destroy_semaphore* once it is
- // done with the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine
- // calls delete_semaphore* after sending the semaphore to Skia and exporting it if need be.
- int mRefs = 2;
-
- DestroySemaphoreInfo(VkSemaphore semaphore) : mSemaphore(semaphore) {}
-};
-
-namespace {
-void onVkDeviceFault(void* callbackContext, const std::string& description,
- const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
- const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
- const std::vector<std::byte>& vendorBinaryData);
-} // anonymous namespace
-
-struct VulkanInterface {
- bool initialized = false;
- VkInstance instance;
- VkPhysicalDevice physicalDevice;
- VkDevice device;
- VkQueue queue;
- int queueIndex;
- uint32_t apiVersion;
- GrVkExtensions grExtensions;
- VkPhysicalDeviceFeatures2* physicalDeviceFeatures2 = nullptr;
- VkPhysicalDeviceSamplerYcbcrConversionFeatures* samplerYcbcrConversionFeatures = nullptr;
- VkPhysicalDeviceProtectedMemoryFeatures* protectedMemoryFeatures = nullptr;
- VkPhysicalDeviceFaultFeaturesEXT* deviceFaultFeatures = nullptr;
- GrVkGetProc grGetProc;
- bool isProtected;
- bool isRealtimePriority;
-
- VulkanFuncs funcs;
-
- std::vector<std::string> instanceExtensionNames;
- std::vector<std::string> deviceExtensionNames;
-
- GrVkBackendContext getBackendContext() {
- GrVkBackendContext backendContext;
- backendContext.fInstance = instance;
- backendContext.fPhysicalDevice = physicalDevice;
- backendContext.fDevice = device;
- backendContext.fQueue = queue;
- backendContext.fGraphicsQueueIndex = queueIndex;
- backendContext.fMaxAPIVersion = apiVersion;
- backendContext.fVkExtensions = &grExtensions;
- backendContext.fDeviceFeatures2 = physicalDeviceFeatures2;
- backendContext.fGetProc = grGetProc;
- backendContext.fProtectedContext = isProtected ? GrProtected::kYes : GrProtected::kNo;
- backendContext.fDeviceLostContext = this; // VulkanInterface is long-lived
- backendContext.fDeviceLostProc = onVkDeviceFault;
- return backendContext;
- };
-
- VkSemaphore createExportableSemaphore() {
- VkExportSemaphoreCreateInfo exportInfo;
- exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO;
- exportInfo.pNext = nullptr;
- exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkSemaphoreCreateInfo semaphoreInfo;
- semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreInfo.pNext = &exportInfo;
- semaphoreInfo.flags = 0;
-
- VkSemaphore semaphore;
- VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
- if (VK_SUCCESS != err) {
- ALOGE("%s: failed to create semaphore. err %d\n", __func__, err);
- return VK_NULL_HANDLE;
- }
-
- return semaphore;
- }
-
- // syncFd cannot be <= 0
- VkSemaphore importSemaphoreFromSyncFd(int syncFd) {
- VkSemaphoreCreateInfo semaphoreInfo;
- semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
- semaphoreInfo.pNext = nullptr;
- semaphoreInfo.flags = 0;
-
- VkSemaphore semaphore;
- VkResult err = funcs.vkCreateSemaphore(device, &semaphoreInfo, nullptr, &semaphore);
- if (VK_SUCCESS != err) {
- ALOGE("%s: failed to create import semaphore", __func__);
- return VK_NULL_HANDLE;
- }
-
- VkImportSemaphoreFdInfoKHR importInfo;
- importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
- importInfo.pNext = nullptr;
- importInfo.semaphore = semaphore;
- importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
- importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
- importInfo.fd = syncFd;
-
- err = funcs.vkImportSemaphoreFdKHR(device, &importInfo);
- if (VK_SUCCESS != err) {
- funcs.vkDestroySemaphore(device, semaphore, nullptr);
- ALOGE("%s: failed to import semaphore", __func__);
- return VK_NULL_HANDLE;
- }
-
- return semaphore;
- }
-
- int exportSemaphoreSyncFd(VkSemaphore semaphore) {
- int res;
-
- VkSemaphoreGetFdInfoKHR getFdInfo;
- getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
- getFdInfo.pNext = nullptr;
- getFdInfo.semaphore = semaphore;
- getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
-
- VkResult err = funcs.vkGetSemaphoreFdKHR(device, &getFdInfo, &res);
- if (VK_SUCCESS != err) {
- ALOGE("%s: failed to export semaphore, err: %d", __func__, err);
- return -1;
- }
- return res;
- }
-
- void destroySemaphore(VkSemaphore semaphore) {
- funcs.vkDestroySemaphore(device, semaphore, nullptr);
- }
-};
-
-namespace {
-void onVkDeviceFault(void* callbackContext, const std::string& description,
- const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
- const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
- const std::vector<std::byte>& vendorBinaryData) {
- VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext);
- const std::string protectedStr = interface->isProtected ? "protected" : "non-protected";
- // The final crash string should contain as much differentiating info as possible, up to 1024
- // bytes. As this final message is constructed, the same information is also dumped to the logs
- // but in a more verbose format. Building the crash string is unsightly, so the clearer logging
- // statement is always placed first to give context.
- ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str());
- std::stringstream crashMsg;
- crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr;
-
- if (!addressInfos.empty()) {
- ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size());
- crashMsg << ", " << addressInfos.size() << " address info (";
- for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) {
- ALOGE(" addressType: %d", (int)addressInfo.addressType);
- ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress);
- ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision);
- crashMsg << addressInfo.addressType << ":"
- << addressInfo.reportedAddress << ":"
- << addressInfo.addressPrecision << ", ";
- }
- crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
- crashMsg << ")";
- }
-
- if (!vendorInfos.empty()) {
- ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size());
- crashMsg << ", " << vendorInfos.size() << " vendor info (";
- for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) {
- ALOGE(" description: %s", vendorInfo.description);
- ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode);
- ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData);
- // Omit descriptions for individual vendor info structs in the crash string, as the
- // fault code and fault data fields should be enough for clustering, and the verbosity
- // isn't worth it. Additionally, vendors may just set the general description field of
- // the overall fault to the description of the first element in this list, and that
- // overall description will be placed at the end of the crash string.
- crashMsg << vendorInfo.vendorFaultCode << ":"
- << vendorInfo.vendorFaultData << ", ";
- }
- crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
- crashMsg << ")";
- }
-
- if (!vendorBinaryData.empty()) {
- // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports
- ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics"
- " Stack team if you observe this message).",
- vendorBinaryData.size());
- crashMsg << ", " << vendorBinaryData.size() << " bytes binary";
- }
-
- crashMsg << "): " << description;
- LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
-};
-} // anonymous namespace
-
-static GrVkGetProc sGetProc = [](const char* proc_name, VkInstance instance, VkDevice device) {
- if (device != VK_NULL_HANDLE) {
- return vkGetDeviceProcAddr(device, proc_name);
- }
- return vkGetInstanceProcAddr(instance, proc_name);
-};
-
-#define BAIL(fmt, ...) \
- { \
- ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \
- return interface; \
- }
-
-#define CHECK_NONNULL(expr) \
- if ((expr) == nullptr) { \
- BAIL("[%s] null", #expr); \
- }
-
-#define VK_CHECK(expr) \
- if ((expr) != VK_SUCCESS) { \
- BAIL("[%s] failed. err = %d", #expr, expr); \
- return interface; \
- }
-
-#define VK_GET_PROC(F) \
- PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \
- CHECK_NONNULL(vk##F)
-#define VK_GET_INST_PROC(instance, F) \
- PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \
- CHECK_NONNULL(vk##F)
-#define VK_GET_DEV_PROC(device, F) \
- PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \
- CHECK_NONNULL(vk##F)
-
-VulkanInterface initVulkanInterface(bool protectedContent = false) {
- const nsecs_t timeBefore = systemTime();
- VulkanInterface interface;
-
- VK_GET_PROC(EnumerateInstanceVersion);
- uint32_t instanceVersion;
- VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion));
-
- if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
- return interface;
- }
-
- const VkApplicationInfo appInfo = {
- VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0,
- VK_MAKE_VERSION(1, 1, 0),
- };
-
- VK_GET_PROC(EnumerateInstanceExtensionProperties);
-
- uint32_t extensionCount = 0;
- VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr));
- std::vector<VkExtensionProperties> instanceExtensions(extensionCount);
- VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
- instanceExtensions.data()));
- std::vector<const char*> enabledInstanceExtensionNames;
- enabledInstanceExtensionNames.reserve(instanceExtensions.size());
- interface.instanceExtensionNames.reserve(instanceExtensions.size());
- for (const auto& instExt : instanceExtensions) {
- enabledInstanceExtensionNames.push_back(instExt.extensionName);
- interface.instanceExtensionNames.push_back(instExt.extensionName);
- }
-
- const VkInstanceCreateInfo instanceCreateInfo = {
- VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- nullptr,
- 0,
- &appInfo,
- 0,
- nullptr,
- (uint32_t)enabledInstanceExtensionNames.size(),
- enabledInstanceExtensionNames.data(),
- };
-
- VK_GET_PROC(CreateInstance);
- VkInstance instance;
- VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
-
- VK_GET_INST_PROC(instance, DestroyInstance);
- interface.funcs.vkDestroyInstance = vkDestroyInstance;
- VK_GET_INST_PROC(instance, EnumeratePhysicalDevices);
- VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties);
- VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2);
- VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties);
- VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2);
- VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2);
- VK_GET_INST_PROC(instance, CreateDevice);
-
- uint32_t physdevCount;
- VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr));
- if (physdevCount == 0) {
- BAIL("Could not find any physical devices");
- }
-
- physdevCount = 1;
- VkPhysicalDevice physicalDevice;
- VkResult enumeratePhysDevsErr =
- vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice);
- if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) {
- BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d",
- enumeratePhysDevsErr);
- }
-
- VkPhysicalDeviceProperties2 physDevProps = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
- 0,
- {},
- };
- VkPhysicalDeviceProtectedMemoryProperties protMemProps = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
- 0,
- {},
- };
-
- if (protectedContent) {
- physDevProps.pNext = &protMemProps;
- }
-
- 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");
- }
-
- // Check for syncfd support. Bail if we cannot both import and export them.
- VkPhysicalDeviceExternalSemaphoreInfo semInfo = {
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
- nullptr,
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- };
- VkExternalSemaphoreProperties semProps = {
- VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0,
- };
- vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps);
-
- bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes &
- VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
- (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
- (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) &&
- (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
-
- if (!sufficientSemaphoreSyncFdSupport) {
- BAIL("Vulkan device does not support sufficient external semaphore sync fd features. "
- "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
- "compatibleHandleTypes 0x%x (needed 0x%x) "
- "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
- semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- semProps.externalSemaphoreFeatures,
- VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
- VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
- } else {
- ALOGD("Vulkan device supports sufficient external semaphore sync fd features. "
- "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
- "compatibleHandleTypes 0x%x (needed 0x%x) "
- "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
- semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
- semProps.externalSemaphoreFeatures,
- VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
- VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
- }
-
- uint32_t queueCount;
- vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr);
- if (queueCount == 0) {
- BAIL("Could not find queues for physical device");
- }
-
- std::vector<VkQueueFamilyProperties2> queueProps(queueCount);
- std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount);
- VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR;
- // Even though we don't yet know if the VK_EXT_global_priority extension is available,
- // we can safely add the request to the pNext chain, and if the extension is not
- // available, it will be ignored.
- for (uint32_t i = 0; i < queueCount; ++i) {
- queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT;
- queuePriorityProps[i].pNext = nullptr;
- queueProps[i].pNext = &queuePriorityProps[i];
- }
- vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data());
-
- int graphicsQueueIndex = -1;
- for (uint32_t i = 0; i < queueCount; ++i) {
- // Look at potential answers to the VK_EXT_global_priority query. If answers were
- // provided, we may adjust the queuePriority.
- if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
- for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) {
- if (queuePriorityProps[i].priorities[j] > queuePriority) {
- queuePriority = queuePriorityProps[i].priorities[j];
- }
- }
- if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) {
- interface.isRealtimePriority = true;
- }
- graphicsQueueIndex = i;
- break;
- }
- }
-
- if (graphicsQueueIndex == -1) {
- BAIL("Could not find a graphics queue family");
- }
-
- uint32_t deviceExtensionCount;
- VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
- nullptr));
- std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount);
- VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
- deviceExtensions.data()));
-
- std::vector<const char*> enabledDeviceExtensionNames;
- enabledDeviceExtensionNames.reserve(deviceExtensions.size());
- interface.deviceExtensionNames.reserve(deviceExtensions.size());
- for (const auto& devExt : deviceExtensions) {
- enabledDeviceExtensionNames.push_back(devExt.extensionName);
- interface.deviceExtensionNames.push_back(devExt.extensionName);
- }
-
- interface.grExtensions.init(sGetProc, instance, physicalDevice,
- enabledInstanceExtensionNames.size(),
- enabledInstanceExtensionNames.data(),
- enabledDeviceExtensionNames.size(),
- enabledDeviceExtensionNames.data());
-
- if (!interface.grExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
- BAIL("Vulkan driver doesn't support external semaphore fd");
- }
-
- interface.physicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2;
- interface.physicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
- interface.physicalDeviceFeatures2->pNext = nullptr;
-
- interface.samplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures;
- interface.samplerYcbcrConversionFeatures->sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
- interface.samplerYcbcrConversionFeatures->pNext = nullptr;
-
- interface.physicalDeviceFeatures2->pNext = interface.samplerYcbcrConversionFeatures;
- void** tailPnext = &interface.samplerYcbcrConversionFeatures->pNext;
-
- if (protectedContent) {
- interface.protectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures;
- interface.protectedMemoryFeatures->sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
- interface.protectedMemoryFeatures->pNext = nullptr;
- *tailPnext = interface.protectedMemoryFeatures;
- tailPnext = &interface.protectedMemoryFeatures->pNext;
- }
-
- if (interface.grExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
- interface.deviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT;
- interface.deviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
- interface.deviceFaultFeatures->pNext = nullptr;
- *tailPnext = interface.deviceFaultFeatures;
- tailPnext = &interface.deviceFaultFeatures->pNext;
- }
-
- vkGetPhysicalDeviceFeatures2(physicalDevice, interface.physicalDeviceFeatures2);
- // Looks like this would slow things down and we can't depend on it on all platforms
- interface.physicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE;
-
- if (protectedContent && !interface.protectedMemoryFeatures->protectedMemory) {
- BAIL("Protected memory not supported");
- }
-
- float queuePriorities[1] = {0.0f};
- void* queueNextPtr = nullptr;
-
- VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = {
- VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
- nullptr,
- // If queue priority is supported, RE should always have realtime priority.
- queuePriority,
- };
-
- if (interface.grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
- queueNextPtr = &queuePriorityCreateInfo;
- }
-
- VkDeviceQueueCreateFlags deviceQueueCreateFlags =
- (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0);
-
- const VkDeviceQueueCreateInfo queueInfo = {
- VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
- queueNextPtr,
- deviceQueueCreateFlags,
- (uint32_t)graphicsQueueIndex,
- 1,
- queuePriorities,
- };
-
- const VkDeviceCreateInfo deviceInfo = {
- VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
- interface.physicalDeviceFeatures2,
- 0,
- 1,
- &queueInfo,
- 0,
- nullptr,
- (uint32_t)enabledDeviceExtensionNames.size(),
- enabledDeviceExtensionNames.data(),
- nullptr,
- };
-
- ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent);
- VkDevice device;
- VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device));
- ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
-
- VkQueue graphicsQueue;
- VK_GET_DEV_PROC(device, GetDeviceQueue2);
- const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr,
- deviceQueueCreateFlags,
- (uint32_t)graphicsQueueIndex, 0};
- vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue);
-
- VK_GET_DEV_PROC(device, DeviceWaitIdle);
- VK_GET_DEV_PROC(device, DestroyDevice);
- interface.funcs.vkDeviceWaitIdle = vkDeviceWaitIdle;
- interface.funcs.vkDestroyDevice = vkDestroyDevice;
-
- VK_GET_DEV_PROC(device, CreateSemaphore);
- VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR);
- VK_GET_DEV_PROC(device, GetSemaphoreFdKHR);
- VK_GET_DEV_PROC(device, DestroySemaphore);
- interface.funcs.vkCreateSemaphore = vkCreateSemaphore;
- interface.funcs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR;
- interface.funcs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR;
- interface.funcs.vkDestroySemaphore = vkDestroySemaphore;
-
- // At this point, everything's succeeded and we can continue
- interface.initialized = true;
- interface.instance = instance;
- interface.physicalDevice = physicalDevice;
- interface.device = device;
- interface.queue = graphicsQueue;
- interface.queueIndex = graphicsQueueIndex;
- interface.apiVersion = physDevProps.properties.apiVersion;
- // grExtensions already constructed
- // feature pointers already constructed
- interface.grGetProc = sGetProc;
- interface.isProtected = protectedContent;
- // funcs already initialized
-
- const nsecs_t timeAfter = systemTime();
- const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
- ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs);
- return interface;
-}
-
-void teardownVulkanInterface(VulkanInterface* interface) {
- interface->initialized = false;
-
- if (interface->device != VK_NULL_HANDLE) {
- interface->funcs.vkDeviceWaitIdle(interface->device);
- interface->funcs.vkDestroyDevice(interface->device, nullptr);
- interface->device = VK_NULL_HANDLE;
- }
- if (interface->instance != VK_NULL_HANDLE) {
- interface->funcs.vkDestroyInstance(interface->instance, nullptr);
- interface->instance = VK_NULL_HANDLE;
- }
-
- if (interface->protectedMemoryFeatures) {
- delete interface->protectedMemoryFeatures;
- }
-
- if (interface->samplerYcbcrConversionFeatures) {
- delete interface->samplerYcbcrConversionFeatures;
- }
-
- if (interface->physicalDeviceFeatures2) {
- delete interface->physicalDeviceFeatures2;
- }
-
- if (interface->deviceFaultFeatures) {
- delete interface->deviceFaultFeatures;
- }
-
- interface->samplerYcbcrConversionFeatures = nullptr;
- interface->physicalDeviceFeatures2 = nullptr;
- interface->protectedMemoryFeatures = nullptr;
-}
-
-static VulkanInterface sVulkanInterface;
-static VulkanInterface sProtectedContentVulkanInterface;
+static skia::VulkanInterface sVulkanInterface;
+static skia::VulkanInterface sProtectedContentVulkanInterface;
static void sSetupVulkanInterface() {
- if (!sVulkanInterface.initialized) {
- sVulkanInterface = initVulkanInterface(false /* no protected content */);
+ if (!sVulkanInterface.isInitialized()) {
+ sVulkanInterface.init(false /* no protected content */);
// We will have to abort if non-protected VkDevice creation fails (then nothing works).
- LOG_ALWAYS_FATAL_IF(!sVulkanInterface.initialized,
+ LOG_ALWAYS_FATAL_IF(!sVulkanInterface.isInitialized(),
"Could not initialize Vulkan RenderEngine!");
}
- if (!sProtectedContentVulkanInterface.initialized) {
- sProtectedContentVulkanInterface = initVulkanInterface(true /* protected content */);
- if (!sProtectedContentVulkanInterface.initialized) {
+ if (!sProtectedContentVulkanInterface.isInitialized()) {
+ sProtectedContentVulkanInterface.init(true /* protected content */);
+ if (!sProtectedContentVulkanInterface.isInitialized()) {
ALOGE("Could not initialize protected content Vulkan RenderEngine.");
}
}
@@ -667,12 +69,38 @@
case GraphicsApi::GL:
return true;
case GraphicsApi::VK: {
- if (!sVulkanInterface.initialized) {
- sVulkanInterface = initVulkanInterface(false /* no protected content */);
- ALOGD("%s: initialized == %s.", __func__,
- sVulkanInterface.initialized ? "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.initialized;
+ if (sProtectedContentVulkanInterface.isInitialized()) {
+ sProtectedContentVulkanInterface.teardown();
+ ALOGD("Tearing down the protected VulkanInterface.");
+ }
+ break;
}
}
}
@@ -681,130 +109,61 @@
using base::StringAppendF;
-std::unique_ptr<SkiaVkRenderEngine> SkiaVkRenderEngine::create(
- const RenderEngineCreationArgs& args) {
- std::unique_ptr<SkiaVkRenderEngine> engine(new SkiaVkRenderEngine(args));
- engine->ensureGrContextsCreated();
-
- if (sVulkanInterface.initialized) {
- 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::createDirectContexts(
- const GrContextOptions& options) {
+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 = GrDirectContexts::MakeVulkan(sVulkanInterface.getBackendContext(), options);
+ contexts.first = createContext(sVulkanInterface);
if (supportsProtectedContentImpl()) {
- contexts.second =
- GrDirectContexts::MakeVulkan(sProtectedContentVulkanInterface.getBackendContext(),
- options);
+ contexts.second = createContext(sProtectedContentVulkanInterface);
}
return contexts;
}
bool SkiaVkRenderEngine::supportsProtectedContentImpl() const {
- return sProtectedContentVulkanInterface.initialized;
+ return sProtectedContentVulkanInterface.isInitialized();
}
bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) {
return true;
}
-static void delete_semaphore(void* semaphore) {
- DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore);
- --info->mRefs;
- if (!info->mRefs) {
- sVulkanInterface.destroySemaphore(info->mSemaphore);
- delete info;
- }
-}
-
-static void delete_semaphore_protected(void* semaphore) {
- DestroySemaphoreInfo* info = reinterpret_cast<DestroySemaphoreInfo*>(semaphore);
- --info->mRefs;
- if (!info->mRefs) {
- sProtectedContentVulkanInterface.destroySemaphore(info->mSemaphore);
- delete info;
- }
-}
-
-static VulkanInterface& getVulkanInterface(bool protectedContext) {
+VulkanInterface& SkiaVkRenderEngine::getVulkanInterface(bool protectedContext) {
if (protectedContext) {
return sProtectedContentVulkanInterface;
}
return sVulkanInterface;
}
-void SkiaVkRenderEngine::waitFence(GrDirectContext* grContext, 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());
- GrBackendSemaphore beSemaphore;
- beSemaphore.initVulkan(waitSemaphore);
- grContext->wait(1, &beSemaphore, true /* delete after wait */);
-}
-
-base::unique_fd SkiaVkRenderEngine::flushAndSubmit(GrDirectContext* grContext) {
- VulkanInterface& vi = getVulkanInterface(isProtected());
- VkSemaphore semaphore = vi.createExportableSemaphore();
-
- GrBackendSemaphore backendSemaphore;
- backendSemaphore.initVulkan(semaphore);
-
- GrFlushInfo flushInfo;
- DestroySemaphoreInfo* destroySemaphoreInfo = nullptr;
- if (semaphore != VK_NULL_HANDLE) {
- destroySemaphoreInfo = new DestroySemaphoreInfo(semaphore);
- flushInfo.fNumSemaphores = 1;
- flushInfo.fSignalSemaphores = &backendSemaphore;
- flushInfo.fFinishedProc = isProtected() ? delete_semaphore_protected : delete_semaphore;
- flushInfo.fFinishedContext = destroySemaphoreInfo;
- }
- GrSemaphoresSubmitted submitted = grContext->flush(flushInfo);
- grContext->submit(GrSyncCpu::kNo);
- int drawFenceFd = -1;
- if (semaphore != VK_NULL_HANDLE) {
- if (GrSemaphoresSubmitted::kYes == submitted) {
- drawFenceFd = vi.exportSemaphoreSyncFd(semaphore);
- }
- // Now that drawFenceFd has been created, we can delete our reference to this semaphore
- flushInfo.fFinishedProc(destroySemaphoreInfo);
- }
- base::unique_fd res(drawFenceFd);
- return res;
-}
-
int SkiaVkRenderEngine::getContextPriority() {
// EGL_CONTEXT_PRIORITY_REALTIME_NV
constexpr int kRealtimePriority = 0x3357;
- if (getVulkanInterface(isProtected()).isRealtimePriority) {
+ if (getVulkanInterface(isProtected()).isRealtimePriority()) {
return kRealtimePriority;
} else {
return 0;
@@ -813,21 +172,21 @@
void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
StringAppendF(&result, "\n ------------RE Vulkan----------\n");
- StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.initialized);
+ StringAppendF(&result, "\n Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
StringAppendF(&result, "\n Vulkan protected device initialized: %d\n",
- sProtectedContentVulkanInterface.initialized);
+ sProtectedContentVulkanInterface.isInitialized());
- if (!sVulkanInterface.initialized) {
+ if (!sVulkanInterface.isInitialized()) {
return;
}
StringAppendF(&result, "\n Instance extensions:\n");
- for (const auto& name : sVulkanInterface.instanceExtensionNames) {
+ for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) {
StringAppendF(&result, "\n %s\n", name.c_str());
}
StringAppendF(&result, "\n Device extensions:\n");
- for (const auto& name : sVulkanInterface.deviceExtensionNames) {
+ for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) {
StringAppendF(&result, "\n %s\n", name.c_str());
}
}
diff --git a/libs/renderengine/skia/SkiaVkRenderEngine.h b/libs/renderengine/skia/SkiaVkRenderEngine.h
index 52bc500..0a2f9b2 100644
--- a/libs/renderengine/skia/SkiaVkRenderEngine.h
+++ b/libs/renderengine/skia/SkiaVkRenderEngine.h
@@ -20,6 +20,8 @@
#include <vk/GrVkBackendContext.h>
#include "SkiaRenderEngine.h"
+#include "VulkanInterface.h"
+#include "compat/SkiaGpuContext.h"
namespace android {
namespace renderengine {
@@ -27,26 +29,64 @@
class SkiaVkRenderEngine : public SkiaRenderEngine {
public:
- static std::unique_ptr<SkiaVkRenderEngine> create(const RenderEngineCreationArgs& args);
~SkiaVkRenderEngine() override;
int getContextPriority() override;
+ class DestroySemaphoreInfo {
+ public:
+ DestroySemaphoreInfo() = delete;
+ DestroySemaphoreInfo(const DestroySemaphoreInfo&) = delete;
+ DestroySemaphoreInfo& operator=(const DestroySemaphoreInfo&) = delete;
+ DestroySemaphoreInfo& operator=(DestroySemaphoreInfo&&) = delete;
+
+ DestroySemaphoreInfo(VulkanInterface& vulkanInterface, std::vector<VkSemaphore> semaphores)
+ : mVulkanInterface(vulkanInterface), mSemaphores(std::move(semaphores)) {}
+ DestroySemaphoreInfo(VulkanInterface& vulkanInterface, VkSemaphore semaphore)
+ : DestroySemaphoreInfo(vulkanInterface, std::vector<VkSemaphore>(1, semaphore)) {}
+
+ void unref() {
+ --mRefs;
+ if (!mRefs) {
+ for (VkSemaphore semaphore : mSemaphores) {
+ mVulkanInterface.destroySemaphore(semaphore);
+ }
+ delete this;
+ }
+ }
+
+ private:
+ ~DestroySemaphoreInfo() = default;
+
+ VulkanInterface& mVulkanInterface;
+ std::vector<VkSemaphore> mSemaphores;
+ // We need to make sure we don't delete the VkSemaphore until it is done being used by both
+ // Skia (including by the GPU) and inside SkiaVkRenderEngine. So we always start with two
+ // refs, one owned by Skia and one owned by the SkiaVkRenderEngine. The refs are decremented
+ // each time unref() is called on this object. Skia will call unref() once it is done with
+ // the semaphore and the GPU has finished work on the semaphore. SkiaVkRenderEngine calls
+ // unref() after sending the semaphore to Skia and exporting it if need be.
+ int mRefs = 2;
+ };
+
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,
+ sk_sp<SkSurface> dstSurface) override = 0;
+
+ SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
+
// Implementations of abstract SkiaRenderEngine functions specific to
- // rendering backend
- virtual SkiaRenderEngine::Contexts createDirectContexts(const GrContextOptions& options);
+ // Vulkan, but shareable between Ganesh and Graphite.
+ SkiaRenderEngine::Contexts createContexts() override;
bool supportsProtectedContentImpl() const override;
bool useProtectedContextImpl(GrProtected isProtected) override;
- void waitFence(GrDirectContext* grContext, base::borrowed_fd fenceFd) override;
- base::unique_fd flushAndSubmit(GrDirectContext* context) override;
void appendBackendSpecificInfoToDump(std::string& result) override;
-private:
- SkiaVkRenderEngine(const RenderEngineCreationArgs& args);
- base::unique_fd flush();
-
- GrVkBackendContext mBackendContext;
+ // TODO: b/300533018 - refactor this to be non-static
+ static VulkanInterface& getVulkanInterface(bool protectedContext);
};
} // namespace skia
diff --git a/libs/renderengine/skia/VulkanInterface.cpp b/libs/renderengine/skia/VulkanInterface.cpp
new file mode 100644
index 0000000..5e756b0
--- /dev/null
+++ b/libs/renderengine/skia/VulkanInterface.cpp
@@ -0,0 +1,622 @@
+/*
+ * 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 "RenderEngine"
+
+#include "VulkanInterface.h"
+
+#include <include/gpu/GpuTypes.h>
+#include <include/gpu/vk/VulkanBackendContext.h>
+
+#include <log/log_main.h>
+#include <utils/Timers.h>
+
+#include <cinttypes>
+#include <sstream>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+GrVkBackendContext VulkanInterface::getGaneshBackendContext() {
+ GrVkBackendContext 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;
+};
+
+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;
+ exportInfo.pNext = nullptr;
+ exportInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = &exportInfo;
+ semaphoreInfo.flags = 0;
+
+ VkSemaphore semaphore;
+ VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to create semaphore. err %d\n", __func__, err);
+ return VK_NULL_HANDLE;
+ }
+
+ return semaphore;
+}
+
+// syncFd cannot be <= 0
+VkSemaphore VulkanInterface::importSemaphoreFromSyncFd(int syncFd) {
+ VkSemaphoreCreateInfo semaphoreInfo;
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+
+ VkSemaphore semaphore;
+ VkResult err = mFuncs.vkCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to create import semaphore", __func__);
+ return VK_NULL_HANDLE;
+ }
+
+ VkImportSemaphoreFdInfoKHR importInfo;
+ importInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR;
+ importInfo.pNext = nullptr;
+ importInfo.semaphore = semaphore;
+ importInfo.flags = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT;
+ importInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ importInfo.fd = syncFd;
+
+ err = mFuncs.vkImportSemaphoreFdKHR(mDevice, &importInfo);
+ if (VK_SUCCESS != err) {
+ mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr);
+ ALOGE("%s: failed to import semaphore", __func__);
+ return VK_NULL_HANDLE;
+ }
+
+ return semaphore;
+}
+
+int VulkanInterface::exportSemaphoreSyncFd(VkSemaphore semaphore) {
+ int res;
+
+ VkSemaphoreGetFdInfoKHR getFdInfo;
+ getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR;
+ getFdInfo.pNext = nullptr;
+ getFdInfo.semaphore = semaphore;
+ getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT;
+ VkResult err = mFuncs.vkGetSemaphoreFdKHR(mDevice, &getFdInfo, &res);
+ if (VK_SUCCESS != err) {
+ ALOGE("%s: failed to export semaphore, err: %d", __func__, err);
+ return -1;
+ }
+ return res;
+}
+
+void VulkanInterface::destroySemaphore(VkSemaphore semaphore) {
+ mFuncs.vkDestroySemaphore(mDevice, semaphore, nullptr);
+}
+
+void VulkanInterface::onVkDeviceFault(void* callbackContext, const std::string& description,
+ const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+ const std::vector<VkDeviceFaultVendorInfoEXT>& vendorInfos,
+ const std::vector<std::byte>& vendorBinaryData) {
+ VulkanInterface* interface = static_cast<VulkanInterface*>(callbackContext);
+ const std::string protectedStr = interface->mIsProtected ? "protected" : "non-protected";
+ // The final crash string should contain as much differentiating info as possible, up to 1024
+ // bytes. As this final message is constructed, the same information is also dumped to the logs
+ // but in a more verbose format. Building the crash string is unsightly, so the clearer logging
+ // statement is always placed first to give context.
+ ALOGE("VK_ERROR_DEVICE_LOST (%s context): %s", protectedStr.c_str(), description.c_str());
+ std::stringstream crashMsg;
+ crashMsg << "VK_ERROR_DEVICE_LOST (" << protectedStr;
+
+ if (!addressInfos.empty()) {
+ ALOGE("%zu VkDeviceFaultAddressInfoEXT:", addressInfos.size());
+ crashMsg << ", " << addressInfos.size() << " address info (";
+ for (VkDeviceFaultAddressInfoEXT addressInfo : addressInfos) {
+ ALOGE(" addressType: %d", (int)addressInfo.addressType);
+ ALOGE(" reportedAddress: %" PRIu64, addressInfo.reportedAddress);
+ ALOGE(" addressPrecision: %" PRIu64, addressInfo.addressPrecision);
+ crashMsg << addressInfo.addressType << ":" << addressInfo.reportedAddress << ":"
+ << addressInfo.addressPrecision << ", ";
+ }
+ crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
+ crashMsg << ")";
+ }
+
+ if (!vendorInfos.empty()) {
+ ALOGE("%zu VkDeviceFaultVendorInfoEXT:", vendorInfos.size());
+ crashMsg << ", " << vendorInfos.size() << " vendor info (";
+ for (VkDeviceFaultVendorInfoEXT vendorInfo : vendorInfos) {
+ ALOGE(" description: %s", vendorInfo.description);
+ ALOGE(" vendorFaultCode: %" PRIu64, vendorInfo.vendorFaultCode);
+ ALOGE(" vendorFaultData: %" PRIu64, vendorInfo.vendorFaultData);
+ // Omit descriptions for individual vendor info structs in the crash string, as the
+ // fault code and fault data fields should be enough for clustering, and the verbosity
+ // isn't worth it. Additionally, vendors may just set the general description field of
+ // the overall fault to the description of the first element in this list, and that
+ // overall description will be placed at the end of the crash string.
+ crashMsg << vendorInfo.vendorFaultCode << ":" << vendorInfo.vendorFaultData << ", ";
+ }
+ crashMsg.seekp(-2, crashMsg.cur); // Move back to overwrite trailing ", "
+ crashMsg << ")";
+ }
+
+ if (!vendorBinaryData.empty()) {
+ // TODO: b/322830575 - Log in base64, or dump directly to a file that gets put in bugreports
+ ALOGE("%zu bytes of vendor-specific binary data (please notify Android's Core Graphics"
+ " Stack team if you observe this message).",
+ vendorBinaryData.size());
+ crashMsg << ", " << vendorBinaryData.size() << " bytes binary";
+ }
+
+ crashMsg << "): " << description;
+ LOG_ALWAYS_FATAL("%s", crashMsg.str().c_str());
+};
+
+static skgpu::VulkanGetProc sGetProc = [](const char* proc_name,
+ VkInstance instance,
+ VkDevice device) {
+ if (device != VK_NULL_HANDLE) {
+ return vkGetDeviceProcAddr(device, proc_name);
+ }
+ return vkGetInstanceProcAddr(instance, proc_name);
+};
+
+#define BAIL(fmt, ...) \
+ { \
+ ALOGE("%s: " fmt ", bailing", __func__, ##__VA_ARGS__); \
+ return; \
+ }
+
+#define CHECK_NONNULL(expr) \
+ if ((expr) == nullptr) { \
+ BAIL("[%s] null", #expr); \
+ }
+
+#define VK_CHECK(expr) \
+ if ((expr) != VK_SUCCESS) { \
+ BAIL("[%s] failed. err = %d", #expr, expr); \
+ return; \
+ }
+
+#define VK_GET_PROC(F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(VK_NULL_HANDLE, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+#define VK_GET_INST_PROC(instance, F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetInstanceProcAddr(instance, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+#define VK_GET_DEV_PROC(device, F) \
+ PFN_vk##F vk##F = (PFN_vk##F)vkGetDeviceProcAddr(device, "vk" #F); \
+ CHECK_NONNULL(vk##F)
+
+void VulkanInterface::init(bool protectedContent) {
+ if (isInitialized()) {
+ ALOGW("Called init on already initialized VulkanInterface");
+ return;
+ }
+
+ const nsecs_t timeBefore = systemTime();
+
+ VK_GET_PROC(EnumerateInstanceVersion);
+ uint32_t instanceVersion;
+ VK_CHECK(vkEnumerateInstanceVersion(&instanceVersion));
+
+ if (instanceVersion < VK_MAKE_VERSION(1, 1, 0)) {
+ 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 = {
+ VK_STRUCTURE_TYPE_APPLICATION_INFO, nullptr, "surfaceflinger", 0, "android platform", 0,
+ VK_MAKE_VERSION(1, 1, 0),
+ };
+
+ VK_GET_PROC(EnumerateInstanceExtensionProperties);
+
+ uint32_t extensionCount = 0;
+ VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr));
+ std::vector<VkExtensionProperties> instanceExtensions(extensionCount);
+ VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount,
+ instanceExtensions.data()));
+ std::vector<const char*> enabledInstanceExtensionNames;
+ enabledInstanceExtensionNames.reserve(instanceExtensions.size());
+ mInstanceExtensionNames.reserve(instanceExtensions.size());
+ for (const auto& instExt : instanceExtensions) {
+ enabledInstanceExtensionNames.push_back(instExt.extensionName);
+ mInstanceExtensionNames.push_back(instExt.extensionName);
+ }
+
+ const VkInstanceCreateInfo instanceCreateInfo = {
+ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ nullptr,
+ 0,
+ &appInfo,
+ 0,
+ nullptr,
+ (uint32_t)enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(),
+ };
+
+ VK_GET_PROC(CreateInstance);
+ VkInstance instance;
+ VK_CHECK(vkCreateInstance(&instanceCreateInfo, nullptr, &instance));
+
+ VK_GET_INST_PROC(instance, DestroyInstance);
+ mFuncs.vkDestroyInstance = vkDestroyInstance;
+ VK_GET_INST_PROC(instance, EnumeratePhysicalDevices);
+ VK_GET_INST_PROC(instance, EnumerateDeviceExtensionProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceProperties2);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceExternalSemaphoreProperties);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceQueueFamilyProperties2);
+ VK_GET_INST_PROC(instance, GetPhysicalDeviceFeatures2);
+ VK_GET_INST_PROC(instance, CreateDevice);
+
+ uint32_t physdevCount;
+ VK_CHECK(vkEnumeratePhysicalDevices(instance, &physdevCount, nullptr));
+ if (physdevCount == 0) {
+ BAIL("Could not find any physical devices");
+ }
+
+ physdevCount = 1;
+ VkPhysicalDevice physicalDevice;
+ VkResult enumeratePhysDevsErr =
+ vkEnumeratePhysicalDevices(instance, &physdevCount, &physicalDevice);
+ if (enumeratePhysDevsErr != VK_SUCCESS && VK_INCOMPLETE != enumeratePhysDevsErr) {
+ BAIL("vkEnumeratePhysicalDevices failed with non-VK_INCOMPLETE error: %d",
+ enumeratePhysDevsErr);
+ }
+
+ VkPhysicalDeviceProperties2 physDevProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
+ 0,
+ {},
+ };
+ VkPhysicalDeviceProtectedMemoryProperties protMemProps = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
+ 0,
+ {},
+ };
+
+ if (protectedContent) {
+ physDevProps.pNext = &protMemProps;
+ }
+
+ vkGetPhysicalDeviceProperties2(physicalDevice, &physDevProps);
+ 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.
+ VkPhysicalDeviceExternalSemaphoreInfo semInfo = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO,
+ nullptr,
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ };
+ VkExternalSemaphoreProperties semProps = {
+ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, nullptr, 0, 0, 0,
+ };
+ vkGetPhysicalDeviceExternalSemaphoreProperties(physicalDevice, &semInfo, &semProps);
+
+ bool sufficientSemaphoreSyncFdSupport = (semProps.exportFromImportedHandleTypes &
+ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+ (semProps.compatibleHandleTypes & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT) &&
+ (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) &&
+ (semProps.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+
+ if (!sufficientSemaphoreSyncFdSupport) {
+ BAIL("Vulkan device does not support sufficient external semaphore sync fd features. "
+ "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+ "compatibleHandleTypes 0x%x (needed 0x%x) "
+ "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+ semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.externalSemaphoreFeatures,
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+ } else {
+ ALOGD("Vulkan device supports sufficient external semaphore sync fd features. "
+ "exportFromImportedHandleTypes 0x%x (needed 0x%x) "
+ "compatibleHandleTypes 0x%x (needed 0x%x) "
+ "externalSemaphoreFeatures 0x%x (needed 0x%x) ",
+ semProps.exportFromImportedHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.compatibleHandleTypes, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
+ semProps.externalSemaphoreFeatures,
+ VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT |
+ VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT);
+ }
+
+ uint32_t queueCount;
+ vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, nullptr);
+ if (queueCount == 0) {
+ BAIL("Could not find queues for physical device");
+ }
+
+ std::vector<VkQueueFamilyProperties2> queueProps(queueCount);
+ std::vector<VkQueueFamilyGlobalPriorityPropertiesEXT> queuePriorityProps(queueCount);
+ VkQueueGlobalPriorityKHR queuePriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_KHR;
+ // Even though we don't yet know if the VK_EXT_global_priority extension is available,
+ // we can safely add the request to the pNext chain, and if the extension is not
+ // available, it will be ignored.
+ for (uint32_t i = 0; i < queueCount; ++i) {
+ queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT;
+ queuePriorityProps[i].pNext = nullptr;
+ queueProps[i].pNext = &queuePriorityProps[i];
+ }
+ vkGetPhysicalDeviceQueueFamilyProperties2(physicalDevice, &queueCount, queueProps.data());
+
+ int graphicsQueueIndex = -1;
+ for (uint32_t i = 0; i < queueCount; ++i) {
+ // Look at potential answers to the VK_EXT_global_priority query. If answers were
+ // provided, we may adjust the queuePriority.
+ if (queueProps[i].queueFamilyProperties.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
+ for (uint32_t j = 0; j < queuePriorityProps[i].priorityCount; j++) {
+ if (queuePriorityProps[i].priorities[j] > queuePriority) {
+ queuePriority = queuePriorityProps[i].priorities[j];
+ }
+ }
+ if (queuePriority == VK_QUEUE_GLOBAL_PRIORITY_REALTIME_KHR) {
+ mIsRealtimePriority = true;
+ }
+ graphicsQueueIndex = i;
+ break;
+ }
+ }
+
+ if (graphicsQueueIndex == -1) {
+ BAIL("Could not find a graphics queue family");
+ }
+
+ uint32_t deviceExtensionCount;
+ VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+ nullptr));
+ std::vector<VkExtensionProperties> deviceExtensions(deviceExtensionCount);
+ VK_CHECK(vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &deviceExtensionCount,
+ deviceExtensions.data()));
+
+ std::vector<const char*> enabledDeviceExtensionNames;
+ enabledDeviceExtensionNames.reserve(deviceExtensions.size());
+ mDeviceExtensionNames.reserve(deviceExtensions.size());
+ for (const auto& devExt : deviceExtensions) {
+ enabledDeviceExtensionNames.push_back(devExt.extensionName);
+ mDeviceExtensionNames.push_back(devExt.extensionName);
+ }
+
+ mGrExtensions.init(sGetProc, instance, physicalDevice, enabledInstanceExtensionNames.size(),
+ enabledInstanceExtensionNames.data(), enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data());
+
+ if (!mGrExtensions.hasExtension(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, 1)) {
+ BAIL("Vulkan driver doesn't support external semaphore fd");
+ }
+
+ mPhysicalDeviceFeatures2 = new VkPhysicalDeviceFeatures2;
+ mPhysicalDeviceFeatures2->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+ mPhysicalDeviceFeatures2->pNext = nullptr;
+
+ mSamplerYcbcrConversionFeatures = new VkPhysicalDeviceSamplerYcbcrConversionFeatures;
+ mSamplerYcbcrConversionFeatures->sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
+ mSamplerYcbcrConversionFeatures->pNext = nullptr;
+
+ mPhysicalDeviceFeatures2->pNext = mSamplerYcbcrConversionFeatures;
+ void** tailPnext = &mSamplerYcbcrConversionFeatures->pNext;
+
+ if (protectedContent) {
+ mProtectedMemoryFeatures = new VkPhysicalDeviceProtectedMemoryFeatures;
+ mProtectedMemoryFeatures->sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES;
+ mProtectedMemoryFeatures->pNext = nullptr;
+ *tailPnext = mProtectedMemoryFeatures;
+ tailPnext = &mProtectedMemoryFeatures->pNext;
+ }
+
+ if (mGrExtensions.hasExtension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, 1)) {
+ mDeviceFaultFeatures = new VkPhysicalDeviceFaultFeaturesEXT;
+ mDeviceFaultFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
+ mDeviceFaultFeatures->pNext = nullptr;
+ *tailPnext = mDeviceFaultFeatures;
+ tailPnext = &mDeviceFaultFeatures->pNext;
+ }
+
+ vkGetPhysicalDeviceFeatures2(physicalDevice, mPhysicalDeviceFeatures2);
+ // Looks like this would slow things down and we can't depend on it on all platforms
+ mPhysicalDeviceFeatures2->features.robustBufferAccess = VK_FALSE;
+
+ if (protectedContent && !mProtectedMemoryFeatures->protectedMemory) {
+ BAIL("Protected memory not supported");
+ }
+
+ float queuePriorities[1] = {0.0f};
+ void* queueNextPtr = nullptr;
+
+ VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT,
+ nullptr,
+ // If queue priority is supported, RE should always have realtime priority.
+ queuePriority,
+ };
+
+ if (mGrExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) {
+ queueNextPtr = &queuePriorityCreateInfo;
+ }
+
+ VkDeviceQueueCreateFlags deviceQueueCreateFlags =
+ (VkDeviceQueueCreateFlags)(protectedContent ? VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT : 0);
+
+ const VkDeviceQueueCreateInfo queueInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ queueNextPtr,
+ deviceQueueCreateFlags,
+ (uint32_t)graphicsQueueIndex,
+ 1,
+ queuePriorities,
+ };
+
+ const VkDeviceCreateInfo deviceInfo = {
+ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ mPhysicalDeviceFeatures2,
+ 0,
+ 1,
+ &queueInfo,
+ 0,
+ nullptr,
+ (uint32_t)enabledDeviceExtensionNames.size(),
+ enabledDeviceExtensionNames.data(),
+ nullptr,
+ };
+
+ ALOGD("Trying to create Vk device with protectedContent=%d", protectedContent);
+ VkDevice device;
+ VK_CHECK(vkCreateDevice(physicalDevice, &deviceInfo, nullptr, &device));
+ ALOGD("Trying to create Vk device with protectedContent=%d (success)", protectedContent);
+
+ VkQueue graphicsQueue;
+ VK_GET_DEV_PROC(device, GetDeviceQueue2);
+ const VkDeviceQueueInfo2 deviceQueueInfo2 = {VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2, nullptr,
+ deviceQueueCreateFlags,
+ (uint32_t)graphicsQueueIndex, 0};
+ vkGetDeviceQueue2(device, &deviceQueueInfo2, &graphicsQueue);
+
+ VK_GET_DEV_PROC(device, DeviceWaitIdle);
+ VK_GET_DEV_PROC(device, DestroyDevice);
+ mFuncs.vkDeviceWaitIdle = vkDeviceWaitIdle;
+ mFuncs.vkDestroyDevice = vkDestroyDevice;
+
+ VK_GET_DEV_PROC(device, CreateSemaphore);
+ VK_GET_DEV_PROC(device, ImportSemaphoreFdKHR);
+ VK_GET_DEV_PROC(device, GetSemaphoreFdKHR);
+ VK_GET_DEV_PROC(device, DestroySemaphore);
+ mFuncs.vkCreateSemaphore = vkCreateSemaphore;
+ mFuncs.vkImportSemaphoreFdKHR = vkImportSemaphoreFdKHR;
+ mFuncs.vkGetSemaphoreFdKHR = vkGetSemaphoreFdKHR;
+ mFuncs.vkDestroySemaphore = vkDestroySemaphore;
+
+ // At this point, everything's succeeded and we can continue
+ mInitialized = true;
+ mInstance = instance;
+ mPhysicalDevice = physicalDevice;
+ mDevice = device;
+ mQueue = graphicsQueue;
+ mQueueIndex = graphicsQueueIndex;
+ mApiVersion = physicalDeviceApiVersion;
+ // grExtensions already constructed
+ // feature pointers already constructed
+ mGrGetProc = sGetProc;
+ mIsProtected = protectedContent;
+ // mIsRealtimePriority already initialized by constructor
+ // funcs already initialized
+
+ const nsecs_t timeAfter = systemTime();
+ const float initTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("%s: Success init Vulkan interface in %f ms", __func__, initTimeMs);
+}
+
+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);
+ mDevice = VK_NULL_HANDLE;
+ }
+ if (mInstance != VK_NULL_HANDLE) {
+ mFuncs.vkDestroyInstance(mInstance, nullptr);
+ 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;
+ }
+
+ // 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 = skgpu::VulkanExtensions();
+ mGrGetProc = nullptr;
+ mIsProtected = false;
+ mIsRealtimePriority = false;
+
+ mFuncs = VulkanFuncs();
+
+ mInstanceExtensionNames.clear();
+ mDeviceExtensionNames.clear();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/VulkanInterface.h b/libs/renderengine/skia/VulkanInterface.h
new file mode 100644
index 0000000..f20b002
--- /dev/null
+++ b/libs/renderengine/skia/VulkanInterface.h
@@ -0,0 +1,106 @@
+/*
+ * 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 <include/gpu/vk/GrVkBackendContext.h>
+#include <include/gpu/vk/VulkanExtensions.h>
+#include <include/gpu/vk/VulkanTypes.h>
+
+#include <vulkan/vulkan.h>
+
+using namespace skgpu;
+
+namespace skgpu {
+struct VulkanBackendContext;
+} // namespace skgpu
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+class VulkanInterface {
+public:
+ // Create an uninitialized interface. Initialize with `init`.
+ VulkanInterface() = default;
+ ~VulkanInterface() = default;
+ VulkanInterface(const VulkanInterface&) = delete;
+ VulkanInterface& operator=(const VulkanInterface&) = delete;
+ 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();
+
+ GrVkBackendContext getGaneshBackendContext();
+ VulkanBackendContext getGraphiteBackendContext();
+ VkSemaphore createExportableSemaphore();
+ VkSemaphore importSemaphoreFromSyncFd(int syncFd);
+ int exportSemaphoreSyncFd(VkSemaphore semaphore);
+ void destroySemaphore(VkSemaphore semaphore);
+
+ bool isInitialized() const { return mInitialized; }
+ bool isRealtimePriority() const { return mIsRealtimePriority; }
+ const std::vector<std::string>& getInstanceExtensionNames() { return mInstanceExtensionNames; }
+ const std::vector<std::string>& getDeviceExtensionNames() { return mDeviceExtensionNames; }
+
+private:
+ struct VulkanFuncs {
+ PFN_vkCreateSemaphore vkCreateSemaphore = nullptr;
+ PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR = nullptr;
+ PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR = nullptr;
+ PFN_vkDestroySemaphore vkDestroySemaphore = nullptr;
+
+ PFN_vkDeviceWaitIdle vkDeviceWaitIdle = nullptr;
+ PFN_vkDestroyDevice vkDestroyDevice = nullptr;
+ PFN_vkDestroyInstance vkDestroyInstance = nullptr;
+ };
+
+ static void onVkDeviceFault(void* callbackContext, const std::string& description,
+ const std::vector<VkDeviceFaultAddressInfoEXT>& addressInfos,
+ 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;
+ VkQueue mQueue = VK_NULL_HANDLE;
+ int mQueueIndex = 0;
+ uint32_t mApiVersion = 0;
+ skgpu::VulkanExtensions mGrExtensions;
+ VkPhysicalDeviceFeatures2* mPhysicalDeviceFeatures2 = nullptr;
+ VkPhysicalDeviceSamplerYcbcrConversionFeatures* mSamplerYcbcrConversionFeatures = nullptr;
+ VkPhysicalDeviceProtectedMemoryFeatures* mProtectedMemoryFeatures = nullptr;
+ VkPhysicalDeviceFaultFeaturesEXT* mDeviceFaultFeatures = nullptr;
+ skgpu::VulkanGetProc mGrGetProc = nullptr;
+ bool mIsProtected = false;
+ bool mIsRealtimePriority = false;
+
+ VulkanFuncs mFuncs;
+
+ std::vector<std::string> mInstanceExtensionNames;
+ std::vector<std::string> mDeviceExtensionNames;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.cpp b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
new file mode 100644
index 0000000..d246466
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "GaneshBackendTexture.h"
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <include/core/SkImage.h>
+#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/ganesh/SkImageGanesh.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
+#include <include/gpu/ganesh/vk/GrVkBackendSurface.h>
+#include <include/gpu/vk/GrVkTypes.h>
+
+#include "skia/ColorSpaces.h"
+#include "skia/compat/SkiaBackendTexture.h"
+
+#include <android/hardware_buffer.h>
+#include <log/log_main.h>
+#include <utils/Trace.h>
+
+namespace android::renderengine::skia {
+
+GaneshBackendTexture::GaneshBackendTexture(sk_sp<GrDirectContext> grContext,
+ AHardwareBuffer* buffer, bool isOutputBuffer)
+ : SkiaBackendTexture(buffer, isOutputBuffer), mGrContext(grContext) {
+ ATRACE_CALL();
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+ const bool createProtectedImage = 0 != (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT);
+
+ GrBackendFormat backendFormat;
+ const GrBackendApi graphicsApi = grContext->backend();
+ if (graphicsApi == GrBackendApi::kOpenGL) {
+ backendFormat =
+ GrAHardwareBufferUtils::GetGLBackendFormat(grContext.get(), desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeGLBackendTexture(grContext.get(), buffer, desc.width,
+ desc.height, &mDeleteProc,
+ &mUpdateProc, &mImageCtx,
+ createProtectedImage, backendFormat,
+ isOutputBuffer);
+ } else if (graphicsApi == GrBackendApi::kVulkan) {
+ backendFormat = GrAHardwareBufferUtils::GetVulkanBackendFormat(grContext.get(), buffer,
+ desc.format, false);
+ mBackendTexture =
+ GrAHardwareBufferUtils::MakeVulkanBackendTexture(grContext.get(), buffer,
+ desc.width, desc.height,
+ &mDeleteProc, &mUpdateProc,
+ &mImageCtx, createProtectedImage,
+ backendFormat, isOutputBuffer);
+ } else {
+ LOG_ALWAYS_FATAL("Unexpected graphics API %u", static_cast<unsigned>(graphicsApi));
+ }
+
+ if (!mBackendTexture.isValid() || !desc.width || !desc.height) {
+ LOG_ALWAYS_FATAL("Failed to create a valid texture. [%p]:[%d,%d] isProtected:%d "
+ "isWriteable:%d format:%d",
+ this, desc.width, desc.height, createProtectedImage, isOutputBuffer,
+ desc.format);
+ }
+}
+
+GaneshBackendTexture::~GaneshBackendTexture() {
+ if (mBackendTexture.isValid()) {
+ mDeleteProc(mImageCtx);
+ mBackendTexture = {};
+ }
+}
+
+sk_sp<SkImage> GaneshBackendTexture::makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) {
+ if (mBackendTexture.isValid()) {
+ mUpdateProc(mImageCtx, mGrContext.get());
+ }
+
+ const SkColorType colorType = colorTypeForImage(alphaType);
+ sk_sp<SkImage> image =
+ SkImages::BorrowTextureFrom(mGrContext.get(), mBackendTexture, kTopLeft_GrSurfaceOrigin,
+ colorType, alphaType, toSkColorSpace(dataspace),
+ releaseImageProc, releaseContext);
+ if (!image) {
+ logFatalTexture("Unable to generate SkImage.", dataspace, colorType);
+ }
+ return image;
+}
+
+sk_sp<SkSurface> GaneshBackendTexture::makeSurface(ui::Dataspace dataspace,
+ TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) {
+ const SkColorType colorType = internalColorType();
+ sk_sp<SkSurface> surface =
+ SkSurfaces::WrapBackendTexture(mGrContext.get(), mBackendTexture,
+ kTopLeft_GrSurfaceOrigin, 0, colorType,
+ toSkColorSpace(dataspace), nullptr, releaseSurfaceProc,
+ releaseContext);
+ if (!surface) {
+ logFatalTexture("Unable to generate SkSurface.", dataspace, colorType);
+ }
+ return surface;
+}
+
+void GaneshBackendTexture::logFatalTexture(const char* msg, ui::Dataspace dataspace,
+ SkColorType colorType) {
+ switch (mBackendTexture.backend()) {
+ case GrBackendApi::kOpenGL: {
+ GrGLTextureInfo textureInfo;
+ bool retrievedTextureInfo =
+ GrBackendTextures::GetGLTextureInfo(mBackendTexture, &textureInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tGrGLTextureInfo: success: %i fTarget: %u fFormat: %u"
+ " colorType %i",
+ msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace),
+ mBackendTexture.width(), mBackendTexture.height(),
+ mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(),
+ static_cast<int>(mBackendTexture.textureType()), retrievedTextureInfo,
+ textureInfo.fTarget, textureInfo.fFormat, colorType);
+ break;
+ }
+ case GrBackendApi::kVulkan: {
+ GrVkImageInfo imageInfo;
+ bool retrievedImageInfo =
+ GrBackendTextures::GetVkImageInfo(mBackendTexture, &imageInfo);
+ LOG_ALWAYS_FATAL("%s isTextureValid:%d dataspace:%d"
+ "\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
+ "texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
+ "fSampleCount: %u fLevelCount: %u colorType %i",
+ msg, mBackendTexture.isValid(), static_cast<int32_t>(dataspace),
+ mBackendTexture.width(), mBackendTexture.height(),
+ mBackendTexture.hasMipmaps(), mBackendTexture.isProtected(),
+ static_cast<int>(mBackendTexture.textureType()), retrievedImageInfo,
+ imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
+ colorType);
+ break;
+ }
+ default:
+ LOG_ALWAYS_FATAL("%s Unexpected backend %u", msg,
+ static_cast<unsigned>(mBackendTexture.backend()));
+ break;
+ }
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GaneshBackendTexture.h b/libs/renderengine/skia/compat/GaneshBackendTexture.h
new file mode 100644
index 0000000..5cf8647
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshBackendTexture.h
@@ -0,0 +1,57 @@
+/*
+ * 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 "ui/GraphicTypes.h"
+
+#include <include/android/GrAHardwareBufferUtils.h>
+#include <include/core/SkColorSpace.h>
+#include <include/gpu/GrDirectContext.h>
+
+#include <android-base/macros.h>
+
+namespace android::renderengine::skia {
+
+class GaneshBackendTexture : public SkiaBackendTexture {
+public:
+ // Creates an internal GrBackendTexture whose contents come from the provided buffer.
+ GaneshBackendTexture(sk_sp<GrDirectContext> grContext, AHardwareBuffer* buffer,
+ bool isOutputBuffer);
+
+ ~GaneshBackendTexture() 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(GaneshBackendTexture);
+
+ void logFatalTexture(const char* msg, ui::Dataspace dataspace, SkColorType colorType);
+
+ const sk_sp<GrDirectContext> mGrContext;
+ GrBackendTexture mBackendTexture;
+ GrAHardwareBufferUtils::DeleteImageProc mDeleteProc;
+ GrAHardwareBufferUtils::UpdateImageProc mUpdateProc;
+ GrAHardwareBufferUtils::TexImageCtx mImageCtx;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.cpp b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
new file mode 100644
index 0000000..b2eae00
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "GaneshGpuContext.h"
+
+#include <include/core/SkImageInfo.h>
+#include <include/core/SkSurface.h>
+#include <include/core/SkTraceMemoryDump.h>
+#include <include/gpu/GrDirectContext.h>
+#include <include/gpu/GrTypes.h>
+#include <include/gpu/ganesh/SkSurfaceGanesh.h>
+#include <include/gpu/ganesh/gl/GrGLDirectContext.h>
+#include <include/gpu/ganesh/vk/GrVkDirectContext.h>
+#include <include/gpu/gl/GrGLInterface.h>
+#include <include/gpu/vk/GrVkBackendContext.h>
+
+#include "../AutoBackendTexture.h"
+#include "GaneshBackendTexture.h"
+#include "skia/compat/SkiaBackendTexture.h"
+
+#include <android-base/macros.h>
+#include <log/log_main.h>
+#include <memory>
+
+namespace android::renderengine::skia {
+
+namespace {
+static GrContextOptions ganeshOptions(GrContextOptions::PersistentCache& skSLCacheMonitor) {
+ GrContextOptions options;
+ options.fDisableDriverCorrectnessWorkarounds = true;
+ options.fDisableDistanceFieldPaths = true;
+ options.fReducedShaderVariations = true;
+ options.fPersistentCache = &skSLCacheMonitor;
+ return options;
+}
+} // namespace
+
+std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeGL_Ganesh(
+ sk_sp<const GrGLInterface> glInterface,
+ GrContextOptions::PersistentCache& skSLCacheMonitor) {
+ return std::make_unique<GaneshGpuContext>(
+ GrDirectContexts::MakeGL(glInterface, ganeshOptions(skSLCacheMonitor)));
+}
+
+std::unique_ptr<SkiaGpuContext> SkiaGpuContext::MakeVulkan_Ganesh(
+ const GrVkBackendContext& grVkBackendContext,
+ GrContextOptions::PersistentCache& skSLCacheMonitor) {
+ return std::make_unique<GaneshGpuContext>(
+ GrDirectContexts::MakeVulkan(grVkBackendContext, ganeshOptions(skSLCacheMonitor)));
+}
+
+GaneshGpuContext::GaneshGpuContext(sk_sp<GrDirectContext> grContext) : mGrContext(grContext) {
+ 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;
+}
+
+std::unique_ptr<SkiaBackendTexture> GaneshGpuContext::makeBackendTexture(AHardwareBuffer* buffer,
+ bool isOutputBuffer) {
+ return std::make_unique<GaneshBackendTexture>(mGrContext, buffer, isOutputBuffer);
+}
+
+sk_sp<SkSurface> GaneshGpuContext::createRenderTarget(SkImageInfo imageInfo) {
+ constexpr int kSampleCount = 1; // enable AA
+ constexpr SkSurfaceProps* kProps = nullptr;
+ constexpr bool kMipmapped = false;
+ return SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kNo, imageInfo, kSampleCount,
+ kTopLeft_GrSurfaceOrigin, kProps, kMipmapped,
+ mGrContext->supportsProtectedContent());
+}
+
+size_t GaneshGpuContext::getMaxRenderTargetSize() const {
+ return mGrContext->maxRenderTargetSize();
+};
+
+size_t GaneshGpuContext::getMaxTextureSize() const {
+ return mGrContext->maxTextureSize();
+};
+
+bool GaneshGpuContext::isAbandonedOrDeviceLost() {
+ return mGrContext->abandoned();
+}
+
+void GaneshGpuContext::setResourceCacheLimit(size_t maxResourceBytes) {
+ mGrContext->setResourceCacheLimit(maxResourceBytes);
+}
+
+void GaneshGpuContext::purgeUnlockedScratchResources() {
+ mGrContext->purgeUnlockedResources(GrPurgeResourceOptions::kScratchResourcesOnly);
+}
+
+void GaneshGpuContext::resetContextIfApplicable() {
+ mGrContext->resetContext(); // Only applicable to GL
+};
+
+void GaneshGpuContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
+ mGrContext->dumpMemoryStatistics(traceMemoryDump);
+}
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/GaneshGpuContext.h b/libs/renderengine/skia/compat/GaneshGpuContext.h
new file mode 100644
index 0000000..aeb1a82
--- /dev/null
+++ b/libs/renderengine/skia/compat/GaneshGpuContext.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <android-base/macros.h>
+
+namespace android::renderengine::skia {
+
+class GaneshGpuContext : public SkiaGpuContext {
+public:
+ GaneshGpuContext(sk_sp<GrDirectContext> grContext);
+ ~GaneshGpuContext() override;
+
+ sk_sp<GrDirectContext> grDirectContext() 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;
+ void setResourceCacheLimit(size_t maxResourceBytes) override;
+
+ void purgeUnlockedScratchResources() override;
+ void resetContextIfApplicable() override;
+
+ void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const override;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(GaneshGpuContext);
+
+ const sk_sp<GrDirectContext> mGrContext;
+};
+
+} // namespace android::renderengine::skia
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/SkiaBackendTexture.h b/libs/renderengine/skia/compat/SkiaBackendTexture.h
new file mode 100644
index 0000000..09877a5
--- /dev/null
+++ b/libs/renderengine/skia/compat/SkiaBackendTexture.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 <include/android/GrAHardwareBufferUtils.h>
+#include <include/core/SkColorSpace.h>
+#include <include/gpu/GrDirectContext.h>
+
+#include <android/hardware_buffer.h>
+#include <ui/GraphicTypes.h>
+
+namespace android::renderengine::skia {
+
+/**
+ * Abstraction over a Skia backend-specific texture type.
+ *
+ * This class does not do any lifecycle management, and should typically be wrapped in an
+ * AutoBackendTexture::LocalRef. Typically created via SkiaGpuContext::makeBackendTexture(...).
+ */
+class SkiaBackendTexture {
+public:
+ SkiaBackendTexture(AHardwareBuffer* buffer, bool isOutputBuffer)
+ : mIsOutputBuffer(isOutputBuffer) {
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(buffer, &desc);
+
+ mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
+ }
+ virtual ~SkiaBackendTexture() = default;
+
+ // These two definitions mirror Skia's own types used for texture release callbacks, which are
+ // re-declared multiple times between context-specific implementation headers for Ganesh vs.
+ // Graphite, and within the context of SkImages vs. SkSurfaces. Our own re-declaration allows us
+ // to not pull in any implementation-specific headers here.
+ using ReleaseContext = void*;
+ using TextureReleaseProc = void (*)(ReleaseContext);
+
+ // Guaranteed to be non-null (crashes otherwise). An opaque alphaType may coerce the internal
+ // color type to RBGX.
+ virtual sk_sp<SkImage> makeImage(SkAlphaType alphaType, ui::Dataspace dataspace,
+ TextureReleaseProc releaseImageProc,
+ ReleaseContext releaseContext) = 0;
+
+ // Guaranteed to be non-null (crashes otherwise).
+ virtual sk_sp<SkSurface> makeSurface(ui::Dataspace dataspace,
+ TextureReleaseProc releaseSurfaceProc,
+ ReleaseContext releaseContext) = 0;
+
+ bool isOutputBuffer() const { return mIsOutputBuffer; }
+
+ SkColorType internalColorType() const { return mColorType; }
+
+protected:
+ // Strip alpha channel from rawColorType if alphaType is opaque (note: only works for RGBA_8888)
+ SkColorType colorTypeForImage(SkAlphaType alphaType) const {
+ if (alphaType == kOpaque_SkAlphaType) {
+ // TODO: b/40043126 - Support RGBX SkColorType for F16 and support it and 101010x as a
+ // source
+ if (internalColorType() == kRGBA_8888_SkColorType) {
+ return kRGB_888x_SkColorType;
+ }
+ }
+ return internalColorType();
+ }
+
+private:
+ const bool mIsOutputBuffer;
+ SkColorType mColorType = kUnknown_SkColorType;
+};
+
+} // namespace android::renderengine::skia
diff --git a/libs/renderengine/skia/compat/SkiaGpuContext.h b/libs/renderengine/skia/compat/SkiaGpuContext.h
new file mode 100644
index 0000000..282dfe7
--- /dev/null
+++ b/libs/renderengine/skia/compat/SkiaGpuContext.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
+
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+
+#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"
+
+#include <log/log.h>
+
+#include <memory>
+
+namespace android::renderengine::skia {
+
+/**
+ * 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);
+
+ /**
+ * 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;
+
+ /**
+ * Only callable on Ganesh-backed instances of SkiaGpuContext, otherwise fatal.
+ */
+ virtual sk_sp<GrDirectContext> grDirectContext() {
+ 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;
+
+ /**
+ * Notes:
+ * - The surface doesn't count against Skia's caching budgets.
+ * - Protected status is set to match the implementation's underlying context.
+ * - The origin of the surface in texture space corresponds to the top-left content pixel.
+ * - AA is always enabled.
+ */
+ virtual sk_sp<SkSurface> createRenderTarget(SkImageInfo imageInfo) = 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 purgeUnlockedScratchResources() = 0;
+ virtual void resetContextIfApplicable() = 0; // No-op outside of GL (&& Ganesh at this point.)
+
+ virtual void dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const = 0;
+};
+
+} // namespace android::renderengine::skia
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/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 9cddc75..180c922 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -21,6 +21,8 @@
#include <SkRuntimeEffect.h>
#include <SkSurface.h>
+#include "../compat/SkiaGpuContext.h"
+
using namespace std;
namespace android {
@@ -38,8 +40,9 @@
virtual ~BlurFilter(){}
// Execute blur, saving it to a texture
- virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
- const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
+ virtual sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput,
+ const SkRect& blurRect) const = 0;
/**
* Draw the blurred content (from the generate method) into the canvas.
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
index e72c501..c9499cb 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -42,14 +42,13 @@
GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
-sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect)
- const {
+sk_sp<SkImage> GaussianBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input,
+ const SkRect& blurRect) const {
// Create blur surface with the bit depth and colorspace of the original surface
SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
std::ceil(blurRect.height() * kInputScale));
- sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context,
- skgpu::Budgeted::kNo, scaledInfo);
+ sk_sp<SkSurface> surface = context->createRenderTarget(scaledInfo);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
index a4febd2..878ab21 100644
--- a/libs/renderengine/skia/filters/GaussianBlurFilter.h
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -37,9 +37,8 @@
virtual ~GaussianBlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
-
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
index 09f09a6..7a070d7 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -73,8 +73,7 @@
return surface->makeImageSnapshot();
}
-sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context,
- const uint32_t blurRadius,
+sk_sp<SkImage> KawaseBlurFilter::generate(SkiaGpuContext* context, const uint32_t blurRadius,
const sk_sp<SkImage> input,
const SkRect& blurRect) const {
LOG_ALWAYS_FATAL_IF(context == nullptr, "%s: Needs GPU context", __func__);
@@ -108,12 +107,7 @@
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
- constexpr int kSampleCount = 1;
- constexpr bool kMipmapped = false;
- constexpr SkSurfaceProps* kProps = nullptr;
- sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kNo, scaledInfo,
- kSampleCount, kTopLeft_GrSurfaceOrigin,
- kProps, kMipmapped, input->isProtected());
+ sk_sp<SkSurface> surface = context->createRenderTarget(scaledInfo);
LOG_ALWAYS_FATAL_IF(!surface, "%s: Failed to create surface for blurring!", __func__);
sk_sp<SkImage> tmpBlur = makeImage(surface.get(), &blurBuilder);
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
index 0ac5ac8..429a537 100644
--- a/libs/renderengine/skia/filters/KawaseBlurFilter.h
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -42,7 +42,7 @@
virtual ~KawaseBlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ sk_sp<SkImage> generate(SkiaGpuContext* context, const uint32_t radius,
const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
private:
diff --git a/libs/renderengine/skia/filters/MouriMap.cpp b/libs/renderengine/skia/filters/MouriMap.cpp
new file mode 100644
index 0000000..7d8b8a5
--- /dev/null
+++ b/libs/renderengine/skia/filters/MouriMap.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "MouriMap.h"
+#include <SkCanvas.h>
+#include <SkColorType.h>
+#include <SkPaint.h>
+#include <SkTileMode.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+namespace {
+sk_sp<SkRuntimeEffect> makeEffect(const SkString& sksl) {
+ auto [effect, error] = SkRuntimeEffect::MakeForShader(sksl);
+ LOG_ALWAYS_FATAL_IF(!effect, "RuntimeShader error: %s", error.c_str());
+ return effect;
+}
+const SkString kCrosstalkAndChunk16x16(R"(
+ uniform shader bitmap;
+ uniform float hdrSdrRatio;
+ vec4 main(vec2 xy) {
+ float maximum = 0.0;
+ for (int y = 0; y < 16; y++) {
+ for (int x = 0; x < 16; x++) {
+ float3 linear = toLinearSrgb(bitmap.eval(xy * 16 + vec2(x, y)).rgb) * hdrSdrRatio;
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ maximum = max(maximum, log2(max(maxRGB, 1.0)));
+ }
+ }
+ return float4(float3(maximum), 1.0);
+ }
+)");
+const SkString kChunk8x8(R"(
+ uniform shader bitmap;
+ vec4 main(vec2 xy) {
+ float maximum = 0.0;
+ for (int y = 0; y < 8; y++) {
+ for (int x = 0; x < 8; x++) {
+ maximum = max(maximum, bitmap.eval(xy * 8 + vec2(x, y)).r);
+ }
+ }
+ return float4(float3(maximum), 1.0);
+ }
+)");
+const SkString kBlur(R"(
+ uniform shader bitmap;
+ vec4 main(vec2 xy) {
+ float C[5];
+ C[0] = 1.0 / 16.0;
+ C[1] = 4.0 / 16.0;
+ C[2] = 6.0 / 16.0;
+ C[3] = 4.0 / 16.0;
+ C[4] = 1.0 / 16.0;
+ float result = 0.0;
+ for (int y = -2; y <= 2; y++) {
+ for (int x = -2; x <= 2; x++) {
+ result += C[y + 2] * C[x + 2] * bitmap.eval(xy + vec2(x, y)).r;
+ }
+ }
+ return float4(float3(exp2(result)), 1.0);
+ }
+)");
+const SkString kTonemap(R"(
+ uniform shader image;
+ uniform shader lux;
+ uniform float scaleFactor;
+ uniform float hdrSdrRatio;
+ vec4 main(vec2 xy) {
+ float localMax = lux.eval(xy * scaleFactor).r;
+ float4 rgba = image.eval(xy);
+ float3 linear = toLinearSrgb(rgba.rgb) * hdrSdrRatio;
+
+ if (localMax <= 1.0) {
+ return float4(fromLinearSrgb(linear), 1.0);
+ }
+
+ float maxRGB = max(linear.r, max(linear.g, linear.b));
+ localMax = max(localMax, maxRGB);
+ float gain = (1 + maxRGB / (localMax * localMax)) / (1 + maxRGB);
+ return float4(fromLinearSrgb(linear * gain), 1.0);
+ }
+)");
+
+// Draws the given runtime shader on a GPU surface and returns the result as an SkImage.
+sk_sp<SkImage> makeImage(SkSurface* surface, const SkRuntimeShaderBuilder& builder) {
+ sk_sp<SkShader> shader = builder.makeShader(nullptr);
+ LOG_ALWAYS_FATAL_IF(!shader, "%s, Failed to make shader!", __func__);
+ SkPaint paint;
+ paint.setShader(std::move(shader));
+ paint.setBlendMode(SkBlendMode::kSrc);
+ surface->getCanvas()->drawPaint(paint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace
+
+MouriMap::MouriMap()
+ : mCrosstalkAndChunk16x16(makeEffect(kCrosstalkAndChunk16x16)),
+ mChunk8x8(makeEffect(kChunk8x8)),
+ mBlur(makeEffect(kBlur)),
+ mTonemap(makeEffect(kTonemap)) {}
+
+sk_sp<SkShader> MouriMap::mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) {
+ auto downchunked = downchunk(context, input, hdrSdrRatio);
+ auto localLux = blur(context, downchunked.get());
+ return tonemap(input, localLux.get(), hdrSdrRatio);
+}
+
+sk_sp<SkImage> MouriMap::downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) const {
+ SkMatrix matrix;
+ SkImage* image = input->isAImage(&matrix, (SkTileMode*)nullptr);
+ SkRuntimeShaderBuilder crosstalkAndChunk16x16Builder(mCrosstalkAndChunk16x16);
+ crosstalkAndChunk16x16Builder.child("bitmap") = input;
+ crosstalkAndChunk16x16Builder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ // TODO: fp16 might be overkill. Most practical surfaces use 8-bit RGB for HDR UI and 10-bit YUV
+ // for HDR video. These downsample operations compute log2(max(linear RGB, 1.0)). So we don't
+ // care about LDR precision since they all resolve to LDR-max. For appropriately mastered HDR
+ // content that follows BT. 2408, 25% of the bit range for HLG and 42% of the bit range for PQ
+ // are reserved for HDR. This means that we can fit the entire HDR range for 10-bit HLG inside
+ // of 8 bits. We can also fit about half of the range for PQ, but most content does not fill the
+ // entire 10k nit range for PQ. Furthermore, we blur all of this later on anyways, so we might
+ // not need to be so precise. So, it's possible that we could use A8 or R8 instead. If we want
+ // to be really conservative we can try to use R16 or even RGBA1010102 to fake an R10 surface,
+ // which would cut write bandwidth significantly.
+ static constexpr auto kFirstDownscaleAmount = 16;
+ sk_sp<SkSurface> firstDownsampledSurface = context->createRenderTarget(
+ image->imageInfo()
+ .makeWH(std::max(1, image->width() / kFirstDownscaleAmount),
+ std::max(1, image->height() / kFirstDownscaleAmount))
+ .makeColorType(kRGBA_F16_SkColorType));
+ LOG_ALWAYS_FATAL_IF(!firstDownsampledSurface, "%s: Failed to create surface!", __func__);
+ auto firstDownsampledImage =
+ makeImage(firstDownsampledSurface.get(), crosstalkAndChunk16x16Builder);
+ SkRuntimeShaderBuilder chunk8x8Builder(mChunk8x8);
+ chunk8x8Builder.child("bitmap") =
+ firstDownsampledImage->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions());
+ static constexpr auto kSecondDownscaleAmount = 8;
+ sk_sp<SkSurface> secondDownsampledSurface = context->createRenderTarget(
+ firstDownsampledImage->imageInfo()
+ .makeWH(std::max(1, firstDownsampledImage->width() / kSecondDownscaleAmount),
+ std::max(1, firstDownsampledImage->height() / kSecondDownscaleAmount)));
+ LOG_ALWAYS_FATAL_IF(!secondDownsampledSurface, "%s: Failed to create surface!", __func__);
+ return makeImage(secondDownsampledSurface.get(), chunk8x8Builder);
+}
+sk_sp<SkImage> MouriMap::blur(SkiaGpuContext* context, SkImage* input) const {
+ SkRuntimeShaderBuilder blurBuilder(mBlur);
+ blurBuilder.child("bitmap") =
+ input->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions());
+ sk_sp<SkSurface> blurSurface = context->createRenderTarget(input->imageInfo());
+ LOG_ALWAYS_FATAL_IF(!blurSurface, "%s: Failed to create surface!", __func__);
+ return makeImage(blurSurface.get(), blurBuilder);
+}
+sk_sp<SkShader> MouriMap::tonemap(sk_sp<SkShader> input, SkImage* localLux,
+ float hdrSdrRatio) const {
+ static constexpr float kScaleFactor = 1.0f / 128.0f;
+ SkRuntimeShaderBuilder tonemapBuilder(mTonemap);
+ tonemapBuilder.child("image") = input;
+ tonemapBuilder.child("lux") =
+ localLux->makeRawShader(SkTileMode::kClamp, SkTileMode::kClamp,
+ SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone));
+ tonemapBuilder.uniform("scaleFactor") = kScaleFactor;
+ tonemapBuilder.uniform("hdrSdrRatio") = hdrSdrRatio;
+ return tonemapBuilder.makeShader();
+}
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/skia/filters/MouriMap.h b/libs/renderengine/skia/filters/MouriMap.h
new file mode 100644
index 0000000..3c0df8a
--- /dev/null
+++ b/libs/renderengine/skia/filters/MouriMap.h
@@ -0,0 +1,81 @@
+/*
+ * 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 <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkShader.h>
+#include "../compat/SkiaGpuContext.h"
+namespace android {
+namespace renderengine {
+namespace skia {
+/**
+ * MouriMap is a fast, albeit not realtime, tonemapping algorithm optimized for near-exact
+ * preservation of SDR (or, equivalently, LDR) regions, while trying to do an acceptable job of
+ * preserving HDR detail.
+ *
+ * MouriMap is a local tonemapping algorithm, meaning that nearby pixels are taken into
+ * consideration when choosing a tonemapping curve.
+ *
+ * The algorithm conceptually is as follows:
+ * 1. Partition the image into 128x128 chunks, computing the log2(maximum luminance) in each chunk
+ *. a. Maximum luminance is computed as max(R, G, B), where the R, G, B values are in linear
+ *. luminance on a scale defined by the destination color gamut. Max(R, G, B) has been found
+ *. to minimize difference in hue while restricting to typical LDR color volumes. See: Burke,
+ *. Adam & Smith, Michael & Zink, Michael. 2020. Color Volume and Hue-preservation in HDR
+ *. Tone Mapping. SMPTE Motion Imaging Journal.
+ *. b. Each computed luminance is lower-bounded by 1.0 in Skia's color
+ *. management, or 203 nits.
+ * 2. Blur the resulting chunks using a 5x5 gaussian kernel, to smooth out the local luminance map.
+ * 3. Now, for each pixel in the original image:
+ * a. Upsample from the blurred chunks of luminance computed in (2). Call this luminance value
+ *. L: an estimate of the maximum luminance of surrounding pixels.
+ *. b. If the luminance is less than 1.0 (203 nits), then do not modify the pixel value of the
+ *. original image.
+ *. c. Otherwise,
+ *. parameterize a tone-mapping curve using a method described by Chrome:
+ *. https://docs.google.com/document/d/17T2ek1i2R7tXdfHCnM-i5n6__RoYe0JyMfKmTEjoGR8/.
+ *. i. Compute a gain G = (1 + max(linear R, linear G, linear B) / (L * L))
+ *. / (1 + max(linear R, linear G, linear B)). Note the similarity with the 1D curve
+ *. described by Erik Reinhard, Michael Stark, Peter Shirley, and James Ferwerda. 2002.
+ *. Photographic tone reproduction for digital images. ACM Trans. Graph.
+ *. ii. Multiply G by the linear source colors to compute the final colors.
+ *
+ * Because it is a multi-renderpass algorithm requiring multiple off-screen textures, MouriMap is
+ * typically not suitable to be ran "frequently", at high refresh rates (e.g., 120hz). However,
+ * MouriMap is sufficiently fast enough for infrequent composition where preserving SDR detail is
+ * most important, such as for screenshots.
+ */
+class MouriMap {
+public:
+ MouriMap();
+ // Apply the MouriMap tonemmaping operator to the input.
+ // The HDR/SDR ratio describes the luminace range of the input. 1.0 means SDR. Anything larger
+ // then 1.0 means that there is headroom above the SDR region.
+ sk_sp<SkShader> mouriMap(SkiaGpuContext* context, sk_sp<SkShader> input, float hdrSdrRatio);
+
+private:
+ sk_sp<SkImage> downchunk(SkiaGpuContext* context, sk_sp<SkShader> input,
+ float hdrSdrRatio) const;
+ sk_sp<SkImage> blur(SkiaGpuContext* context, SkImage* input) const;
+ sk_sp<SkShader> tonemap(sk_sp<SkShader> input, SkImage* localLux, float hdrSdrRatio) const;
+ const sk_sp<SkRuntimeEffect> mCrosstalkAndChunk16x16;
+ const sk_sp<SkRuntimeEffect> mChunk8x8;
+ const sk_sp<SkRuntimeEffect> mBlur;
+ const sk_sp<SkRuntimeEffect> mTonemap;
+};
+} // namespace skia
+} // namespace renderengine
+} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 0eea187..0783714 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -46,6 +46,7 @@
"libshaders",
"libtonemap",
"libsurfaceflinger_common",
+ "libsurfaceflingerflags",
],
header_libs: [
"libtonemap_headers",
@@ -64,5 +65,6 @@
"libui",
"libutils",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index 7b8eb84..a8a9823 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -22,6 +22,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wextra"
+#include <com_android_graphics_surfaceflinger_flags.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <renderengine/ExternalTexture.h>
@@ -41,6 +42,14 @@
#include "../skia/SkiaVkRenderEngine.h"
#include "../threaded/RenderEngineThreaded.h"
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE) || \
+ COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(FORCE_COMPILE_GRAPHITE_RENDERENGINE)
+#define COMPILE_GRAPHITE_RENDERENGINE 1
+#else
+#define COMPILE_GRAPHITE_RENDERENGINE 0
+#endif
+
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
constexpr int DEFAULT_DISPLAY_OFFSET = 64;
@@ -107,6 +116,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 +125,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,8 +142,41 @@
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;
+ }
+};
+
+// TODO: b/341728634 - Clean up conditional compilation.
+#if COMPILE_GRAPHITE_RENDERENGINE
+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;
+ }
+};
+#endif
+
class RenderEngineTest : public ::testing::TestWithParam<std::shared_ptr<RenderEngineFactory>> {
public:
std::shared_ptr<renderengine::ExternalTexture> allocateDefaultBuffer() {
@@ -219,7 +254,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 +263,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 +1277,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() {
@@ -1469,9 +1509,15 @@
expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
}
+// TODO: b/341728634 - Clean up conditional compilation.
INSTANTIATE_TEST_SUITE_P(PerRenderEngineType, RenderEngineTest,
testing::Values(std::make_shared<SkiaGLESRenderEngineFactory>(),
- std::make_shared<SkiaVkRenderEngineFactory>()));
+ std::make_shared<GaneshVkRenderEngineFactory>()
+#if COMPILE_GRAPHITE_RENDERENGINE
+ ,
+ std::make_shared<GraphiteVkRenderEngineFactory>()
+#endif
+ ));
TEST_P(RenderEngineTest, drawLayers_noLayersToDraw) {
if (!GetParam()->apiSupported()) {
@@ -2490,51 +2536,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,12 +3148,19 @@
}
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();
}
initializeRenderEngine();
- auto fut = mRE->primeCache(false);
+ PrimeCacheConfig config;
+ config.cacheUltraHDR = false;
+ auto fut = mRE->primeCache(config);
if (fut.valid()) {
fut.wait();
}
diff --git a/libs/renderengine/tests/RenderEngineThreadedTest.cpp b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
index d56dbb2..bdd9402 100644
--- a/libs/renderengine/tests/RenderEngineThreadedTest.cpp
+++ b/libs/renderengine/tests/RenderEngineThreadedTest.cpp
@@ -25,6 +25,7 @@
namespace android {
+using renderengine::PrimeCacheConfig;
using testing::_;
using testing::Eq;
using testing::Mock;
@@ -48,9 +49,25 @@
mThreadedRE->dump(testString);
}
+MATCHER_P(EqConfig, other, "Equality for prime cache config") {
+ return arg.cacheHolePunchLayer == other.cacheHolePunchLayer &&
+ arg.cacheSolidLayers == other.cacheSolidLayers &&
+ arg.cacheSolidDimmedLayers == other.cacheSolidDimmedLayers &&
+ arg.cacheImageLayers == other.cacheImageLayers &&
+ arg.cacheImageDimmedLayers == other.cacheImageDimmedLayers &&
+ arg.cacheClippedLayers == other.cacheClippedLayers &&
+ arg.cacheShadowLayers == other.cacheShadowLayers &&
+ arg.cachePIPImageLayers == other.cachePIPImageLayers &&
+ arg.cacheTransparentImageDimmedLayers == other.cacheTransparentImageDimmedLayers &&
+ arg.cacheClippedDimmedImageLayers == other.cacheClippedDimmedImageLayers &&
+ arg.cacheUltraHDR == other.cacheUltraHDR;
+}
+
TEST_F(RenderEngineThreadedTest, primeCache) {
- EXPECT_CALL(*mRenderEngine, primeCache(false));
- mThreadedRE->primeCache(false);
+ PrimeCacheConfig config;
+ config.cacheUltraHDR = false;
+ EXPECT_CALL(*mRenderEngine, primeCache(EqConfig(config)));
+ mThreadedRE->primeCache(config);
// need to call ANY synchronous function after primeCache to ensure that primeCache has
// completed asynchronously before the test completes execution.
mThreadedRE->getContextPriority();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index f4cebc0..d27c151 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -130,7 +130,7 @@
}
}
-std::future<void> RenderEngineThreaded::primeCache(bool shouldPrimeUltraHDR) {
+std::future<void> RenderEngineThreaded::primeCache(PrimeCacheConfig config) {
const auto resultPromise = std::make_shared<std::promise<void>>();
std::future<void> resultFuture = resultPromise->get_future();
ATRACE_CALL();
@@ -138,20 +138,19 @@
// for the futures.
{
std::lock_guard lock(mThreadMutex);
- mFunctionCalls.push(
- [resultPromise, shouldPrimeUltraHDR](renderengine::RenderEngine& instance) {
- ATRACE_NAME("REThreaded::primeCache");
- if (setSchedFifo(false) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_OTHER for primeCache");
- }
+ mFunctionCalls.push([resultPromise, config](renderengine::RenderEngine& instance) {
+ ATRACE_NAME("REThreaded::primeCache");
+ if (setSchedFifo(false) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_OTHER for primeCache");
+ }
- instance.primeCache(shouldPrimeUltraHDR);
- resultPromise->set_value();
+ instance.primeCache(config);
+ resultPromise->set_value();
- if (setSchedFifo(true) != NO_ERROR) {
- ALOGW("Couldn't set SCHED_FIFO for primeCache");
- }
- });
+ if (setSchedFifo(true) != NO_ERROR) {
+ ALOGW("Couldn't set SCHED_FIFO for primeCache");
+ }
+ });
}
mCondition.notify_one();
diff --git a/libs/renderengine/threaded/RenderEngineThreaded.h b/libs/renderengine/threaded/RenderEngineThreaded.h
index d440c96..d4997d6 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.h
+++ b/libs/renderengine/threaded/RenderEngineThreaded.h
@@ -41,7 +41,7 @@
RenderEngineThreaded(CreateInstanceFactory factory);
~RenderEngineThreaded() override;
- std::future<void> primeCache(bool shouldPrimeUltraHDR) override;
+ std::future<void> primeCache(PrimeCacheConfig config) override;
void dump(std::string& result) override;
diff --git a/libs/sensor/libsensor_flags.aconfig b/libs/sensor/libsensor_flags.aconfig
index c511f4a..cbf3055 100644
--- a/libs/sensor/libsensor_flags.aconfig
+++ b/libs/sensor/libsensor_flags.aconfig
@@ -8,3 +8,10 @@
bug: "322228259"
is_fixed_read_only: true
}
+
+flag {
+ name: "sensor_event_queue_report_sensor_usage_in_tracing"
+ namespace: "sensors"
+ description: "When this flag is enabled, sensor event queue will report sensor usage when system trace is enabled."
+ bug: "333132224"
+}
\ No newline at end of file
diff --git a/libs/sensorprivacy/Android.bp b/libs/sensorprivacy/Android.bp
index 1e7e707..00514c4 100644
--- a/libs/sensorprivacy/Android.bp
+++ b/libs/sensorprivacy/Android.bp
@@ -57,7 +57,6 @@
filegroup {
name: "libsensorprivacy_aidl",
srcs: [
- "aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl",
"aidl/android/hardware/ISensorPrivacyListener.aidl",
"aidl/android/hardware/ISensorPrivacyManager.aidl",
],
diff --git a/libs/sensorprivacy/SensorPrivacyManager.cpp b/libs/sensorprivacy/SensorPrivacyManager.cpp
index fe93786..3f3ad93 100644
--- a/libs/sensorprivacy/SensorPrivacyManager.cpp
+++ b/libs/sensorprivacy/SensorPrivacyManager.cpp
@@ -155,10 +155,9 @@
return DISABLED;
}
-std::vector<hardware::CameraPrivacyAllowlistEntry>
- SensorPrivacyManager::getCameraPrivacyAllowlist(){
+std::vector<String16> SensorPrivacyManager::getCameraPrivacyAllowlist(){
sp<hardware::ISensorPrivacyManager> service = getService();
- std::vector<hardware::CameraPrivacyAllowlistEntry> result;
+ std::vector<String16> result;
if (service != nullptr) {
service->getCameraPrivacyAllowlist(&result);
return result;
diff --git a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl b/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
deleted file mode 100644
index 03e1537..0000000
--- a/libs/sensorprivacy/aidl/android/hardware/CameraPrivacyAllowlistEntry.aidl
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Copyright (c) 2024, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware;
-
-parcelable CameraPrivacyAllowlistEntry {
- String packageName;
- boolean isMandatory;
-}
diff --git a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
index b6bd39e..f707187 100644
--- a/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
+++ b/libs/sensorprivacy/aidl/android/hardware/ISensorPrivacyManager.aidl
@@ -16,7 +16,6 @@
package android.hardware;
-import android.hardware.CameraPrivacyAllowlistEntry;
import android.hardware.ISensorPrivacyListener;
/** @hide */
@@ -43,7 +42,7 @@
void setToggleSensorPrivacyForProfileGroup(int userId, int source, int sensor, boolean enable);
- List<CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ List<String> getCameraPrivacyAllowlist();
int getToggleSensorPrivacyState(int toggleType, int sensor);
diff --git a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
index 9e97e16..8935b76 100644
--- a/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
+++ b/libs/sensorprivacy/include/sensorprivacy/SensorPrivacyManager.h
@@ -45,9 +45,7 @@
enum {
ENABLED = 1,
DISABLED = 2,
- AUTOMOTIVE_DRIVER_ASSISTANCE_HELPFUL_APPS = 3,
- AUTOMOTIVE_DRIVER_ASSISTANCE_REQUIRED_APPS = 4,
- AUTOMOTIVE_DRIVER_ASSISTANCE_APPS = 5
+ ENABLED_EXCEPT_ALLOWLISTED_APPS = 3
};
SensorPrivacyManager();
@@ -62,7 +60,7 @@
bool isToggleSensorPrivacyEnabled(int toggleType, int sensor);
status_t isToggleSensorPrivacyEnabled(int toggleType, int sensor, bool &result);
int getToggleSensorPrivacyState(int toggleType, int sensor);
- std::vector<hardware::CameraPrivacyAllowlistEntry> getCameraPrivacyAllowlist();
+ std::vector<String16> getCameraPrivacyAllowlist();
bool isCameraPrivacyEnabled(String16 packageName);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/tracing_perfetto/.clang-format b/libs/tracing_perfetto/.clang-format
new file mode 100644
index 0000000..f397454
--- /dev/null
+++ b/libs/tracing_perfetto/.clang-format
@@ -0,0 +1,12 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 80
+ContinuationIndentWidth: 4
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+UseTab: Never
+PenaltyExcessCharacter: 32
\ No newline at end of file
diff --git a/libs/tracing_perfetto/Android.bp b/libs/tracing_perfetto/Android.bp
new file mode 100644
index 0000000..3a4c869
--- /dev/null
+++ b/libs/tracing_perfetto/Android.bp
@@ -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.
+
+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_shared {
+ name: "libtracing_perfetto",
+ export_include_dirs: [
+ "include",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ "-Wno-unused-function",
+ ],
+
+ srcs: [
+ "tracing_perfetto.cpp",
+ "tracing_perfetto_internal.cpp",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "libperfetto_c",
+ "android.os.flags-aconfig-cc-host",
+ ],
+
+ host_supported: true,
+}
diff --git a/libs/tracing_perfetto/OWNERS b/libs/tracing_perfetto/OWNERS
new file mode 100644
index 0000000..e2d4b46
--- /dev/null
+++ b/libs/tracing_perfetto/OWNERS
@@ -0,0 +1,2 @@
+zezeozue@google.com
+biswarupp@google.com
\ No newline at end of file
diff --git a/libs/tracing_perfetto/TEST_MAPPING b/libs/tracing_perfetto/TEST_MAPPING
new file mode 100644
index 0000000..1805e18
--- /dev/null
+++ b/libs/tracing_perfetto/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "libtracing_perfetto_tests"
+ }
+ ]
+}
diff --git a/libs/tracing_perfetto/include/trace_categories.h b/libs/tracing_perfetto/include/trace_categories.h
new file mode 100644
index 0000000..6d4168b
--- /dev/null
+++ b/libs/tracing_perfetto/include/trace_categories.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRACE_CATEGORIES_H
+#define TRACE_CATEGORIES_H
+
+/**
+ * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.
+ */
+#define TRACE_CATEGORY_ALWAYS (1 << 0)
+#define TRACE_CATEGORY_GRAPHICS (1 << 1)
+#define TRACE_CATEGORY_INPUT (1 << 2)
+#define TRACE_CATEGORY_VIEW (1 << 3)
+#define TRACE_CATEGORY_WEBVIEW (1 << 4)
+#define TRACE_CATEGORY_WINDOW_MANAGER (1 << 5)
+#define TRACE_CATEGORY_ACTIVITY_MANAGER (1 << 6)
+#define TRACE_CATEGORY_SYNC_MANAGER (1 << 7)
+#define TRACE_CATEGORY_AUDIO (1 << 8)
+#define TRACE_CATEGORY_VIDEO (1 << 9)
+#define TRACE_CATEGORY_CAMERA (1 << 10)
+#define TRACE_CATEGORY_HAL (1 << 11)
+#define TRACE_CATEGORY_APP (1 << 12)
+#define TRACE_CATEGORY_RESOURCES (1 << 13)
+#define TRACE_CATEGORY_DALVIK (1 << 14)
+#define TRACE_CATEGORY_RS (1 << 15)
+#define TRACE_CATEGORY_BIONIC (1 << 16)
+#define TRACE_CATEGORY_POWER (1 << 17)
+#define TRACE_CATEGORY_PACKAGE_MANAGER (1 << 18)
+#define TRACE_CATEGORY_SYSTEM_SERVER (1 << 19)
+#define TRACE_CATEGORY_DATABASE (1 << 20)
+#define TRACE_CATEGORY_NETWORK (1 << 21)
+#define TRACE_CATEGORY_ADB (1 << 22)
+#define TRACE_CATEGORY_VIBRATOR (1 << 23)
+#define TRACE_CATEGORY_AIDL (1 << 24)
+#define TRACE_CATEGORY_NNAPI (1 << 25)
+#define TRACE_CATEGORY_RRO (1 << 26)
+#define TRACE_CATEGORY_THERMAL (1 << 27)
+
+// Allow all categories except TRACE_CATEGORY_APP
+#define TRACE_CATEGORIES \
+ TRACE_CATEGORY_ALWAYS | TRACE_CATEGORY_GRAPHICS | TRACE_CATEGORY_INPUT | \
+ TRACE_CATEGORY_VIEW | TRACE_CATEGORY_WEBVIEW | \
+ TRACE_CATEGORY_WINDOW_MANAGER | TRACE_CATEGORY_ACTIVITY_MANAGER | \
+ TRACE_CATEGORY_SYNC_MANAGER | TRACE_CATEGORY_AUDIO | \
+ TRACE_CATEGORY_VIDEO | TRACE_CATEGORY_CAMERA | TRACE_CATEGORY_HAL | \
+ TRACE_CATEGORY_RESOURCES | TRACE_CATEGORY_DALVIK | TRACE_CATEGORY_RS | \
+ TRACE_CATEGORY_BIONIC | TRACE_CATEGORY_POWER | \
+ TRACE_CATEGORY_PACKAGE_MANAGER | TRACE_CATEGORY_SYSTEM_SERVER | \
+ TRACE_CATEGORY_DATABASE | TRACE_CATEGORY_NETWORK | TRACE_CATEGORY_ADB | \
+ TRACE_CATEGORY_VIBRATOR | TRACE_CATEGORY_AIDL | TRACE_CATEGORY_NNAPI | \
+ TRACE_CATEGORY_RRO | TRACE_CATEGORY_THERMAL
+#endif // TRACE_CATEGORIES_H
diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/libs/tracing_perfetto/include/trace_result.h
similarity index 71%
copy from libs/gui/aidl/android/gui/LayerDebugInfo.aidl
copy to libs/tracing_perfetto/include/trace_result.h
index faca980..f7581fc 100644
--- a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl
+++ b/libs/tracing_perfetto/include/trace_result.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * 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.
@@ -14,6 +14,17 @@
* limitations under the License.
*/
-package android.gui;
+#ifndef TRACE_RESULT_H
+#define TRACE_RESULT_H
-parcelable LayerDebugInfo cpp_header "gui/LayerDebugInfo.h";
+namespace tracing_perfetto {
+
+enum class Result {
+ SUCCESS,
+ NOT_SUPPORTED,
+ INVALID_INPUT,
+};
+
+}
+
+#endif // TRACE_RESULT_H
diff --git a/libs/tracing_perfetto/include/tracing_perfetto.h b/libs/tracing_perfetto/include/tracing_perfetto.h
new file mode 100644
index 0000000..2c1c2a4
--- /dev/null
+++ b/libs/tracing_perfetto/include/tracing_perfetto.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef TRACING_PERFETTO_H
+#define TRACING_PERFETTO_H
+
+#include <stdint.h>
+
+#include "trace_result.h"
+
+namespace tracing_perfetto {
+
+void registerWithPerfetto(bool test = false);
+
+Result traceBegin(uint64_t category, const char* name);
+
+Result traceEnd(uint64_t category);
+
+Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie);
+
+Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie);
+
+Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+ const char* trackName, int32_t cookie);
+
+Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+ int32_t cookie);
+
+Result traceInstant(uint64_t category, const char* name);
+
+Result traceInstantForTrack(uint64_t category, const char* trackName,
+ const char* name);
+
+Result traceCounter(uint64_t category, const char* name, int64_t value);
+
+bool isTagEnabled(uint64_t category);
+
+} // namespace tracing_perfetto
+
+#endif // TRACING_PERFETTO_H
diff --git a/libs/tracing_perfetto/tests/Android.bp b/libs/tracing_perfetto/tests/Android.bp
new file mode 100644
index 0000000..a35b0e0
--- /dev/null
+++ b/libs/tracing_perfetto/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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"],
+}
+
+cc_test {
+ name: "libtracing_perfetto_tests",
+ static_libs: [
+ "libflagtest",
+ "libgmock",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "android.os.flags-aconfig-cc-host",
+ "libbase",
+ "libperfetto_c",
+ "libtracing_perfetto",
+ ],
+ srcs: [
+ "tracing_perfetto_test.cpp",
+ "utils.cpp",
+ ],
+ test_suites: ["device-tests"],
+}
diff --git a/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
new file mode 100644
index 0000000..7716b9a
--- /dev/null
+++ b/libs/tracing_perfetto/tests/tracing_perfetto_test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "tracing_perfetto.h"
+
+#include <thread>
+
+#include <android_os.h>
+#include <flag_macros.h>
+
+#include "gtest/gtest.h"
+#include "perfetto/public/abi/data_source_abi.h"
+#include "perfetto/public/abi/heap_buffer.h"
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/abi/tracing_session_abi.h"
+#include "perfetto/public/abi/track_event_abi.h"
+#include "perfetto/public/data_source.h"
+#include "perfetto/public/pb_decoder.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
+#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
+#include "perfetto/public/protos/trace/test_event.pzc.h"
+#include "perfetto/public/protos/trace/trace.pzc.h"
+#include "perfetto/public/protos/trace/trace_packet.pzc.h"
+#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
+#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
+#include "perfetto/public/protos/trace/trigger.pzc.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "trace_categories.h"
+#include "utils.h"
+
+namespace tracing_perfetto {
+
+using ::perfetto::shlib::test_utils::AllFieldsWithId;
+using ::perfetto::shlib::test_utils::FieldView;
+using ::perfetto::shlib::test_utils::IdFieldView;
+using ::perfetto::shlib::test_utils::MsgField;
+using ::perfetto::shlib::test_utils::PbField;
+using ::perfetto::shlib::test_utils::StringField;
+using ::perfetto::shlib::test_utils::TracingSession;
+using ::perfetto::shlib::test_utils::VarIntField;
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::UnorderedElementsAre;
+
+const auto PERFETTO_SDK_TRACING = ACONFIG_FLAG(android::os, perfetto_sdk_tracing);
+
+class TracingPerfettoTest : public testing::Test {
+ protected:
+ void SetUp() override {
+ tracing_perfetto::registerWithPerfetto(true /* test */);
+ }
+};
+
+// TODO(b/303199244): Add tests for all the library functions.
+
+TEST_F_WITH_FLAGS(TracingPerfettoTest, traceInstant,
+ REQUIRES_FLAGS_ENABLED(PERFETTO_SDK_TRACING)) {
+ TracingSession tracing_session =
+ TracingSession::Builder().set_data_source_name("track_event").Build();
+ tracing_perfetto::traceInstant(TRACE_CATEGORY_INPUT, "");
+
+ tracing_session.StopBlocking();
+ std::vector<uint8_t> data = tracing_session.ReadBlocking();
+ bool found = false;
+ for (struct PerfettoPbDecoderField trace_field : FieldView(data)) {
+ ASSERT_THAT(trace_field, PbField(perfetto_protos_Trace_packet_field_number,
+ MsgField(_)));
+ IdFieldView track_event(
+ trace_field, perfetto_protos_TracePacket_track_event_field_number);
+ if (track_event.size() == 0) {
+ continue;
+ }
+ found = true;
+ IdFieldView cat_iid_fields(
+ track_event.front(),
+ perfetto_protos_TrackEvent_category_iids_field_number);
+ ASSERT_THAT(cat_iid_fields, ElementsAre(VarIntField(_)));
+ uint64_t cat_iid = cat_iid_fields.front().value.integer64;
+ EXPECT_THAT(
+ trace_field,
+ AllFieldsWithId(
+ perfetto_protos_TracePacket_interned_data_field_number,
+ ElementsAre(AllFieldsWithId(
+ perfetto_protos_InternedData_event_categories_field_number,
+ ElementsAre(MsgField(UnorderedElementsAre(
+ PbField(perfetto_protos_EventCategory_iid_field_number,
+ VarIntField(cat_iid)),
+ PbField(perfetto_protos_EventCategory_name_field_number,
+ StringField("input")))))))));
+ }
+ EXPECT_TRUE(found);
+}
+
+} // namespace tracing_perfetto
\ No newline at end of file
diff --git a/libs/tracing_perfetto/tests/utils.cpp b/libs/tracing_perfetto/tests/utils.cpp
new file mode 100644
index 0000000..9c42028
--- /dev/null
+++ b/libs/tracing_perfetto/tests/utils.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.cc
+
+#include "utils.h"
+
+#include "perfetto/public/abi/heap_buffer.h"
+#include "perfetto/public/pb_msg.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/protos/config/data_source_config.pzc.h"
+#include "perfetto/public/protos/config/trace_config.pzc.h"
+#include "perfetto/public/protos/config/track_event/track_event_config.pzc.h"
+#include "perfetto/public/tracing_session.h"
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+namespace {
+
+std::string ToHexChars(uint8_t val) {
+ std::string ret;
+ uint8_t high_nibble = (val & 0xF0) >> 4;
+ uint8_t low_nibble = (val & 0xF);
+ static const char hex_chars[] = "0123456789ABCDEF";
+ ret.push_back(hex_chars[high_nibble]);
+ ret.push_back(hex_chars[low_nibble]);
+ return ret;
+}
+
+} // namespace
+
+TracingSession TracingSession::Builder::Build() {
+ struct PerfettoPbMsgWriter writer;
+ struct PerfettoHeapBuffer* hb = PerfettoHeapBufferCreate(&writer.writer);
+
+ struct perfetto_protos_TraceConfig cfg;
+ PerfettoPbMsgInit(&cfg.msg, &writer);
+
+ {
+ struct perfetto_protos_TraceConfig_BufferConfig buffers;
+ perfetto_protos_TraceConfig_begin_buffers(&cfg, &buffers);
+
+ perfetto_protos_TraceConfig_BufferConfig_set_size_kb(&buffers, 1024);
+
+ perfetto_protos_TraceConfig_end_buffers(&cfg, &buffers);
+ }
+
+ {
+ struct perfetto_protos_TraceConfig_DataSource data_sources;
+ perfetto_protos_TraceConfig_begin_data_sources(&cfg, &data_sources);
+
+ {
+ struct perfetto_protos_DataSourceConfig ds_cfg;
+ perfetto_protos_TraceConfig_DataSource_begin_config(&data_sources,
+ &ds_cfg);
+
+ perfetto_protos_DataSourceConfig_set_cstr_name(&ds_cfg,
+ data_source_name_.c_str());
+ if (!enabled_categories_.empty() && !disabled_categories_.empty()) {
+ perfetto_protos_TrackEventConfig te_cfg;
+ perfetto_protos_DataSourceConfig_begin_track_event_config(&ds_cfg,
+ &te_cfg);
+ for (const std::string& cat : enabled_categories_) {
+ perfetto_protos_TrackEventConfig_set_enabled_categories(
+ &te_cfg, cat.data(), cat.size());
+ }
+ for (const std::string& cat : disabled_categories_) {
+ perfetto_protos_TrackEventConfig_set_disabled_categories(
+ &te_cfg, cat.data(), cat.size());
+ }
+ perfetto_protos_DataSourceConfig_end_track_event_config(&ds_cfg,
+ &te_cfg);
+ }
+
+ perfetto_protos_TraceConfig_DataSource_end_config(&data_sources, &ds_cfg);
+ }
+
+ perfetto_protos_TraceConfig_end_data_sources(&cfg, &data_sources);
+ }
+ size_t cfg_size = PerfettoStreamWriterGetWrittenSize(&writer.writer);
+ std::unique_ptr<uint8_t[]> ser(new uint8_t[cfg_size]);
+ PerfettoHeapBufferCopyInto(hb, &writer.writer, ser.get(), cfg_size);
+ PerfettoHeapBufferDestroy(hb, &writer.writer);
+
+ struct PerfettoTracingSessionImpl* ts =
+ PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
+
+ PerfettoTracingSessionSetup(ts, ser.get(), cfg_size);
+
+ PerfettoTracingSessionStartBlocking(ts);
+
+ return TracingSession::Adopt(ts);
+}
+
+TracingSession TracingSession::Adopt(struct PerfettoTracingSessionImpl* session) {
+ TracingSession ret;
+ ret.session_ = session;
+ ret.stopped_ = std::make_unique<WaitableEvent>();
+ PerfettoTracingSessionSetStopCb(
+ ret.session_,
+ [](struct PerfettoTracingSessionImpl*, void* arg) {
+ static_cast<WaitableEvent*>(arg)->Notify();
+ },
+ ret.stopped_.get());
+ return ret;
+}
+
+TracingSession::TracingSession(TracingSession&& other) noexcept {
+ session_ = other.session_;
+ other.session_ = nullptr;
+ stopped_ = std::move(other.stopped_);
+ other.stopped_ = nullptr;
+}
+
+TracingSession::~TracingSession() {
+ if (!session_) {
+ return;
+ }
+ if (!stopped_->IsNotified()) {
+ PerfettoTracingSessionStopBlocking(session_);
+ stopped_->WaitForNotification();
+ }
+ PerfettoTracingSessionDestroy(session_);
+}
+
+bool TracingSession::FlushBlocking(uint32_t timeout_ms) {
+ WaitableEvent notification;
+ bool result;
+ auto* cb = new std::function<void(bool)>([&](bool success) {
+ result = success;
+ notification.Notify();
+ });
+ PerfettoTracingSessionFlushAsync(
+ session_, timeout_ms,
+ [](PerfettoTracingSessionImpl*, bool success, void* user_arg) {
+ auto* f = reinterpret_cast<std::function<void(bool)>*>(user_arg);
+ (*f)(success);
+ delete f;
+ },
+ cb);
+ notification.WaitForNotification();
+ return result;
+}
+
+void TracingSession::WaitForStopped() {
+ stopped_->WaitForNotification();
+}
+
+void TracingSession::StopBlocking() {
+ PerfettoTracingSessionStopBlocking(session_);
+}
+
+std::vector<uint8_t> TracingSession::ReadBlocking() {
+ std::vector<uint8_t> data;
+ PerfettoTracingSessionReadTraceBlocking(
+ session_,
+ [](struct PerfettoTracingSessionImpl*, const void* trace_data,
+ size_t size, bool, void* user_arg) {
+ auto& dst = *static_cast<std::vector<uint8_t>*>(user_arg);
+ auto* src = static_cast<const uint8_t*>(trace_data);
+ dst.insert(dst.end(), src, src + size);
+ },
+ &data);
+ return data;
+}
+
+} // namespace test_utils
+} // namespace shlib
+} // namespace perfetto
+
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream* pos) {
+ std::ostream& os = *pos;
+ PerfettoPbDecoderStatus status =
+ static_cast<PerfettoPbDecoderStatus>(field.status);
+ switch (status) {
+ case PERFETTO_PB_DECODER_ERROR:
+ os << "MALFORMED PROTOBUF";
+ break;
+ case PERFETTO_PB_DECODER_DONE:
+ os << "DECODER DONE";
+ break;
+ case PERFETTO_PB_DECODER_OK:
+ switch (field.wire_type) {
+ case PERFETTO_PB_WIRE_TYPE_DELIMITED:
+ os << "\"";
+ for (size_t i = 0; i < field.value.delimited.len; i++) {
+ os << perfetto::shlib::test_utils::ToHexChars(
+ field.value.delimited.start[i])
+ << " ";
+ }
+ os << "\"";
+ break;
+ case PERFETTO_PB_WIRE_TYPE_VARINT:
+ os << "varint: " << field.value.integer64;
+ break;
+ case PERFETTO_PB_WIRE_TYPE_FIXED32:
+ os << "fixed32: " << field.value.integer32;
+ break;
+ case PERFETTO_PB_WIRE_TYPE_FIXED64:
+ os << "fixed64: " << field.value.integer64;
+ break;
+ }
+ break;
+ }
+}
diff --git a/libs/tracing_perfetto/tests/utils.h b/libs/tracing_perfetto/tests/utils.h
new file mode 100644
index 0000000..4353554
--- /dev/null
+++ b/libs/tracing_perfetto/tests/utils.h
@@ -0,0 +1,452 @@
+/*
+ * 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.
+ */
+
+// Copied from //external/perfetto/src/shared_lib/test/utils.h
+
+#ifndef UTILS_H
+#define UTILS_H
+
+#include <cassert>
+#include <condition_variable>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <mutex>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock-more-matchers.h"
+#include "gtest/gtest-matchers.h"
+#include "gtest/gtest.h"
+#include "perfetto/public/abi/pb_decoder_abi.h"
+#include "perfetto/public/pb_utils.h"
+#include "perfetto/public/tracing_session.h"
+
+// Pretty printer for gtest
+void PrintTo(const PerfettoPbDecoderField& field, std::ostream*);
+
+namespace perfetto {
+namespace shlib {
+namespace test_utils {
+
+class WaitableEvent {
+ public:
+ WaitableEvent() = default;
+ void Notify() {
+ std::unique_lock<std::mutex> lock(m_);
+ notified_ = true;
+ cv_.notify_one();
+ }
+ bool WaitForNotification() {
+ std::unique_lock<std::mutex> lock(m_);
+ cv_.wait(lock, [this] { return notified_; });
+ return notified_;
+ }
+ bool IsNotified() {
+ std::unique_lock<std::mutex> lock(m_);
+ return notified_;
+ }
+
+ private:
+ std::mutex m_;
+ std::condition_variable cv_;
+ bool notified_ = false;
+};
+
+class TracingSession {
+ public:
+ class Builder {
+ public:
+ Builder() = default;
+ Builder& set_data_source_name(std::string data_source_name) {
+ data_source_name_ = std::move(data_source_name);
+ return *this;
+ }
+ Builder& add_enabled_category(std::string category) {
+ enabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ Builder& add_disabled_category(std::string category) {
+ disabled_categories_.push_back(std::move(category));
+ return *this;
+ }
+ TracingSession Build();
+
+ private:
+ std::string data_source_name_;
+ std::vector<std::string> enabled_categories_;
+ std::vector<std::string> disabled_categories_;
+ };
+
+ static TracingSession Adopt(struct PerfettoTracingSessionImpl*);
+
+ TracingSession(TracingSession&&) noexcept;
+
+ ~TracingSession();
+
+ struct PerfettoTracingSessionImpl* session() const {
+ return session_;
+ }
+
+ bool FlushBlocking(uint32_t timeout_ms);
+ void WaitForStopped();
+ void StopBlocking();
+ std::vector<uint8_t> ReadBlocking();
+
+ private:
+ TracingSession() = default;
+ struct PerfettoTracingSessionImpl* session_;
+ std::unique_ptr<WaitableEvent> stopped_;
+};
+
+template <typename FieldSkipper>
+class FieldViewBase {
+ public:
+ class Iterator {
+ public:
+ using iterator_category = std::input_iterator_tag;
+ using value_type = const PerfettoPbDecoderField;
+ using pointer = value_type;
+ using reference = value_type;
+ reference operator*() const {
+ struct PerfettoPbDecoder decoder;
+ decoder.read_ptr = read_ptr_;
+ decoder.end_ptr = end_ptr_;
+ struct PerfettoPbDecoderField field;
+ do {
+ field = PerfettoPbDecoderParseField(&decoder);
+ } while (field.status == PERFETTO_PB_DECODER_OK &&
+ skipper_.ShouldSkip(field));
+ return field;
+ }
+ Iterator& operator++() {
+ struct PerfettoPbDecoder decoder;
+ decoder.read_ptr = read_ptr_;
+ decoder.end_ptr = end_ptr_;
+ PerfettoPbDecoderSkipField(&decoder);
+ read_ptr_ = decoder.read_ptr;
+ AdvanceToFirstInterestingField();
+ return *this;
+ }
+ Iterator operator++(int) {
+ Iterator tmp = *this;
+ ++(*this);
+ return tmp;
+ }
+
+ friend bool operator==(const Iterator& a, const Iterator& b) {
+ return a.read_ptr_ == b.read_ptr_;
+ }
+ friend bool operator!=(const Iterator& a, const Iterator& b) {
+ return a.read_ptr_ != b.read_ptr_;
+ }
+
+ private:
+ Iterator(const uint8_t* read_ptr, const uint8_t* end_ptr,
+ const FieldSkipper& skipper)
+ : read_ptr_(read_ptr), end_ptr_(end_ptr), skipper_(skipper) {
+ AdvanceToFirstInterestingField();
+ }
+ void AdvanceToFirstInterestingField() {
+ struct PerfettoPbDecoder decoder;
+ decoder.read_ptr = read_ptr_;
+ decoder.end_ptr = end_ptr_;
+ struct PerfettoPbDecoderField field;
+ const uint8_t* prev_read_ptr;
+ do {
+ prev_read_ptr = decoder.read_ptr;
+ field = PerfettoPbDecoderParseField(&decoder);
+ } while (field.status == PERFETTO_PB_DECODER_OK &&
+ skipper_.ShouldSkip(field));
+ if (field.status == PERFETTO_PB_DECODER_OK) {
+ read_ptr_ = prev_read_ptr;
+ } else {
+ read_ptr_ = decoder.read_ptr;
+ }
+ }
+ friend class FieldViewBase<FieldSkipper>;
+ const uint8_t* read_ptr_;
+ const uint8_t* end_ptr_;
+ const FieldSkipper& skipper_;
+ };
+ using value_type = const PerfettoPbDecoderField;
+ using const_iterator = Iterator;
+ template <typename... Args>
+ explicit FieldViewBase(const uint8_t* begin, const uint8_t* end, Args... args)
+ : begin_(begin), end_(end), s_(args...) {
+ }
+ template <typename... Args>
+ explicit FieldViewBase(const std::vector<uint8_t>& data, Args... args)
+ : FieldViewBase(data.data(), data.data() + data.size(), args...) {
+ }
+ template <typename... Args>
+ explicit FieldViewBase(const struct PerfettoPbDecoderField& field,
+ Args... args)
+ : s_(args...) {
+ if (field.wire_type != PERFETTO_PB_WIRE_TYPE_DELIMITED) {
+ abort();
+ }
+ begin_ = field.value.delimited.start;
+ end_ = begin_ + field.value.delimited.len;
+ }
+ Iterator begin() const {
+ return Iterator(begin_, end_, s_);
+ }
+ Iterator end() const {
+ return Iterator(end_, end_, s_);
+ }
+ PerfettoPbDecoderField front() const {
+ return *begin();
+ }
+
+ size_t size() const {
+ size_t count = 0;
+ for (auto field : *this) {
+ (void)field;
+ count++;
+ }
+ return count;
+ }
+
+ bool ok() const {
+ for (auto field : *this) {
+ if (field.status != PERFETTO_PB_DECODER_OK) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private:
+ const uint8_t* begin_;
+ const uint8_t* end_;
+ FieldSkipper s_;
+};
+
+// Pretty printer for gtest
+template <typename FieldSkipper>
+void PrintTo(const FieldViewBase<FieldSkipper>& field_view, std::ostream* pos) {
+ std::ostream& os = *pos;
+ os << "{";
+ for (PerfettoPbDecoderField f : field_view) {
+ PrintTo(f, pos);
+ os << ", ";
+ }
+ os << "}";
+}
+
+class IdFieldSkipper {
+ public:
+ explicit IdFieldSkipper(uint32_t id) : id_(id) {
+ }
+ explicit IdFieldSkipper(int32_t id) : id_(static_cast<uint32_t>(id)) {
+ }
+ bool ShouldSkip(const struct PerfettoPbDecoderField& field) const {
+ return field.id != id_;
+ }
+
+ private:
+ uint32_t id_;
+};
+
+class NoFieldSkipper {
+ public:
+ NoFieldSkipper() = default;
+ bool ShouldSkip(const struct PerfettoPbDecoderField&) const {
+ return false;
+ }
+};
+
+// View over all the fields of a contiguous serialized protobuf message.
+//
+// Examples:
+//
+// for (struct PerfettoPbDecoderField field : FieldView(msg_begin, msg_end)) {
+// //...
+// }
+// FieldView fields2(/*PerfettoPbDecoderField*/ nested_field);
+// FieldView fields3(/*std::vector<uint8_t>*/ data);
+// size_t num = fields1.size(); // The number of fields.
+// bool ok = fields1.ok(); // Checks that the message is not malformed.
+using FieldView = FieldViewBase<NoFieldSkipper>;
+
+// Like `FieldView`, but only considers fields with a specific id.
+//
+// Examples:
+//
+// IdFieldView fields(msg_begin, msg_end, id)
+using IdFieldView = FieldViewBase<IdFieldSkipper>;
+
+// Matches a PerfettoPbDecoderField with the specified id. Accepts another
+// matcher to match the contents of the field.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, PbField(900, VarIntField(5)));
+template <typename M>
+auto PbField(int32_t id, M m) {
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::id, id), m);
+}
+
+// Matches a PerfettoPbDecoderField submessage field. Accepts a container
+// matcher for the subfields.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, MsgField(ElementsAre(...)));
+template <typename M>
+auto MsgField(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) { return FieldView(field); };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_DELIMITED),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField length delimited field. Accepts a string
+// matcher.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, StringField("string"));
+template <typename M>
+auto StringField(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return std::string(
+ reinterpret_cast<const char*>(field.value.delimited.start),
+ field.value.delimited.len);
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_DELIMITED),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField VarInt field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, VarIntField(1)));
+template <typename M>
+auto VarIntField(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return field.value.integer64;
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_VARINT),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField fixed64 field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, Fixed64Field(1)));
+template <typename M>
+auto Fixed64Field(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return field.value.integer64;
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_FIXED64),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField fixed32 field. Accepts an integer matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, Fixed32Field(1)));
+template <typename M>
+auto Fixed32Field(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return field.value.integer32;
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_FIXED32),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField double field. Accepts a double matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, DoubleField(1.0)));
+template <typename M>
+auto DoubleField(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return field.value.double_val;
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_FIXED64),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField float field. Accepts a float matcher
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, FloatField(1.0)));
+template <typename M>
+auto FloatField(M m) {
+ auto f = [](const PerfettoPbDecoderField& field) {
+ return field.value.float_val;
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_FIXED32),
+ testing::ResultOf(f, m));
+}
+
+// Matches a PerfettoPbDecoderField submessage field. Accepts a container
+// matcher for the subfields.
+//
+// Example:
+// PerfettoPbDecoderField field = ...
+// EXPECT_THAT(field, AllFieldsWithId(900, ElementsAre(...)));
+template <typename M>
+auto AllFieldsWithId(int32_t id, M m) {
+ auto f = [id](const PerfettoPbDecoderField& field) {
+ return IdFieldView(field, id);
+ };
+ return testing::AllOf(
+ testing::Field(&PerfettoPbDecoderField::status, PERFETTO_PB_DECODER_OK),
+ testing::Field(&PerfettoPbDecoderField::wire_type,
+ PERFETTO_PB_WIRE_TYPE_DELIMITED),
+ testing::ResultOf(f, m));
+}
+
+} // namespace test_utils
+} // namespace shlib
+} // namespace perfetto
+
+#endif // UTILS_H
diff --git a/libs/tracing_perfetto/tracing_perfetto.cpp b/libs/tracing_perfetto/tracing_perfetto.cpp
new file mode 100644
index 0000000..6f716ee
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "tracing_perfetto.h"
+
+#include <cutils/trace.h>
+
+#include "perfetto/public/te_category_macros.h"
+#include "trace_categories.h"
+#include "tracing_perfetto_internal.h"
+
+namespace tracing_perfetto {
+
+void registerWithPerfetto(bool test) {
+ internal::registerWithPerfetto(test);
+}
+
+Result traceBegin(uint64_t category, const char* name) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceBegin(*perfettoTeCategory, name);
+ } else {
+ atrace_begin(category, name);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceEnd(uint64_t category) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceEnd(*perfettoTeCategory);
+ } else {
+ atrace_end(category);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceAsyncBegin(uint64_t category, const char* name, int32_t cookie) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceAsyncBegin(*perfettoTeCategory, name, cookie);
+ } else {
+ atrace_async_begin(category, name, cookie);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceAsyncEnd(uint64_t category, const char* name, int32_t cookie) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceAsyncEnd(*perfettoTeCategory, name, cookie);
+ } else {
+ atrace_async_end(category, name, cookie);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceAsyncBeginForTrack(uint64_t category, const char* name,
+ const char* trackName, int32_t cookie) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceAsyncBeginForTrack(*perfettoTeCategory, name, trackName, cookie);
+ } else {
+ atrace_async_for_track_begin(category, trackName, name, cookie);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceAsyncEndForTrack(uint64_t category, const char* trackName,
+ int32_t cookie) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceAsyncEndForTrack(*perfettoTeCategory, trackName, cookie);
+ } else {
+ atrace_async_for_track_end(category, trackName, cookie);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceInstant(uint64_t category, const char* name) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceInstant(*perfettoTeCategory, name);
+ } else {
+ atrace_instant(category, name);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceInstantForTrack(uint64_t category, const char* trackName,
+ const char* name) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceInstantForTrack(*perfettoTeCategory, trackName, name);
+ } else {
+ atrace_instant_for_track(category, trackName, name);
+ return Result::SUCCESS;
+ }
+}
+
+Result traceCounter(uint64_t category, const char* name, int64_t value) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return internal::perfettoTraceCounter(*perfettoTeCategory, name, value);
+ } else {
+ atrace_int64(category, name, value);
+ return Result::SUCCESS;
+ }
+}
+
+bool isTagEnabled(uint64_t category) {
+ struct PerfettoTeCategory* perfettoTeCategory =
+ internal::toPerfettoCategory(category);
+ if (perfettoTeCategory != nullptr) {
+ return true;
+ } else {
+ return (atrace_get_enabled_tags() & category) != 0;
+ }
+}
+
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.cpp b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
new file mode 100644
index 0000000..758ace6
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define FRAMEWORK_CATEGORIES(C) \
+ C(always, "always", "Always category") \
+ C(graphics, "graphics", "Graphics category") \
+ C(input, "input", "Input category") \
+ C(view, "view", "View category") \
+ C(webview, "webview", "WebView category") \
+ C(windowmanager, "wm", "WindowManager category") \
+ C(activitymanager, "am", "ActivityManager category") \
+ C(syncmanager, "syncmanager", "SyncManager category") \
+ C(audio, "audio", "Audio category") \
+ C(video, "video", "Video category") \
+ C(camera, "camera", "Camera category") \
+ C(hal, "hal", "HAL category") \
+ C(app, "app", "App category") \
+ C(resources, "res", "Resources category") \
+ C(dalvik, "dalvik", "Dalvik category") \
+ C(rs, "rs", "RS category") \
+ C(bionic, "bionic", "Bionic category") \
+ C(power, "power", "Power category") \
+ C(packagemanager, "packagemanager", "PackageManager category") \
+ C(systemserver, "ss", "System Server category") \
+ C(database, "database", "Database category") \
+ C(network, "network", "Network category") \
+ C(adb, "adb", "ADB category") \
+ C(vibrator, "vibrator", "Vibrator category") \
+ C(aidl, "aidl", "AIDL category") \
+ C(nnapi, "nnapi", "NNAPI category") \
+ C(rro, "rro", "RRO category") \
+ C(thermal, "thermal", "Thermal category")
+
+#include "tracing_perfetto_internal.h"
+
+#include <inttypes.h>
+
+#include <mutex>
+
+#include <android_os.h>
+
+#include "perfetto/public/compiler.h"
+#include "perfetto/public/producer.h"
+#include "perfetto/public/te_category_macros.h"
+#include "perfetto/public/te_macros.h"
+#include "perfetto/public/track_event.h"
+#include "trace_categories.h"
+#include "trace_result.h"
+
+namespace tracing_perfetto {
+
+namespace internal {
+
+namespace {
+
+PERFETTO_TE_CATEGORIES_DECLARE(FRAMEWORK_CATEGORIES);
+
+PERFETTO_TE_CATEGORIES_DEFINE(FRAMEWORK_CATEGORIES);
+
+std::atomic_bool is_perfetto_registered = false;
+
+struct PerfettoTeCategory* toCategory(uint64_t inCategory) {
+ switch (inCategory) {
+ case TRACE_CATEGORY_ALWAYS:
+ return &always;
+ case TRACE_CATEGORY_GRAPHICS:
+ return &graphics;
+ case TRACE_CATEGORY_INPUT:
+ return &input;
+ case TRACE_CATEGORY_VIEW:
+ return &view;
+ case TRACE_CATEGORY_WEBVIEW:
+ return &webview;
+ case TRACE_CATEGORY_WINDOW_MANAGER:
+ return &windowmanager;
+ case TRACE_CATEGORY_ACTIVITY_MANAGER:
+ return &activitymanager;
+ case TRACE_CATEGORY_SYNC_MANAGER:
+ return &syncmanager;
+ case TRACE_CATEGORY_AUDIO:
+ return &audio;
+ case TRACE_CATEGORY_VIDEO:
+ return &video;
+ case TRACE_CATEGORY_CAMERA:
+ return &camera;
+ case TRACE_CATEGORY_HAL:
+ return &hal;
+ case TRACE_CATEGORY_APP:
+ return &app;
+ case TRACE_CATEGORY_RESOURCES:
+ return &resources;
+ case TRACE_CATEGORY_DALVIK:
+ return &dalvik;
+ case TRACE_CATEGORY_RS:
+ return &rs;
+ case TRACE_CATEGORY_BIONIC:
+ return &bionic;
+ case TRACE_CATEGORY_POWER:
+ return &power;
+ case TRACE_CATEGORY_PACKAGE_MANAGER:
+ return &packagemanager;
+ case TRACE_CATEGORY_SYSTEM_SERVER:
+ return &systemserver;
+ case TRACE_CATEGORY_DATABASE:
+ return &database;
+ case TRACE_CATEGORY_NETWORK:
+ return &network;
+ case TRACE_CATEGORY_ADB:
+ return &adb;
+ case TRACE_CATEGORY_VIBRATOR:
+ return &vibrator;
+ case TRACE_CATEGORY_AIDL:
+ return &aidl;
+ case TRACE_CATEGORY_NNAPI:
+ return &nnapi;
+ case TRACE_CATEGORY_RRO:
+ return &rro;
+ case TRACE_CATEGORY_THERMAL:
+ return &thermal;
+ default:
+ return nullptr;
+ }
+}
+
+} // namespace
+
+bool isPerfettoRegistered() {
+ return is_perfetto_registered;
+}
+
+struct PerfettoTeCategory* toPerfettoCategory(uint64_t category) {
+ struct PerfettoTeCategory* perfettoCategory = toCategory(category);
+ if (perfettoCategory == nullptr) {
+ return nullptr;
+ }
+
+ bool enabled = PERFETTO_UNLIKELY(PERFETTO_ATOMIC_LOAD_EXPLICIT(
+ (*perfettoCategory).enabled, PERFETTO_MEMORY_ORDER_RELAXED));
+ return enabled ? perfettoCategory : nullptr;
+}
+
+void registerWithPerfetto(bool test) {
+ if (!android::os::perfetto_sdk_tracing()) {
+ return;
+ }
+
+ static std::once_flag registration;
+ std::call_once(registration, [test]() {
+ struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
+ args.backends = test ? PERFETTO_BACKEND_IN_PROCESS : PERFETTO_BACKEND_SYSTEM;
+ PerfettoProducerInit(args);
+ PerfettoTeInit();
+ PERFETTO_TE_REGISTER_CATEGORIES(FRAMEWORK_CATEGORIES);
+ is_perfetto_registered = true;
+ });
+}
+
+Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name) {
+ PERFETTO_TE(category, PERFETTO_TE_SLICE_BEGIN(name));
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceEnd(const struct PerfettoTeCategory& category) {
+ PERFETTO_TE(category, PERFETTO_TE_SLICE_END());
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+ const char* trackName, uint64_t cookie) {
+ PERFETTO_TE(
+ category, PERFETTO_TE_SLICE_BEGIN(name),
+ PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+ const char* trackName, uint64_t cookie) {
+ PERFETTO_TE(
+ category, PERFETTO_TE_SLICE_END(),
+ PERFETTO_TE_NAMED_TRACK(trackName, cookie, PerfettoTeProcessTrackUuid()));
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+ uint64_t cookie) {
+ return perfettoTraceAsyncBeginForTrack(category, name, name, cookie);
+}
+
+Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+ uint64_t cookie) {
+ return perfettoTraceAsyncEndForTrack(category, name, cookie);
+}
+
+Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name) {
+ PERFETTO_TE(category, PERFETTO_TE_INSTANT(name));
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+ const char* trackName, const char* name) {
+ PERFETTO_TE(
+ category, PERFETTO_TE_INSTANT(name),
+ PERFETTO_TE_NAMED_TRACK(trackName, 1, PerfettoTeProcessTrackUuid()));
+ return Result::SUCCESS;
+}
+
+Result perfettoTraceCounter(const struct PerfettoTeCategory& category,
+ [[maybe_unused]] const char* name, int64_t value) {
+ PERFETTO_TE(category, PERFETTO_TE_COUNTER(),
+ PERFETTO_TE_INT_COUNTER(value));
+ return Result::SUCCESS;
+}
+
+uint64_t getDefaultCategories() {
+ return TRACE_CATEGORIES;
+}
+
+} // namespace internal
+
+} // namespace tracing_perfetto
diff --git a/libs/tracing_perfetto/tracing_perfetto_internal.h b/libs/tracing_perfetto/tracing_perfetto_internal.h
new file mode 100644
index 0000000..79e4b8f
--- /dev/null
+++ b/libs/tracing_perfetto/tracing_perfetto_internal.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TRACING_PERFETTO_INTERNAL_H
+#define TRACING_PERFETTO_INTERNAL_H
+
+#include <stdint.h>
+
+#include "include/trace_result.h"
+#include "perfetto/public/te_category_macros.h"
+
+namespace tracing_perfetto {
+
+namespace internal {
+
+bool isPerfettoRegistered();
+
+struct PerfettoTeCategory* toPerfettoCategory(uint64_t category);
+
+void registerWithPerfetto(bool test = false);
+
+Result perfettoTraceBegin(const struct PerfettoTeCategory& category, const char* name);
+
+Result perfettoTraceEnd(const struct PerfettoTeCategory& category);
+
+Result perfettoTraceAsyncBegin(const struct PerfettoTeCategory& category, const char* name,
+ uint64_t cookie);
+
+Result perfettoTraceAsyncEnd(const struct PerfettoTeCategory& category, const char* name,
+ uint64_t cookie);
+
+Result perfettoTraceAsyncBeginForTrack(const struct PerfettoTeCategory& category, const char* name,
+ const char* trackName, uint64_t cookie);
+
+Result perfettoTraceAsyncEndForTrack(const struct PerfettoTeCategory& category,
+ const char* trackName, uint64_t cookie);
+
+Result perfettoTraceInstant(const struct PerfettoTeCategory& category, const char* name);
+
+Result perfettoTraceInstantForTrack(const struct PerfettoTeCategory& category,
+ const char* trackName, const char* name);
+
+Result perfettoTraceCounter(const struct PerfettoTeCategory& category, const char* name,
+ int64_t value);
+
+uint64_t getDefaultCategories();
+
+} // namespace internal
+
+} // namespace tracing_perfetto
+
+#endif // TRACING_PERFETTO_INTERNAL_H
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 312a1e6..12230f9 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -277,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 ed7f193..e5af740 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;
@@ -399,13 +345,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/DisplayId.h b/libs/ui/include/ui/DisplayId.h
index 3a31fa0..8a14db8 100644
--- a/libs/ui/include/ui/DisplayId.h
+++ b/libs/ui/include/ui/DisplayId.h
@@ -20,6 +20,7 @@
#include <ostream>
#include <string>
+#include <ftl/hash.h>
#include <ftl/optional.h>
namespace android {
@@ -38,6 +39,9 @@
constexpr DisplayId(const DisplayId&) = default;
DisplayId& operator=(const DisplayId&) = default;
+ constexpr bool isVirtual() const { return value & FLAG_VIRTUAL; }
+ constexpr bool isStable() const { return value & FLAG_STABLE; }
+
uint64_t value;
// For deserialization.
@@ -76,10 +80,10 @@
// DisplayId of a physical display, such as the internal display or externally connected display.
struct PhysicalDisplayId : DisplayId {
static constexpr ftl::Optional<PhysicalDisplayId> tryCast(DisplayId id) {
- if (id.value & FLAG_VIRTUAL) {
+ if (id.isVirtual()) {
return std::nullopt;
}
- return {PhysicalDisplayId(id)};
+ return PhysicalDisplayId(id);
}
// Returns a stable ID based on EDID information.
@@ -117,25 +121,23 @@
static constexpr uint64_t FLAG_GPU = 1ULL << 61;
static constexpr std::optional<VirtualDisplayId> tryCast(DisplayId id) {
- if (id.value & FLAG_VIRTUAL) {
- return {VirtualDisplayId(id)};
+ if (id.isVirtual()) {
+ return VirtualDisplayId(id);
}
return std::nullopt;
}
protected:
- constexpr VirtualDisplayId(uint64_t flags, BaseId baseId)
- : DisplayId(DisplayId::FLAG_VIRTUAL | flags | baseId) {}
-
+ explicit constexpr VirtualDisplayId(uint64_t value) : DisplayId(FLAG_VIRTUAL | value) {}
explicit constexpr VirtualDisplayId(DisplayId other) : DisplayId(other) {}
};
struct HalVirtualDisplayId : VirtualDisplayId {
- explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(0, baseId) {}
+ explicit constexpr HalVirtualDisplayId(BaseId baseId) : VirtualDisplayId(baseId) {}
static constexpr std::optional<HalVirtualDisplayId> tryCast(DisplayId id) {
- if ((id.value & FLAG_VIRTUAL) && !(id.value & VirtualDisplayId::FLAG_GPU)) {
- return {HalVirtualDisplayId(id)};
+ if (id.isVirtual() && !(id.value & FLAG_GPU)) {
+ return HalVirtualDisplayId(id);
}
return std::nullopt;
}
@@ -145,17 +147,27 @@
};
struct GpuVirtualDisplayId : VirtualDisplayId {
- explicit constexpr GpuVirtualDisplayId(BaseId baseId)
- : VirtualDisplayId(VirtualDisplayId::FLAG_GPU, baseId) {}
+ explicit constexpr GpuVirtualDisplayId(BaseId baseId) : VirtualDisplayId(FLAG_GPU | baseId) {}
+
+ static constexpr std::optional<GpuVirtualDisplayId> fromUniqueId(const std::string& uniqueId) {
+ if (const auto hashOpt = ftl::stable_hash(uniqueId)) {
+ return GpuVirtualDisplayId(HashTag{}, *hashOpt);
+ }
+ return {};
+ }
static constexpr std::optional<GpuVirtualDisplayId> tryCast(DisplayId id) {
- if ((id.value & FLAG_VIRTUAL) && (id.value & VirtualDisplayId::FLAG_GPU)) {
- return {GpuVirtualDisplayId(id)};
+ if (id.isVirtual() && (id.value & FLAG_GPU)) {
+ return GpuVirtualDisplayId(id);
}
return std::nullopt;
}
private:
+ struct HashTag {}; // Disambiguate with BaseId constructor.
+ constexpr GpuVirtualDisplayId(HashTag, uint64_t hash)
+ : VirtualDisplayId(FLAG_STABLE | FLAG_GPU | hash) {}
+
explicit constexpr GpuVirtualDisplayId(DisplayId other) : VirtualDisplayId(other) {}
};
@@ -169,7 +181,7 @@
if (GpuVirtualDisplayId::tryCast(id)) {
return std::nullopt;
}
- return {HalDisplayId(id)};
+ return HalDisplayId(id);
}
private:
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..fd84b12
--- /dev/null
+++ b/libs/ui/include/ui/LogicalDisplayId.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 <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()); }
+
+ static const LogicalDisplayId INVALID;
+ static const LogicalDisplayId DEFAULT;
+};
+
+constexpr inline LogicalDisplayId LogicalDisplayId::INVALID{-1};
+constexpr inline LogicalDisplayId LogicalDisplayId::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/DisplayId_test.cpp b/libs/ui/tests/DisplayId_test.cpp
index 8ddee7e..ef686df 100644
--- a/libs/ui/tests/DisplayId_test.cpp
+++ b/libs/ui/tests/DisplayId_test.cpp
@@ -24,7 +24,7 @@
constexpr uint8_t port = 1;
constexpr uint16_t manufacturerId = 13;
constexpr uint32_t modelHash = 42;
- PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
+ const PhysicalDisplayId id = PhysicalDisplayId::fromEdid(port, manufacturerId, modelHash);
EXPECT_EQ(port, id.getPort());
EXPECT_EQ(manufacturerId, id.getManufacturerId());
EXPECT_FALSE(VirtualDisplayId::tryCast(id));
@@ -39,7 +39,7 @@
TEST(DisplayIdTest, createPhysicalIdFromPort) {
constexpr uint8_t port = 3;
- PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
+ const PhysicalDisplayId id = PhysicalDisplayId::fromPort(port);
EXPECT_EQ(port, id.getPort());
EXPECT_FALSE(VirtualDisplayId::tryCast(id));
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
@@ -52,7 +52,34 @@
}
TEST(DisplayIdTest, createGpuVirtualId) {
- GpuVirtualDisplayId id(42);
+ const GpuVirtualDisplayId id(42);
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+ EXPECT_EQ(id, DisplayId::fromValue(id.value));
+ EXPECT_EQ(id, DisplayId::fromValue<GpuVirtualDisplayId>(id.value));
+}
+
+TEST(DisplayIdTest, createVirtualIdFromGpuVirtualId) {
+ const VirtualDisplayId id(GpuVirtualDisplayId(42));
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_FALSE(HalDisplayId::tryCast(id));
+
+ const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
+ EXPECT_EQ((id.isVirtual() && isGpuVirtualId), GpuVirtualDisplayId::tryCast(id).has_value());
+}
+
+TEST(DisplayIdTest, createGpuVirtualIdFromUniqueId) {
+ static const std::string kUniqueId("virtual:ui:DisplayId_test");
+ const auto idOpt = GpuVirtualDisplayId::fromUniqueId(kUniqueId);
+ ASSERT_TRUE(idOpt.has_value());
+ const GpuVirtualDisplayId id = idOpt.value();
EXPECT_TRUE(VirtualDisplayId::tryCast(id));
EXPECT_TRUE(GpuVirtualDisplayId::tryCast(id));
EXPECT_FALSE(HalVirtualDisplayId::tryCast(id));
@@ -64,7 +91,7 @@
}
TEST(DisplayIdTest, createHalVirtualId) {
- HalVirtualDisplayId id(42);
+ const HalVirtualDisplayId id(42);
EXPECT_TRUE(VirtualDisplayId::tryCast(id));
EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
@@ -75,4 +102,16 @@
EXPECT_EQ(id, DisplayId::fromValue<HalVirtualDisplayId>(id.value));
}
+TEST(DisplayIdTest, createVirtualIdFromHalVirtualId) {
+ const VirtualDisplayId id(HalVirtualDisplayId(42));
+ EXPECT_TRUE(VirtualDisplayId::tryCast(id));
+ EXPECT_TRUE(HalVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(GpuVirtualDisplayId::tryCast(id));
+ EXPECT_FALSE(PhysicalDisplayId::tryCast(id));
+ EXPECT_TRUE(HalDisplayId::tryCast(id));
+
+ const bool isGpuVirtualId = (id.value & VirtualDisplayId::FLAG_GPU);
+ EXPECT_EQ((id.isVirtual() && !isGpuVirtualId), HalVirtualDisplayId::tryCast(id).has_value());
+}
+
} // namespace android::ui
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 3297fe0..bf0e38e 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 a481c62..fadb1fd 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -100,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/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/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..6b2627c 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::LogicalDisplayId::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 36e133b..00dd6ba 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -17,7 +17,13 @@
#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/Keyboard.h>
#include <input/PrintTools.h>
+#include <unordered_set>
#include "PointerChoreographer.h"
@@ -58,8 +64,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;
}
@@ -79,22 +86,70 @@
}
}
+// filters and returns a set of privacy sensitive displays that are currently visible.
+std::unordered_set<ui::LogicalDisplayId> getPrivacySensitiveDisplaysFromWindowInfos(
+ const std::vector<gui::WindowInfo>& windowInfos) {
+ std::unordered_set<ui::LogicalDisplayId> privacySensitiveDisplays;
+ for (const auto& windowInfo : windowInfos) {
+ if (!windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE) &&
+ windowInfo.inputConfig.test(gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY)) {
+ privacySensitiveDisplays.insert(windowInfo.displayId);
+ }
+ }
+ return privacySensitiveDisplays;
+}
+
} // namespace
// --- PointerChoreographer ---
-PointerChoreographer::PointerChoreographer(InputListenerInterface& listener,
+PointerChoreographer::PointerChoreographer(InputListenerInterface& inputListener,
PointerChoreographerPolicyInterface& policy)
+ : PointerChoreographer(
+ inputListener, policy,
+ [](const sp<android::gui::WindowInfosListener>& listener) {
+ auto initialInfo = std::make_pair(std::vector<android::gui::WindowInfo>{},
+ std::vector<android::gui::DisplayInfo>{});
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->addWindowInfosListener(listener,
+ &initialInfo);
+#endif
+ return initialInfo.first;
+ },
+ [](const sp<android::gui::WindowInfosListener>& listener) {
+#if defined(__ANDROID__)
+ SurfaceComposerClient::getDefault()->removeWindowInfosListener(listener);
+#endif
+ }) {
+}
+
+PointerChoreographer::PointerChoreographer(
+ android::InputListenerInterface& listener,
+ android::PointerChoreographerPolicyInterface& policy,
+ const android::PointerChoreographer::WindowListenerRegisterConsumer& registerListener,
+ const android::PointerChoreographer::WindowListenerUnregisterConsumer& unregisterListener)
: mTouchControllerConstructor([this]() {
return mPolicy.createPointerController(
PointerControllerInterface::ControllerType::TOUCH);
}),
mNextListener(listener),
mPolicy(policy),
- mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT),
- mNotifiedPointerDisplayId(ADISPLAY_ID_NONE),
+ mDefaultMouseDisplayId(ui::LogicalDisplayId::DEFAULT),
+ mNotifiedPointerDisplayId(ui::LogicalDisplayId::INVALID),
mShowTouchesEnabled(false),
- mStylusPointerIconEnabled(false) {}
+ mStylusPointerIconEnabled(false),
+ mCurrentFocusedDisplay(ui::LogicalDisplayId::DEFAULT),
+ mRegisterListener(registerListener),
+ mUnregisterListener(unregisterListener) {}
+
+PointerChoreographer::~PointerChoreographer() {
+ std::scoped_lock _l(mLock);
+ if (mWindowInfoListener == nullptr) {
+ return;
+ }
+ mWindowInfoListener->onPointerChoreographerDestroyed();
+ mUnregisterListener(mWindowInfoListener);
+}
void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) {
PointerDisplayChange pointerDisplayChange;
@@ -115,6 +170,7 @@
}
void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) {
+ fadeMouseCursorOnKeyPress(args);
mNextListener.notify(args);
}
@@ -124,6 +180,32 @@
mNextListener.notify(newArgs);
}
+void PointerChoreographer::fadeMouseCursorOnKeyPress(const android::NotifyKeyArgs& args) {
+ if (args.action == AKEY_EVENT_ACTION_UP || isMetaKey(args.keyCode)) {
+ return;
+ }
+ // Meta state for these keys is ignored for dismissing cursor while typing
+ constexpr static int32_t ALLOW_FADING_META_STATE_MASK = AMETA_CAPS_LOCK_ON | AMETA_NUM_LOCK_ON |
+ AMETA_SCROLL_LOCK_ON | AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON | AMETA_SHIFT_ON;
+ if (args.metaState & ~ALLOW_FADING_META_STATE_MASK) {
+ // Do not fade if any other meta state is active
+ return;
+ }
+ if (!mPolicy.isInputMethodConnectionActive()) {
+ return;
+ }
+
+ std::scoped_lock _l(mLock);
+ ui::LogicalDisplayId targetDisplay = args.displayId;
+ if (targetDisplay == ui::LogicalDisplayId::INVALID) {
+ targetDisplay = mCurrentFocusedDisplay;
+ }
+ auto it = mMousePointersByDisplay.find(targetDisplay);
+ if (it != mMousePointersByDisplay.end()) {
+ it->second->fade(PointerControllerInterface::Transition::GRADUAL);
+ }
+}
+
NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) {
std::scoped_lock _l(mLock);
@@ -147,26 +229,39 @@
<< args.dump();
}
+ mMouseDevices.emplace(args.deviceId);
auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
+ NotifyMotionArgs newArgs(args);
+ newArgs.displayId = displayId;
- const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- pc.move(deltaX, deltaY);
+ if (MotionEvent::isValidCursorPosition(args.xCursorPosition, args.yCursorPosition)) {
+ // This is an absolute mouse device that knows about the location of the cursor on the
+ // display, so set the cursor position to the specified location.
+ const auto [x, y] = pc.getPosition();
+ const float deltaX = args.xCursorPosition - x;
+ const float deltaY = args.yCursorPosition - y;
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
+ pc.setPosition(args.xCursorPosition, args.yCursorPosition);
+ } else {
+ // This is a relative mouse, so move the cursor by the specified amount.
+ const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ pc.move(deltaX, deltaY);
+ const auto [x, y] = pc.getPosition();
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
+ newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+ newArgs.xCursorPosition = x;
+ newArgs.yCursorPosition = y;
+ }
if (canUnfadeOnDisplay(displayId)) {
pc.unfade(PointerControllerInterface::Transition::IMMEDIATE);
}
-
- const auto [x, y] = pc.getPosition();
- NotifyMotionArgs newArgs(args);
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- newArgs.xCursorPosition = x;
- newArgs.yCursorPosition = y;
- newArgs.displayId = displayId;
return newArgs;
}
NotifyMotionArgs PointerChoreographer::processTouchpadEventLocked(const NotifyMotionArgs& args) {
+ mMouseDevices.emplace(args.deviceId);
auto [displayId, pc] = ensureMouseControllerLocked(args.displayId);
NotifyMotionArgs newArgs(args);
@@ -205,7 +300,7 @@
}
void PointerChoreographer::processDrawingTabletEventLocked(const android::NotifyMotionArgs& args) {
- if (args.displayId == ADISPLAY_ID_NONE) {
+ if (args.displayId == ui::LogicalDisplayId::INVALID) {
return;
}
@@ -215,9 +310,13 @@
}
// Use a mouse pointer controller for drawing tablets, or create one if it doesn't exist.
- auto [it, _] = mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
- getMouseControllerConstructor(
- args.displayId));
+ auto [it, controllerAdded] =
+ mDrawingTabletPointersByDevice.try_emplace(args.deviceId,
+ getMouseControllerConstructor(
+ args.displayId));
+ if (controllerAdded) {
+ onControllerAddedOrRemovedLocked();
+ }
PointerControllerInterface& pc = *it->second;
@@ -241,7 +340,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;
}
@@ -255,7 +354,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) {
+ onControllerAddedOrRemovedLocked();
+ }
PointerControllerInterface& pc = *it->second;
@@ -280,7 +383,7 @@
}
void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& args) {
- if (args.displayId == ADISPLAY_ID_NONE) {
+ if (!args.displayId.isValid()) {
return;
}
@@ -290,9 +393,12 @@
}
// Get the stylus pointer controller for the device, or create one if it doesn't exist.
- auto [it, _] =
+ auto [it, controllerAdded] =
mStylusPointersByDevice.try_emplace(args.deviceId,
getStylusControllerConstructor(args.displayId));
+ if (controllerAdded) {
+ onControllerAddedOrRemovedLocked();
+ }
PointerControllerInterface& pc = *it->second;
@@ -332,11 +438,63 @@
mTouchPointersByDevice.erase(args.deviceId);
mStylusPointersByDevice.erase(args.deviceId);
mDrawingTabletPointersByDevice.erase(args.deviceId);
+ onControllerAddedOrRemovedLocked();
+}
+
+void PointerChoreographer::onControllerAddedOrRemovedLocked() {
+ if (!com::android::input::flags::hide_pointer_indicators_for_secure_windows()) {
+ return;
+ }
+ bool requireListener = !mTouchPointersByDevice.empty() || !mMousePointersByDisplay.empty() ||
+ !mDrawingTabletPointersByDevice.empty() || !mStylusPointersByDevice.empty();
+
+ if (requireListener && mWindowInfoListener == nullptr) {
+ mWindowInfoListener = sp<PointerChoreographerDisplayInfoListener>::make(this);
+ mWindowInfoListener->setInitialDisplayInfos(mRegisterListener(mWindowInfoListener));
+ onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
+ } else if (!requireListener && mWindowInfoListener != nullptr) {
+ mUnregisterListener(mWindowInfoListener);
+ mWindowInfoListener = nullptr;
+ } else if (requireListener && mWindowInfoListener != nullptr) {
+ // controller may have been added to an existing privacy sensitive display, we need to
+ // update all controllers again
+ onPrivacySensitiveDisplaysChangedLocked(mWindowInfoListener->getPrivacySensitiveDisplays());
+ }
+}
+
+void PointerChoreographer::onPrivacySensitiveDisplaysChangedLocked(
+ const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
+ for (auto& [_, pc] : mTouchPointersByDevice) {
+ pc->clearSkipScreenshotFlags();
+ for (auto displayId : privacySensitiveDisplays) {
+ pc->setSkipScreenshotFlagForDisplay(displayId);
+ }
+ }
+
+ for (auto& [displayId, pc] : mMousePointersByDisplay) {
+ if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
+ pc->setSkipScreenshotFlagForDisplay(displayId);
+ } else {
+ pc->clearSkipScreenshotFlags();
+ }
+ }
+
+ for (auto* pointerControllerByDevice :
+ {&mDrawingTabletPointersByDevice, &mStylusPointersByDevice}) {
+ for (auto& [_, pc] : *pointerControllerByDevice) {
+ auto displayId = pc->getDisplayId();
+ if (privacySensitiveDisplays.find(displayId) != privacySensitiveDisplays.end()) {
+ pc->setSkipScreenshotFlagForDisplay(displayId);
+ } else {
+ pc->clearSkipScreenshotFlags();
+ }
+ }
+ }
}
void PointerChoreographer::notifyPointerCaptureChanged(
const NotifyPointerCaptureChangedArgs& args) {
- if (args.request.enable) {
+ if (args.request.isEnable()) {
std::scoped_lock _l(mLock);
for (const auto& [_, mousePointerController] : mMousePointersByDisplay) {
mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
@@ -345,6 +503,12 @@
mNextListener.notify(args);
}
+void PointerChoreographer::onPrivacySensitiveDisplaysChanged(
+ const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays) {
+ std::scoped_lock _l(mLock);
+ onPrivacySensitiveDisplaysChangedLocked(privacySensitiveDisplays);
+}
+
void PointerChoreographer::dump(std::string& dump) {
std::scoped_lock _l(mLock);
@@ -356,7 +520,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) {
@@ -376,7 +540,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;
@@ -385,17 +550,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;
+ onControllerAddedOrRemovedLocked();
+ }
return {displayId, *it->second};
}
@@ -406,12 +575,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;
@@ -419,30 +588,42 @@
// 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)) {
+ if (isNewMousePointer) {
+ onControllerAddedOrRemovedLocked();
+ }
+
+ 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());
}
}
@@ -466,13 +647,15 @@
mInputDeviceInfos.end();
});
+ onControllerAddedOrRemovedLocked();
+
// 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::LogicalDisplayId::INVALID;
FloatPoint cursorPosition = {0, 0};
if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId);
it != mMousePointersByDisplay.end()) {
@@ -490,7 +673,7 @@
return {{displayIdToNotify, cursorPosition}};
}
-void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) {
+void PointerChoreographer::setDefaultMouseDisplayId(ui::LogicalDisplayId displayId) {
PointerDisplayChange pointerDisplayChange;
{ // acquire lock
@@ -509,7 +692,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);
@@ -535,18 +718,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();
@@ -585,8 +768,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.";
@@ -630,7 +813,7 @@
return false;
}
-void PointerChoreographer::setPointerIconVisibility(int32_t displayId, bool visible) {
+void PointerChoreographer::setPointerIconVisibility(ui::LogicalDisplayId displayId, bool visible) {
std::scoped_lock lock(mLock);
if (visible) {
mDisplaysWithPointersHidden.erase(displayId);
@@ -652,8 +835,13 @@
}
}
+void PointerChoreographer::setFocusedDisplay(ui::LogicalDisplayId displayId) {
+ std::scoped_lock lock(mLock);
+ mCurrentFocusedDisplay = displayId;
+}
+
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(
@@ -667,7 +855,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(
@@ -680,4 +868,36 @@
return ConstructorDelegate(std::move(ctor));
}
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::onWindowInfosChanged(
+ const gui::WindowInfosUpdate& windowInfosUpdate) {
+ std::scoped_lock _l(mListenerLock);
+ if (mPointerChoreographer == nullptr) {
+ return;
+ }
+ auto newPrivacySensitiveDisplays =
+ getPrivacySensitiveDisplaysFromWindowInfos(windowInfosUpdate.windowInfos);
+ if (newPrivacySensitiveDisplays != mPrivacySensitiveDisplays) {
+ mPrivacySensitiveDisplays = std::move(newPrivacySensitiveDisplays);
+ mPointerChoreographer->onPrivacySensitiveDisplaysChanged(mPrivacySensitiveDisplays);
+ }
+}
+
+void PointerChoreographer::PointerChoreographerDisplayInfoListener::setInitialDisplayInfos(
+ const std::vector<gui::WindowInfo>& windowInfos) {
+ std::scoped_lock _l(mListenerLock);
+ mPrivacySensitiveDisplays = getPrivacySensitiveDisplaysFromWindowInfos(windowInfos);
+}
+
+std::unordered_set<ui::LogicalDisplayId /*displayId*/>
+PointerChoreographer::PointerChoreographerDisplayInfoListener::getPrivacySensitiveDisplays() {
+ std::scoped_lock _l(mListenerLock);
+ return mPrivacySensitiveDisplays;
+}
+
+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..aaf1e3e 100644
--- a/services/inputflinger/PointerChoreographer.h
+++ b/services/inputflinger/PointerChoreographer.h
@@ -21,7 +21,9 @@
#include "PointerChoreographerPolicyInterface.h"
#include <android-base/thread_annotations.h>
+#include <gui/WindowInfosListener.h>
#include <type_traits>
+#include <unordered_set>
namespace android {
@@ -53,11 +55,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::LogicalDisplayId::INVALID) = 0;
+ virtual FloatPoint getMouseCursorPosition(ui::LogicalDisplayId displayId) = 0;
virtual void setShowTouchesEnabled(bool enabled) = 0;
virtual void setStylusPointerIconEnabled(bool enabled) = 0;
/**
@@ -66,12 +68,17 @@
* 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;
+
+ /**
+ * Used by Dispatcher to notify changes in the current focused display.
+ */
+ virtual void setFocusedDisplay(ui::LogicalDisplayId displayId) = 0;
/**
* This method may be called on any thread (usually by the input manager on a binder thread).
@@ -83,18 +90,19 @@
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 setFocusedDisplay(ui::LogicalDisplayId displayId) override;
void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override;
void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override;
@@ -109,17 +117,20 @@
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);
+ void fadeMouseCursorOnKeyPress(const NotifyKeyArgs& args);
NotifyMotionArgs processMotion(const NotifyMotionArgs& args);
NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
NotifyMotionArgs processTouchpadEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
@@ -127,20 +138,52 @@
void processTouchscreenAndStylusEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processStylusHoverEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock);
void processDeviceReset(const NotifyDeviceResetArgs& args);
+ void onControllerAddedOrRemovedLocked() REQUIRES(mLock);
+ void onPrivacySensitiveDisplaysChangedLocked(
+ const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays)
+ REQUIRES(mLock);
+ void onPrivacySensitiveDisplaysChanged(
+ const std::unordered_set<ui::LogicalDisplayId>& privacySensitiveDisplays);
+
+ /* This listener keeps tracks of visible privacy sensitive displays and updates the
+ * choreographer if there are any changes.
+ *
+ * Listener uses mListenerLock to guard all private data as choreographer and SurfaceComposer
+ * both can call into the listener. To prevent deadlocks Choreographer can call listener with
+ * its lock held, but listener must not call choreographer with its lock.
+ */
+ class PointerChoreographerDisplayInfoListener : public gui::WindowInfosListener {
+ public:
+ explicit PointerChoreographerDisplayInfoListener(PointerChoreographer* pc)
+ : mPointerChoreographer(pc){};
+ void onWindowInfosChanged(const gui::WindowInfosUpdate&) override;
+ void setInitialDisplayInfos(const std::vector<gui::WindowInfo>& windowInfos);
+ std::unordered_set<ui::LogicalDisplayId /*displayId*/> getPrivacySensitiveDisplays();
+ void onPointerChoreographerDestroyed();
+
+ private:
+ std::mutex mListenerLock;
+ PointerChoreographer* mPointerChoreographer GUARDED_BY(mListenerLock);
+ std::unordered_set<ui::LogicalDisplayId /*displayId*/> mPrivacySensitiveDisplays
+ 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 +191,29 @@
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;
+ ui::LogicalDisplayId mCurrentFocusedDisplay GUARDED_BY(mLock);
+
+protected:
+ using WindowListenerRegisterConsumer = std::function<std::vector<gui::WindowInfo>(
+ const sp<android::gui::WindowInfosListener>&)>;
+ using WindowListenerUnregisterConsumer =
+ std::function<void(const sp<android::gui::WindowInfosListener>&)>;
+ explicit PointerChoreographer(InputListenerInterface& listener,
+ PointerChoreographerPolicyInterface&,
+ const WindowListenerRegisterConsumer& registerListener,
+ const WindowListenerUnregisterConsumer& unregisterListener);
+
+private:
+ const WindowListenerRegisterConsumer mRegisterListener;
+ const WindowListenerUnregisterConsumer mUnregisterListener;
};
} // 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..96c8640 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::LogicalDisplayId::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::LogicalDisplayId::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::LogicalDisplayId::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 70c3ad1..1a0ec48 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -61,6 +61,8 @@
],
shared_libs: [
"libbase",
+ "libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libinput",
@@ -71,6 +73,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 83e6a60..4a0889f 100644
--- a/services/inputflinger/dispatcher/CancelationOptions.h
+++ b/services/inputflinger/dispatcher/CancelationOptions.h
@@ -16,6 +16,8 @@
#pragma once
+#include "trace/EventTrackerInterface.h"
+
#include <input/Input.h>
#include <bitset>
#include <optional>
@@ -46,12 +48,18 @@
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;
- CancelationOptions(Mode mode, const char* reason) : mode(mode), reason(reason) {}
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker;
+
+ explicit CancelationOptions(Mode mode, const char* reason,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker)
+ : mode(mode), reason(reason), traceTracker(traceTracker) {}
+ CancelationOptions(const CancelationOptions&) = delete;
+ CancelationOptions operator=(const CancelationOptions&) = delete;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 264dc03..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;
@@ -110,7 +111,7 @@
std::string PointerCaptureChangedEntry::getDescription() const {
return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)",
- pointerCaptureRequest.enable ? "true" : "false");
+ pointerCaptureRequest.isEnable() ? "true" : "false");
}
// --- DragEntry ---
@@ -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 1298b5d..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;
@@ -140,12 +140,13 @@
mutable InterceptKeyResult interceptKeyResult; // set based on the interception result
mutable nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER
mutable int32_t flags;
+ // TODO(b/328618922): Refactor key repeat generation to make repeatCount non-mutable.
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;
};
@@ -154,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;
@@ -174,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;
};
@@ -204,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 2fd1763..f9fbfef 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -103,6 +103,26 @@
}
}
+// Helper to get a trace tracker from a traced key or motion entry.
+const std::unique_ptr<trace::EventTrackerInterface>& getTraceTracker(const EventEntry& entry) {
+ switch (entry.type) {
+ case EventEntry::Type::MOTION: {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ ensureEventTraced(motion);
+ return motion.traceTracker;
+ }
+ case EventEntry::Type::KEY: {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ ensureEventTraced(key);
+ return key.traceTracker;
+ }
+ default: {
+ const static std::unique_ptr<trace::EventTrackerInterface> kNullTracker;
+ return kNullTracker;
+ }
+ }
+}
+
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
class scoped_unlock {
@@ -379,7 +399,8 @@
const InputTarget& inputTarget,
std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags,
- int64_t vsyncId) {
+ int64_t vsyncId,
+ trace::InputTracerInterface* tracer) {
const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS);
const sp<WindowInfoHandle> win = inputTarget.windowHandle;
const std::optional<int32_t> windowId =
@@ -423,10 +444,12 @@
newCoords.copyFrom(motionEntry.pointerCoords[i]);
// First, apply the current pointer's transform to update the coordinates into
// window space.
- newCoords.transform(currTransform);
+ MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source,
+ motionEntry.flags, currTransform);
// Next, apply the inverse transform of the normalized coordinates so the
// current coordinates are transformed into the normalized coordinate space.
- newCoords.transform(inverseTransform);
+ MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source,
+ motionEntry.flags, inverseTransform);
}
}
@@ -442,6 +465,10 @@
motionEntry.xCursorPosition, motionEntry.yCursorPosition,
motionEntry.downTime, motionEntry.pointerProperties,
pointerCoords);
+ if (tracer) {
+ combinedMotionEntry->traceTracker =
+ tracer->traceDerivedEvent(*combinedMotionEntry, *motionEntry.traceTracker);
+ }
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
@@ -546,8 +573,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)) {
@@ -573,6 +600,18 @@
return true;
}
+// Returns true if the given window's frame can occlude pointer events at the given display
+// location.
+bool windowOccludesTouchAt(const WindowInfo& windowInfo, ui::LogicalDisplayId displayId, float x,
+ float y, const ui::Transform& displayTransform) {
+ if (windowInfo.displayId != displayId) {
+ return false;
+ }
+ const auto frame = displayTransform.transform(windowInfo.frame);
+ const auto p = floor(displayTransform.transform(x, y));
+ return p.x >= frame.left && p.x < frame.right && p.y >= frame.top && p.y < frame.bottom;
+}
+
bool isPointerFromStylus(const MotionEntry& entry, int32_t pointerIndex) {
return isFromSource(entry.source, AINPUT_SOURCE_STYLUS) &&
isStylusToolType(entry.pointerProperties[pointerIndex].toolType);
@@ -656,13 +695,13 @@
std::vector<TouchedWindow> getHoveringWindowsLocked(const TouchState* oldState,
const TouchState& newTouchState,
const MotionEntry& entry) {
- std::vector<TouchedWindow> out;
const int32_t maskedAction = MotionEvent::getActionMasked(entry.action);
if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) {
// ACTION_SCROLL events should not affect the hovering pointer dispatch
return {};
}
+ std::vector<TouchedWindow> out;
// We should consider all hovering pointers here. But for now, just use the first one
const PointerProperties& pointer = entry.pointerProperties[0];
@@ -778,9 +817,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;
@@ -837,6 +876,31 @@
}
}
+class ScopedSyntheticEventTracer {
+public:
+ ScopedSyntheticEventTracer(std::unique_ptr<trace::InputTracerInterface>& tracer)
+ : mTracer(tracer), mProcessingTimestamp(now()) {
+ if (mTracer) {
+ mEventTracker = mTracer->createTrackerForSyntheticEvent();
+ }
+ }
+
+ ~ScopedSyntheticEventTracer() {
+ if (mTracer) {
+ mTracer->eventProcessingComplete(*mEventTracker, mProcessingTimestamp);
+ }
+ }
+
+ const std::unique_ptr<trace::EventTrackerInterface>& getTracker() const {
+ return mEventTracker;
+ }
+
+private:
+ const std::unique_ptr<trace::InputTracerInterface>& mTracer;
+ std::unique_ptr<trace::EventTrackerInterface> mEventTracker;
+ const nsecs_t mProcessingTimestamp;
+};
+
} // namespace
// --- InputDispatcher ---
@@ -857,8 +921,9 @@
mDispatchFrozen(false),
mInputFilterEnabled(false),
mMaximumObscuringOpacityForTouch(1.0f),
- mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
+ mFocusedDisplayId(ui::LogicalDisplayId::DEFAULT),
mWindowTokenWithPointerCapture(nullptr),
+ mAwaitedApplicationDisplayId(ui::LogicalDisplayId::INVALID),
mLatencyAggregator(),
mLatencyTracker(&mLatencyAggregator) {
mLooper = sp<Looper>::make(false);
@@ -1147,10 +1212,6 @@
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*keyEntry);
- mTracer->eventProcessingComplete(*keyEntry->traceTracker);
- }
break;
}
@@ -1176,10 +1237,6 @@
}
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
- if (done && mTracer) {
- ensureEventTraced(*motionEntry);
- mTracer->eventProcessingComplete(*motionEntry->traceTracker);
- }
break;
}
@@ -1205,6 +1262,12 @@
}
mLastDropReason = dropReason;
+ if (mTracer) {
+ if (auto& traceTracker = getTraceTracker(*mPendingEvent); traceTracker != nullptr) {
+ mTracer->eventProcessingComplete(*traceTracker, currentTime);
+ }
+ }
+
releasePendingEventLocked();
nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
}
@@ -1227,7 +1290,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);
@@ -1352,8 +1415,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);
@@ -1372,7 +1435,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 {};
}
@@ -1399,7 +1463,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);
@@ -1456,8 +1520,9 @@
switch (entry.type) {
case EventEntry::Type::KEY: {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason);
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS, reason,
+ keyEntry.traceTracker);
options.displayId = keyEntry.displayId;
options.deviceId = keyEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -1466,13 +1531,14 @@
case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
- CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, reason,
+ motionEntry.traceTracker);
options.displayId = motionEntry.displayId;
options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
} else {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- reason);
+ reason, motionEntry.traceTracker);
options.displayId = motionEntry.displayId;
options.deviceId = motionEntry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -1607,7 +1673,9 @@
resetKeyRepeatLocked();
}
- CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset");
+ ScopedSyntheticEventTracer traceContext(mTracer);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, "device was reset",
+ traceContext.getTracker());
options.deviceId = entry.deviceId;
synthesizeCancelationEventsForAllConnectionsLocked(options);
@@ -1664,7 +1732,7 @@
const bool haveWindowWithPointerCapture = mWindowTokenWithPointerCapture != nullptr;
sp<IBinder> token;
- if (entry->pointerCaptureRequest.enable) {
+ if (entry->pointerCaptureRequest.isEnable()) {
// Enable Pointer Capture.
if (haveWindowWithPointerCapture &&
(entry->pointerCaptureRequest == mCurrentPointerCaptureRequest)) {
@@ -1673,7 +1741,7 @@
ALOGI("Skipping dispatch of Pointer Capture being enabled: no state change.");
return;
}
- if (!mCurrentPointerCaptureRequest.enable) {
+ if (!mCurrentPointerCaptureRequest.isEnable()) {
// This can happen if a window requests capture and immediately releases capture.
ALOGW("No window requested Pointer Capture.");
dropReason = DropReason::NO_POINTER_CAPTURE;
@@ -1686,6 +1754,8 @@
token = mFocusResolver.getFocusedWindowToken(mFocusedDisplayId);
LOG_ALWAYS_FATAL_IF(!token, "Cannot find focused window for Pointer Capture.");
+ LOG_ALWAYS_FATAL_IF(token != entry->pointerCaptureRequest.window,
+ "Unexpected requested window for Pointer Capture.");
mWindowTokenWithPointerCapture = token;
} else {
// Disable Pointer Capture.
@@ -1705,8 +1775,8 @@
}
token = mWindowTokenWithPointerCapture;
mWindowTokenWithPointerCapture = nullptr;
- if (mCurrentPointerCaptureRequest.enable) {
- setPointerCaptureLocked(false);
+ if (mCurrentPointerCaptureRequest.isEnable()) {
+ setPointerCaptureLocked(nullptr);
}
}
@@ -1714,8 +1784,8 @@
if (connection == nullptr) {
// Window has gone away, clean up Pointer Capture state.
mWindowTokenWithPointerCapture = nullptr;
- if (mCurrentPointerCaptureRequest.enable) {
- setPointerCaptureLocked(false);
+ if (mCurrentPointerCaptureRequest.isEnable()) {
+ setPointerCaptureLocked(nullptr);
}
return;
}
@@ -1762,7 +1832,7 @@
DropReason* dropReason, nsecs_t& nextWakeupTime) {
// Preprocessing.
if (!entry->dispatchInProgress) {
- if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN &&
+ if (!entry->syntheticRepeat && entry->action == AKEY_EVENT_ACTION_DOWN &&
(entry->policyFlags & POLICY_FLAG_TRUSTED) &&
(!(entry->policyFlags & POLICY_FLAG_DISABLE_KEY_REPEAT))) {
if (mKeyRepeatState.lastKeyEntry &&
@@ -1827,8 +1897,6 @@
doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry);
};
postCommandLocked(std::move(command));
- // Poke user activity for keys not passed to user
- pokeUserActivityLocked(*entry);
return false; // wait for the command to run
} else {
entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE;
@@ -1845,8 +1913,12 @@
*dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
: InputEventInjectionResult::FAILED);
mReporter->reportDroppedKey(entry->id);
- // Poke user activity for undispatched keys
- pokeUserActivityLocked(*entry);
+ // Poke user activity for consumed keys, as it may have not been reported due to
+ // the focused window requesting user activity to be disabled
+ if (*dropReason == DropReason::POLICY &&
+ mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ pokeUserActivityLocked(*entry);
+ }
return true;
}
@@ -1886,12 +1958,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);
}
}
@@ -1996,7 +2068,7 @@
CancelationOptions::Mode mode(
isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
: CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
- CancelationOptions options(mode, "input event injection failed");
+ CancelationOptions options(mode, "input event injection failed", entry->traceTracker);
options.displayId = entry->displayId;
synthesizeCancelationEventsForMonitorsLocked(options);
return true;
@@ -2040,16 +2112,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, "
@@ -2102,8 +2173,9 @@
if (connection->status != Connection::Status::NORMAL) {
return;
}
+ ScopedSyntheticEventTracer traceContext(mTracer);
CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS,
- "application not responding");
+ "application not responding", traceContext.getTracker());
sp<WindowInfoHandle> windowHandle;
if (!connection->monitor) {
@@ -2133,8 +2205,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::LogicalDisplayId::INVALID};
switch (entry.type) {
case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
@@ -2154,10 +2226,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::LogicalDisplayId::INVALID;
}
}
- return displayId == ADISPLAY_ID_NONE ? mFocusedDisplayId : displayId;
+ return displayId == ui::LogicalDisplayId::INVALID ? mFocusedDisplayId : displayId;
}
bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime,
@@ -2196,7 +2268,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);
@@ -2205,8 +2277,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;
}
@@ -2314,7 +2386,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);
@@ -2384,9 +2456,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.
@@ -2425,8 +2498,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 {};
}
@@ -2463,11 +2536,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
@@ -2542,11 +2623,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);
@@ -2566,9 +2647,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;
@@ -2636,19 +2717,14 @@
{
std::vector<TouchedWindow> hoveringWindows =
getHoveringWindowsLocked(oldState, tempTouchState, entry);
+ // Hardcode to single hovering pointer for now.
+ std::bitset<MAX_POINTER_ID + 1> pointerIds;
+ pointerIds.set(entry.pointerProperties[0].id);
for (const TouchedWindow& touchedWindow : hoveringWindows) {
- std::optional<InputTarget> target =
- createInputTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
- touchedWindow.targetFlags,
- touchedWindow.getDownTimeInTarget(entry.deviceId));
- if (!target) {
- continue;
- }
- // Hardcode to single hovering pointer for now.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
- pointerIds.set(entry.pointerProperties[0].id);
- target->addPointers(pointerIds, touchedWindow.windowHandle->getInfo()->transform);
- targets.push_back(*target);
+ addPointerWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.dispatchMode,
+ touchedWindow.targetFlags, pointerIds,
+ touchedWindow.getDownTimeInTarget(entry.deviceId),
+ targets);
}
}
@@ -2672,7 +2748,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) {
@@ -2761,7 +2837,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::LogicalDisplayId::DEFAULT) {
tempTouchState.clearWindowsWithoutPointers();
mTouchStatesByDisplay[displayId] = tempTouchState;
} else {
@@ -2776,7 +2852,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;
@@ -2985,11 +3061,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;
@@ -3059,9 +3139,9 @@
* If neither of those is true, then it means the touch can be allowed.
*/
InputDispatcher::TouchOcclusionInfo InputDispatcher::computeTouchOcclusionInfoLocked(
- const sp<WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const {
+ 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;
@@ -3073,7 +3153,8 @@
break; // All future windows are below us. Exit early.
}
const WindowInfo* otherInfo = otherHandle->getInfo();
- if (canBeObscuredBy(windowHandle, otherHandle) && otherInfo->frameContainsPoint(x, y) &&
+ if (canBeObscuredBy(windowHandle, otherHandle) &&
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId)) &&
!haveSameApplicationToken(windowInfo, otherInfo)) {
if (DEBUG_TOUCH_OCCLUSION) {
info.debugInfo.push_back(
@@ -3143,8 +3224,8 @@
}
bool InputDispatcher::isWindowObscuredAtPointLocked(const sp<WindowInfoHandle>& windowHandle,
- int32_t x, int32_t y) const {
- int32_t displayId = windowHandle->getInfo()->displayId;
+ float x, float y) const {
+ ui::LogicalDisplayId displayId = windowHandle->getInfo()->displayId;
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& otherHandle : windowHandles) {
if (windowHandle == otherHandle) {
@@ -3152,7 +3233,7 @@
}
const WindowInfo* otherInfo = otherHandle->getInfo();
if (canBeObscuredBy(windowHandle, otherHandle) &&
- otherInfo->frameContainsPoint(x, y)) {
+ windowOccludesTouchAt(*otherInfo, displayId, x, y, getTransformLocked(displayId))) {
return true;
}
}
@@ -3160,7 +3241,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) {
@@ -3168,8 +3249,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;
}
}
@@ -3211,7 +3291,7 @@
}
}
- int32_t displayId = getTargetDisplayId(eventEntry);
+ ui::LogicalDisplayId displayId = getTargetDisplayId(eventEntry);
sp<WindowInfoHandle> focusedWindowHandle = getFocusedWindowHandleLocked(displayId);
const WindowInfo* windowDisablingUserActivityInfo = nullptr;
if (focusedWindowHandle != nullptr) {
@@ -3241,22 +3321,16 @@
if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
}
- // If the key code is unknown, we don't consider it user activity
- if (keyEntry.keyCode == AKEYCODE_UNKNOWN) {
- return;
- }
// Don't inhibit events that were intercepted or are not passed to
// the apps, like system shortcuts
if (windowDisablingUserActivityInfo != nullptr &&
- keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP &&
- keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) {
+ keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("Not poking user activity: disabled by window '%s'.",
windowDisablingUserActivityInfo->name.c_str());
}
return;
}
-
break;
}
default: {
@@ -3376,7 +3450,7 @@
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags,
- mWindowInfosVsyncId);
+ mWindowInfosVsyncId, mTracer.get());
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
@@ -3459,21 +3533,31 @@
usingCoords = pointerInfo->second;
}
}
- // Generate a new MotionEntry with a new eventId using the resolved action and
- // flags.
- resolvedMotion = std::make_shared<
- MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
- motionEntry.eventTime, motionEntry.deviceId,
- motionEntry.source, motionEntry.displayId,
- motionEntry.policyFlags, resolvedAction,
- motionEntry.actionButton, resolvedFlags,
- motionEntry.metaState, motionEntry.buttonState,
- motionEntry.classification, motionEntry.edgeFlags,
- motionEntry.xPrecision, motionEntry.yPrecision,
- motionEntry.xCursorPosition, motionEntry.yCursorPosition,
- motionEntry.downTime,
- usingProperties.value_or(motionEntry.pointerProperties),
- usingCoords.value_or(motionEntry.pointerCoords));
+ {
+ // Generate a new MotionEntry with a new eventId using the resolved action
+ // and flags, and set it as the resolved entry.
+ auto newEntry = std::make_shared<
+ MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition,
+ motionEntry.yCursorPosition, motionEntry.downTime,
+ usingProperties.value_or(
+ motionEntry.pointerProperties),
+ usingCoords.value_or(motionEntry.pointerCoords));
+ if (mTracer) {
+ ensureEventTraced(motionEntry);
+ newEntry->traceTracker =
+ mTracer->traceDerivedEvent(*newEntry,
+ *motionEntry.traceTracker);
+ }
+ resolvedMotion = newEntry;
+ }
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
") to MotionEvent(id=0x%" PRIx32 ").",
@@ -3496,9 +3580,14 @@
LOG(INFO) << "Canceling pointers for device " << resolvedMotion->deviceId << " in "
<< connection->getInputChannelName() << " with event "
<< cancelEvent->getDescription();
+ if (mTracer) {
+ static_cast<MotionEntry&>(*cancelEvent).traceTracker =
+ mTracer->traceDerivedEvent(*cancelEvent, *resolvedMotion->traceTracker);
+ }
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent),
- ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId);
+ ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId,
+ mTracer.get());
// Send these cancel events to the queue before sending the event from the new
// device.
@@ -3732,7 +3821,8 @@
keyEntry.metaState, keyEntry.repeatCount,
keyEntry.downTime, keyEntry.eventTime);
if (mTracer) {
- mTracer->traceEventDispatch(*dispatchEntry, keyEntry.traceTracker.get());
+ ensureEventTraced(keyEntry);
+ mTracer->traceEventDispatch(*dispatchEntry, *keyEntry.traceTracker);
}
break;
}
@@ -3745,7 +3835,8 @@
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
if (mTracer) {
- mTracer->traceEventDispatch(*dispatchEntry, motionEntry.traceTracker.get());
+ ensureEventTraced(motionEntry);
+ mTracer->traceEventDispatch(*dispatchEntry, *motionEntry.traceTracker);
}
break;
}
@@ -3771,9 +3862,10 @@
case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
const auto& captureEntry =
static_cast<const PointerCaptureChangedEntry&>(eventEntry);
- status = connection->inputPublisher
- .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
- captureEntry.pointerCaptureRequest.enable);
+ status =
+ connection->inputPublisher
+ .publishCaptureEvent(dispatchEntry->seq, captureEntry.id,
+ captureEntry.pointerCaptureRequest.isEnable());
break;
}
@@ -4124,6 +4216,11 @@
switch (cancelationEventEntry->type) {
case EventEntry::Type::KEY: {
+ if (mTracer) {
+ static_cast<KeyEntry&>(*cancelationEventEntry).traceTracker =
+ mTracer->traceDerivedEvent(*cancelationEventEntry,
+ *options.traceTracker);
+ }
const auto& keyEntry = static_cast<const KeyEntry&>(*cancelationEventEntry);
if (window) {
addWindowTargetLocked(window, InputTarget::DispatchMode::AS_IS,
@@ -4135,6 +4232,11 @@
break;
}
case EventEntry::Type::MOTION: {
+ if (mTracer) {
+ static_cast<MotionEntry&>(*cancelationEventEntry).traceTracker =
+ mTracer->traceDerivedEvent(*cancelationEventEntry,
+ *options.traceTracker);
+ }
const auto& motionEntry = static_cast<const MotionEntry&>(*cancelationEventEntry);
if (window) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
@@ -4182,6 +4284,9 @@
}
if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ if (mTracer) {
+ mTracer->dispatchToTargetHint(*options.traceTracker, targets[0]);
+ }
enqueueDispatchEntryLocked(connection, std::move(cancelationEventEntry), targets[0]);
}
@@ -4193,7 +4298,8 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
- ftl::Flags<InputTarget::Flags> targetFlags) {
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) {
if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4222,6 +4328,10 @@
std::vector<InputTarget> targets{};
switch (downEventEntry->type) {
case EventEntry::Type::MOTION: {
+ if (mTracer) {
+ static_cast<MotionEntry&>(*downEventEntry).traceTracker =
+ mTracer->traceDerivedEvent(*downEventEntry, *traceTracker);
+ }
const auto& motionEntry = static_cast<const MotionEntry&>(*downEventEntry);
if (windowHandle != nullptr) {
std::bitset<MAX_POINTER_ID + 1> pointerIds;
@@ -4259,6 +4369,9 @@
}
if (targets.size() != 1) LOG(FATAL) << __func__ << ": InputTarget not created";
+ if (mTracer) {
+ mTracer->dispatchToTargetHint(*traceTracker, targets[0]);
+ }
enqueueDispatchEntryLocked(connection, std::move(downEventEntry), targets[0]);
}
@@ -4271,72 +4384,27 @@
std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent(
const MotionEntry& originalMotionEntry, std::bitset<MAX_POINTER_ID + 1> pointerIds,
nsecs_t splitDownTime) {
- ALOG_ASSERT(pointerIds.any());
-
- uint32_t splitPointerIndexMap[MAX_POINTERS];
- std::vector<PointerProperties> splitPointerProperties;
- std::vector<PointerCoords> splitPointerCoords;
-
- uint32_t originalPointerCount = originalMotionEntry.getPointerCount();
- uint32_t splitPointerCount = 0;
-
- for (uint32_t originalPointerIndex = 0; originalPointerIndex < originalPointerCount;
- originalPointerIndex++) {
- const PointerProperties& pointerProperties =
- originalMotionEntry.pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.test(pointerId)) {
- splitPointerIndexMap[splitPointerCount] = originalPointerIndex;
- splitPointerProperties.push_back(pointerProperties);
- splitPointerCoords.push_back(originalMotionEntry.pointerCoords[originalPointerIndex]);
- splitPointerCount += 1;
- }
- }
-
- if (splitPointerCount != pointerIds.count()) {
+ const auto& [action, pointerProperties, pointerCoords] =
+ MotionEvent::split(originalMotionEntry.action, originalMotionEntry.flags,
+ /*historySize=*/0, originalMotionEntry.pointerProperties,
+ originalMotionEntry.pointerCoords, pointerIds);
+ if (pointerIds.count() != pointerCoords.size()) {
+ // TODO(b/329107108): Determine why some IDs in pointerIds were not in originalMotionEntry.
// This is bad. We are missing some of the pointers that we expected to deliver.
// Most likely this indicates that we received an ACTION_MOVE events that has
// 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",
- splitPointerCount, pointerIds.count(), originalMotionEntry.getDescription().c_str());
+ pointerCoords.size(), pointerIds.count(),
+ originalMotionEntry.getDescription().c_str());
return nullptr;
}
- int32_t action = originalMotionEntry.action;
- int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
- if (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
- int32_t originalPointerIndex = MotionEvent::getActionIndex(action);
- const PointerProperties& pointerProperties =
- originalMotionEntry.pointerProperties[originalPointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- if (pointerIds.test(pointerId)) {
- if (pointerIds.count() == 1) {
- // The first/last pointer went down/up.
- action = maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN
- ? AMOTION_EVENT_ACTION_DOWN
- : (originalMotionEntry.flags & AMOTION_EVENT_FLAG_CANCELED) != 0
- ? AMOTION_EVENT_ACTION_CANCEL
- : AMOTION_EVENT_ACTION_UP;
- } else {
- // A secondary pointer went down/up.
- uint32_t splitPointerIndex = 0;
- while (pointerId != uint32_t(splitPointerProperties[splitPointerIndex].id)) {
- splitPointerIndex += 1;
- }
- action = maskedAction |
- (splitPointerIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- }
- } else {
- // An unrelated pointer changed.
- action = AMOTION_EVENT_ACTION_MOVE;
- }
- }
-
+ // TODO(b/327503168): Move this check inside MotionEvent::split once all callers handle it
+ // correctly.
if (action == AMOTION_EVENT_ACTION_DOWN && splitDownTime != originalMotionEntry.eventTime) {
logDispatchStateLocked();
LOG_ALWAYS_FATAL("Split motion event has mismatching downTime and eventTime for "
@@ -4364,7 +4432,11 @@
originalMotionEntry.yPrecision,
originalMotionEntry.xCursorPosition,
originalMotionEntry.yCursorPosition, splitDownTime,
- splitPointerProperties, splitPointerCoords);
+ pointerProperties, pointerCoords);
+ if (mTracer) {
+ splitMotionEntry->traceTracker =
+ mTracer->traceDerivedEvent(*splitMotionEntry, *originalMotionEntry.traceTracker);
+ }
return splitMotionEntry;
}
@@ -4396,12 +4468,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();
@@ -4477,15 +4550,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",
@@ -4514,7 +4587,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(),
@@ -4660,6 +4734,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);
@@ -4686,7 +4761,7 @@
void InputDispatcher::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) {
if (debugInboundEventDetails()) {
ALOGD("notifyPointerCaptureChanged - eventTime=%" PRId64 ", enabled=%s", args.eventTime,
- args.request.enable ? "true" : "false");
+ args.request.isEnable() ? "true" : "false");
}
bool needWake = false;
@@ -4788,8 +4863,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 int32_t displayId = isPointerEvent && (event->getDisplayId() == ADISPLAY_ID_NONE)
- ? ADISPLAY_ID_DEFAULT
+ const ui::LogicalDisplayId displayId =
+ isPointerEvent && (event->getDisplayId() == ui::LogicalDisplayId::INVALID)
+ ? ui::LogicalDisplayId::DEFAULT
: event->getDisplayId();
int32_t flags = motionEvent.getFlags();
@@ -4810,6 +4886,31 @@
}
mLock.lock();
+
+ {
+ // Verify all injected streams, whether the injection is coming from apps or from
+ // input filter. Print an error if the stream becomes inconsistent with this event.
+ // An inconsistent injected event sent could cause a crash in the later stages of
+ // dispatching pipeline.
+ auto [it, _] =
+ mInputFilterVerifiersByDisplay.try_emplace(displayId,
+ std::string("Injection on ") +
+ displayId.toString());
+ 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>
@@ -4859,6 +4960,10 @@
pointerCount));
transformMotionEntryForInjectionLocked(*nextInjectedEntry,
motionEvent.getTransform());
+ if (mTracer) {
+ nextInjectedEntry->traceTracker =
+ mTracer->traceInboundEvent(*nextInjectedEntry);
+ }
injectedEntries.push(std::move(nextInjectedEntry));
}
break;
@@ -5030,8 +5135,8 @@
}
for (uint32_t i = 0; i < entry.getPointerCount(); i++) {
entry.pointerCoords[i] =
- MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay,
- entry.pointerCoords[i]);
+ MotionEvent::calculateTransformedCoords(entry.source, entry.flags,
+ transformToDisplay, entry.pointerCoords[i]);
}
}
@@ -5052,22 +5157,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;
@@ -5088,16 +5192,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;
}
@@ -5106,12 +5209,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;
@@ -5180,7 +5284,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);
@@ -5212,13 +5317,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);
@@ -5239,7 +5345,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) {
@@ -5247,6 +5354,7 @@
}
LOG(INFO) << "setInputWindows displayId=" << displayId << " " << windowList;
}
+ ScopedSyntheticEventTracer traceContext(mTracer);
// Check preconditions for new input windows
for (const sp<WindowInfoHandle>& window : windowInfoHandles) {
@@ -5286,34 +5394,35 @@
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
- onFocusChangedLocked(*changes, removedFocusedWindowHandle);
+ 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");
- 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
@@ -5342,9 +5451,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
@@ -5357,7 +5467,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);
@@ -5394,12 +5505,13 @@
* 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);
+ ScopedSyntheticEventTracer traceContext(mTracer);
if (mFocusedDisplayId != displayId) {
sp<IBinder> oldFocusedWindowToken =
@@ -5412,18 +5524,31 @@
}
CancelationOptions
options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "The display which contains this window no longer has focus.");
- options.displayId = ADISPLAY_ID_NONE;
+ "The display which contains this window no longer has focus.",
+ traceContext.getTracker());
+ options.displayId = ui::LogicalDisplayId::INVALID;
synthesizeCancelationEventsForWindowLocked(windowHandle, options);
}
mFocusedDisplayId = displayId;
+ // Enqueue a command to run outside the lock to tell the policy that the focused display
+ // changed.
+ auto command = [this]() REQUIRES(mLock) {
+ scoped_unlock unlock(mLock);
+ mPolicy.notifyFocusedDisplayChanged(mFocusedDisplayId);
+ };
+ postCommandLocked(std::move(command));
+
+ // Only a window on the focused display can have Pointer Capture, so disable the active
+ // Pointer Capture session if there is one, since the focused display changed.
+ disablePointerCaptureForcedLocked();
// Find new focused window and validate
sp<IBinder> newFocusedWindowToken = mFocusResolver.getFocusedWindowToken(displayId);
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());
@@ -5489,15 +5614,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());
@@ -5555,7 +5680,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) {
@@ -5564,7 +5689,7 @@
}
}
}
- return std::make_tuple(nullptr, nullptr, ADISPLAY_ID_DEFAULT);
+ return std::make_tuple(nullptr, nullptr, ui::LogicalDisplayId::DEFAULT);
}
bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
@@ -5586,13 +5711,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);
@@ -5637,19 +5762,27 @@
}
// Synthesize cancel for old window and down for new window.
+ ScopedSyntheticEventTracer traceContext(mTracer);
std::shared_ptr<Connection> fromConnection = getConnectionLocked(fromToken);
std::shared_ptr<Connection> toConnection = getConnectionLocked(toToken);
if (fromConnection != nullptr && toConnection != nullptr) {
fromConnection->inputState.mergePointerStateTo(toConnection->inputState);
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch from this window to another window");
+ "transferring touch from this window to another window",
+ traceContext.getTracker());
synthesizeCancelationEventsForWindowLocked(fromWindowHandle, options, fromConnection);
- synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, toConnection,
- newTargetFlags);
// Check if the wallpaper window should deliver the corresponding event.
transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle,
- *state, deviceId, pointers);
+ *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
@@ -5663,10 +5796,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;
}
@@ -5689,14 +5823,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;
}
@@ -5717,7 +5851,9 @@
ALOGD("Resetting and dropping all events (%s).", reason);
}
- CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason);
+ ScopedSyntheticEventTracer traceContext(mTracer);
+ CancelationOptions options(CancelationOptions::Mode::CANCEL_ALL_EVENTS, reason,
+ traceContext.getTracker());
synthesizeCancelationEventsForAllConnectionsLocked(options);
resetKeyRepeatLocked();
@@ -5745,7 +5881,7 @@
std::string dump;
dump += StringPrintf(INDENT "Pointer Capture Requested: %s\n",
- toString(mCurrentPointerCaptureRequest.enable));
+ toString(mCurrentPointerCaptureRequest.isEnable()));
std::string windowName = "None";
if (mWindowTokenWithPointerCapture) {
@@ -5763,18 +5899,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");
@@ -5787,7 +5924,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";
@@ -5800,7 +5937,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,
@@ -5826,7 +5963,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 {
@@ -5915,8 +6053,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";
@@ -5989,9 +6127,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);
@@ -6002,7 +6139,7 @@
{ // acquire lock
std::scoped_lock _l(mLock);
- if (displayId < 0) {
+ if (displayId < ui::LogicalDisplayId::DEFAULT) {
return base::Error(BAD_VALUE) << "Attempted to create input monitor with name " << name
<< " without a specified display.";
}
@@ -6112,12 +6249,13 @@
return BAD_VALUE;
}
+ ScopedSyntheticEventTracer traceContext(mTracer);
for (const DeviceId deviceId : deviceIds) {
TouchState& state = *statePtr;
TouchedWindow& window = *windowPtr;
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "input channel stole pointer stream");
+ "input channel stole pointer stream", traceContext.getTracker());
options.deviceId = deviceId;
options.displayId = displayId;
std::vector<PointerProperties> pointers = window.getTouchingPointers(deviceId);
@@ -6163,7 +6301,7 @@
return;
}
- if (enabled == mCurrentPointerCaptureRequest.enable) {
+ if (enabled == mCurrentPointerCaptureRequest.isEnable()) {
ALOGW("Ignoring request to %s Pointer Capture: "
"window has %s requested pointer capture.",
enabled ? "enable" : "disable", enabled ? "already" : "not");
@@ -6179,14 +6317,15 @@
}
}
- setPointerCaptureLocked(enabled);
+ setPointerCaptureLocked(enabled ? windowToken : nullptr);
} // release lock
// Wake the thread to process command entries.
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);
@@ -6263,9 +6402,8 @@
}
if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) {
- const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry));
fallbackKeyEntry =
- afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled);
+ afterKeyEventLockedInterruptable(connection, &dispatchEntry, handled);
}
} // End critical section: The -LockedInterruptable methods may have released the lock.
@@ -6489,8 +6627,17 @@
}
std::unique_ptr<const KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable(
- const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry,
- const KeyEntry& keyEntry, bool handled) {
+ const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) {
+ // The dispatchEntry is currently valid, but it might point to a deleted object after we release
+ // the lock. For simplicity, make copies of the data of interest here and assume that
+ // 'dispatchEntry' is not valid after this section.
+ // Hold a strong reference to the EventEntry to ensure it's valid for the duration of this
+ // function, even if the DispatchEntry gets destroyed and releases its share of the ownership.
+ std::shared_ptr<const EventEntry> eventEntry = dispatchEntry->eventEntry;
+ const bool hasForegroundTarget = dispatchEntry->hasForegroundTarget();
+ const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(eventEntry));
+ // To prevent misuse, ensure dispatchEntry is no longer valid.
+ dispatchEntry = nullptr;
if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) {
if (!handled) {
// Report the key as unhandled, since the fallback was not handled.
@@ -6507,7 +6654,7 @@
connection->inputState.removeFallbackKey(originalKeyCode);
}
- if (handled || !dispatchEntry.hasForegroundTarget()) {
+ if (handled || !hasForegroundTarget) {
// If the application handles the original key for which we previously
// generated a fallback or if the window is not a foreground window,
// then cancel the associated fallback key, if any.
@@ -6541,7 +6688,8 @@
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
"application handled the original non-fallback key "
"or is no longer a foreground target, "
- "canceling previously dispatched fallback key");
+ "canceling previously dispatched fallback key",
+ keyEntry.traceTracker);
options.keyCode = *fallbackKeyCode;
synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection);
}
@@ -6623,7 +6771,8 @@
const auto windowHandle = getWindowHandleLocked(connection->getToken());
if (windowHandle != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_FALLBACK_EVENTS,
- "canceling fallback, policy no longer desires it");
+ "canceling fallback, policy no longer desires it",
+ keyEntry.traceTracker);
options.keyCode = *fallbackKeyCode;
synthesizeCancelationEventsForWindowLocked(windowHandle, options, connection);
}
@@ -6659,6 +6808,10 @@
*fallbackKeyCode, event.getScanCode(),
event.getMetaState(), event.getRepeatCount(),
event.getDownTime());
+ if (mTracer) {
+ newEntry->traceTracker =
+ mTracer->traceDerivedEvent(*newEntry, *keyEntry.traceTracker);
+ }
if (DEBUG_OUTBOUND_EVENT_DETAILS) {
ALOGD("Unhandled key event: Dispatching fallback key. "
"originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x",
@@ -6756,17 +6909,22 @@
{ // 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);
+ onFocusChangedLocked(*changes, traceContext.getTracker());
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
-void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
- const sp<WindowInfoHandle> removedFocusedWindowHandle) {
+void InputDispatcher::onFocusChangedLocked(
+ const FocusResolver::FocusChanges& changes,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker,
+ const sp<WindowInfoHandle> removedFocusedWindowHandle) {
if (changes.oldFocus) {
const auto resolvedWindow = removedFocusedWindowHandle != nullptr
? removedFocusedWindowHandle
@@ -6775,7 +6933,7 @@
LOG(FATAL) << __func__ << ": Previously focused token did not have a window";
}
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
- "focus left window");
+ "focus left window", traceTracker);
synthesizeCancelationEventsForWindowLocked(resolvedWindow, options);
enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
}
@@ -6784,30 +6942,30 @@
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
- // If a window has pointer capture, then it must have focus. We need to ensure that this
- // contract is upheld when pointer capture is being disabled due to a loss of window focus.
- // If the window loses focus before it loses pointer capture, then the window can be in a state
- // where it has pointer capture but not focus, violating the contract. Therefore we must
- // dispatch the pointer capture event before the focus event. Since focus events are added to
- // the front of the queue (above), we add the pointer capture event to the front of the queue
- // after the focus events are added. This ensures the pointer capture event ends up at the
- // front.
- disablePointerCaptureForcedLocked();
-
if (mFocusedDisplayId == changes.displayId) {
+ // If a window has pointer capture, then it must have focus and must be on the top-focused
+ // display. We need to ensure that this contract is upheld when pointer capture is being
+ // disabled due to a loss of window focus. If the window loses focus before it loses pointer
+ // capture, then the window can be in a state where it has pointer capture but not focus,
+ // violating the contract. Therefore we must dispatch the pointer capture event before the
+ // focus event. Since focus events are added to the front of the queue (above), we add the
+ // pointer capture event to the front of the queue after the focus events are added. This
+ // ensures the pointer capture event ends up at the front.
+ disablePointerCaptureForcedLocked();
+
sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
}
}
void InputDispatcher::disablePointerCaptureForcedLocked() {
- if (!mCurrentPointerCaptureRequest.enable && !mWindowTokenWithPointerCapture) {
+ if (!mCurrentPointerCaptureRequest.isEnable() && !mWindowTokenWithPointerCapture) {
return;
}
ALOGD_IF(DEBUG_FOCUS, "Disabling Pointer Capture because the window lost focus.");
- if (mCurrentPointerCaptureRequest.enable) {
- setPointerCaptureLocked(false);
+ if (mCurrentPointerCaptureRequest.isEnable()) {
+ setPointerCaptureLocked(nullptr);
}
if (!mWindowTokenWithPointerCapture) {
@@ -6827,8 +6985,8 @@
mInboundQueue.push_front(std::move(entry));
}
-void InputDispatcher::setPointerCaptureLocked(bool enable) {
- mCurrentPointerCaptureRequest.enable = enable;
+void InputDispatcher::setPointerCaptureLocked(const sp<IBinder>& windowToken) {
+ mCurrentPointerCaptureRequest.window = windowToken;
mCurrentPointerCaptureRequest.seq++;
auto command = [this, request = mCurrentPointerCaptureRequest]() REQUIRES(mLock) {
scoped_unlock unlock(mLock);
@@ -6837,7 +6995,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.
@@ -6851,6 +7009,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.
@@ -6869,7 +7028,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));
@@ -6911,10 +7070,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;
@@ -6928,9 +7087,10 @@
void InputDispatcher::cancelCurrentTouch() {
{
std::scoped_lock _l(mLock);
+ ScopedSyntheticEventTracer traceContext(mTracer);
ALOGD("Canceling all ongoing pointer gestures on all displays.");
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "cancel current touch");
+ "cancel current touch", traceContext.getTracker());
synthesizeCancelationEventsForAllConnectionsLocked(options);
mTouchStatesByDisplay.clear();
@@ -6947,7 +7107,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};
@@ -6957,7 +7117,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) {
@@ -6980,12 +7140,12 @@
}
}
-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 std::vector<PointerProperties>& pointers) {
+void InputDispatcher::transferWallpaperTouch(
+ ftl::Flags<InputTarget::Flags> oldTargetFlags,
+ ftl::Flags<InputTarget::Flags> newTargetFlags, const sp<WindowInfoHandle> fromWindowHandle,
+ 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) &&
fromWindowHandle->getInfo()->inputConfig.test(
gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER);
@@ -6994,7 +7154,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) {
@@ -7003,7 +7163,7 @@
if (oldWallpaper != nullptr) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
- "transferring touch focus to another window");
+ "transferring touch focus to another window", traceTracker);
state.removeWindowByToken(oldWallpaper->getToken());
synthesizeCancelationEventsForWindowLocked(oldWallpaper, options);
}
@@ -7023,7 +7183,7 @@
getConnectionLocked(toWindowHandle->getToken());
toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState);
synthesizePointerDownEventsForConnectionLocked(downTimeInTarget, wallpaperConnection,
- wallpaperFlags);
+ wallpaperFlags, traceTracker);
}
}
}
@@ -7057,8 +7217,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()) {
@@ -7074,4 +7235,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 269bfdd..e2fc7a0 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,13 +425,15 @@
// 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);
// Set the Pointer Capture state in the Policy.
- void setPointerCaptureLocked(bool enable) REQUIRES(mLock);
+ // The window is not nullptr for requests to enable, otherwise it is nullptr.
+ void setPointerCaptureLocked(const sp<IBinder>& window) REQUIRES(mLock);
// Dispatcher state at time of last ANR.
std::string mLastAnrState GUARDED_BY(mLock);
@@ -489,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);
/**
@@ -523,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);
@@ -548,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;
@@ -565,11 +577,11 @@
};
TouchOcclusionInfo computeTouchOcclusionInfoLocked(
- const sp<android::gui::WindowInfoHandle>& windowHandle, int32_t x, int32_t y) const
+ const sp<android::gui::WindowInfoHandle>& windowHandle, float x, float y) const
REQUIRES(mLock);
bool isTouchTrustedLocked(const TouchOcclusionInfo& occlusionInfo) const REQUIRES(mLock);
bool isWindowObscuredAtPointLocked(const sp<android::gui::WindowInfoHandle>& windowHandle,
- int32_t x, int32_t y) const REQUIRES(mLock);
+ float x, float y) const REQUIRES(mLock);
bool isWindowObscuredLocked(const sp<android::gui::WindowInfoHandle>& windowHandle) const
REQUIRES(mLock);
std::string dumpWindowForTouchOcclusion(const android::gui::WindowInfo* info,
@@ -628,7 +640,8 @@
void synthesizePointerDownEventsForConnectionLocked(
const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
- ftl::Flags<InputTarget::Flags> targetFlags) REQUIRES(mLock);
+ ftl::Flags<InputTarget::Flags> targetFlags,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker) REQUIRES(mLock);
// Splitting motion events across windows. When splitting motion event for a target,
// splitDownTime refers to the time of first 'down' event on that particular target
@@ -657,6 +670,7 @@
void doInterceptKeyBeforeDispatchingCommand(const sp<IBinder>& focusedWindowToken,
const KeyEntry& entry) REQUIRES(mLock);
void onFocusChangedLocked(const FocusResolver::FocusChanges& changes,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker,
const sp<gui::WindowInfoHandle> removedFocusedWindowHandle = nullptr)
REQUIRES(mLock);
void sendFocusChangedCommandLocked(const sp<IBinder>& oldToken, const sp<IBinder>& newToken)
@@ -670,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);
+ const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry,
+ 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.
@@ -696,15 +710,17 @@
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,
- const std::vector<PointerProperties>& pointers) REQUIRES(mLock);
+ TouchState& state, DeviceId deviceId,
+ const std::vector<PointerProperties>& pointers,
+ const std::unique_ptr<trace::EventTrackerInterface>& traceTracker)
+ REQUIRES(mLock);
sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow(
const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index 1fec9b7..dfbe02f 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;
@@ -495,67 +499,53 @@
nsecs_t currentTime) {
std::vector<std::unique_ptr<MotionEntry>> events;
std::vector<uint32_t> canceledPointerIndices;
- std::vector<PointerProperties> pointerProperties(MAX_POINTERS);
- std::vector<PointerCoords> pointerCoords(MAX_POINTERS);
+
for (uint32_t pointerIdx = 0; pointerIdx < memento.getPointerCount(); pointerIdx++) {
uint32_t pointerId = uint32_t(memento.pointerProperties[pointerIdx].id);
- pointerProperties[pointerIdx] = memento.pointerProperties[pointerIdx];
- pointerCoords[pointerIdx] = memento.pointerCoords[pointerIdx];
if (pointerIds.test(pointerId)) {
canceledPointerIndices.push_back(pointerIdx);
}
}
if (canceledPointerIndices.size() == memento.getPointerCount()) {
- const int32_t action =
- memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL;
- int32_t flags = memento.flags;
- if (action == AMOTION_EVENT_ACTION_CANCEL) {
- flags |= AMOTION_EVENT_FLAG_CANCELED;
+ // We are cancelling all pointers.
+ events.emplace_back(createCancelEntryForMemento(memento, currentTime));
+ return events;
+ }
+
+ // If we aren't canceling all pointers, we need to generate ACTION_POINTER_UP with
+ // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
+ // previously canceled pointers from PointerProperties and PointerCoords, and update
+ // pointerCount appropriately. For convenience, sort the canceled pointer indices in
+ // descending order so that we can just slide the remaining pointers to the beginning of
+ // the array when a pointer is canceled.
+ std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
+ std::greater<uint32_t>());
+
+ std::vector<PointerProperties> pointerProperties = memento.pointerProperties;
+ std::vector<PointerCoords> pointerCoords = memento.pointerCoords;
+ for (const uint32_t pointerIdx : canceledPointerIndices) {
+ if (pointerProperties.size() <= 1) {
+ LOG(FATAL) << "Unexpected code path for canceling all pointers!";
}
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
events.push_back(
std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
currentTime, memento.deviceId, memento.source,
memento.displayId, memento.policyFlags, action,
- /*actionButton=*/0, flags, AMETA_NONE,
- /*buttonState=*/0, MotionClassification::NONE,
+ /*actionButton=*/0,
+ memento.flags | AMOTION_EVENT_FLAG_CANCELED,
+ AMETA_NONE, /*buttonState=*/0,
+ MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
memento.yPrecision, memento.xCursorPosition,
memento.yCursorPosition, memento.downTime,
- memento.pointerProperties, memento.pointerCoords));
- } else {
- // If we aren't canceling all pointers, we need to generate ACTION_POINTER_UP with
- // FLAG_CANCELED for each of the canceled pointers. For each event, we must remove the
- // previously canceled pointers from PointerProperties and PointerCoords, and update
- // pointerCount appropriately. For convenience, sort the canceled pointer indices so that we
- // can just slide the remaining pointers to the beginning of the array when a pointer is
- // canceled.
- std::sort(canceledPointerIndices.begin(), canceledPointerIndices.end(),
- std::greater<uint32_t>());
+ pointerProperties, pointerCoords));
- uint32_t pointerCount = memento.getPointerCount();
- for (const uint32_t pointerIdx : canceledPointerIndices) {
- const int32_t action = pointerCount == 1 ? AMOTION_EVENT_ACTION_CANCEL
- : AMOTION_EVENT_ACTION_POINTER_UP |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- events.push_back(
- std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr,
- currentTime, memento.deviceId, memento.source,
- memento.displayId, memento.policyFlags, action,
- /*actionButton=*/0,
- memento.flags | AMOTION_EVENT_FLAG_CANCELED,
- AMETA_NONE, /*buttonState=*/0,
- MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision,
- memento.yPrecision, memento.xCursorPosition,
- memento.yCursorPosition, memento.downTime,
- pointerProperties, pointerCoords));
-
- // Cleanup pointer information
- pointerProperties.erase(pointerProperties.begin() + pointerIdx);
- pointerCoords.erase(pointerCoords.begin() + pointerIdx);
- pointerCount--;
- }
+ // Cleanup pointer information
+ pointerProperties.erase(pointerProperties.begin() + pointerIdx);
+ pointerCoords.erase(pointerCoords.begin() + pointerIdx);
}
return events;
}
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index d49469d..2808ba7 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::LogicalDisplayId::INVALID};
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::LogicalDisplayId::INVALID};
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 62c2b02..65fb76d 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h
@@ -76,6 +76,11 @@
InputDeviceSensorAccuracy accuracy) = 0;
virtual void notifyVibratorState(int32_t deviceId, bool isOn) = 0;
+ /*
+ * Notifies the system that focused display has changed.
+ */
+ virtual void notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) = 0;
+
/* Filters an input event.
* Return true to dispatch the event unmodified, false to consume the event.
* A filter can also transform and inject events later by passing POLICY_FLAG_FILTERED
@@ -99,8 +104,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 +125,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
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
index a61fa85..0b17507 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp
@@ -21,21 +21,43 @@
namespace android::inputdispatcher::trace {
+namespace {
+
+using namespace ftl::flag_operators;
+
+// The trace config to use for maximal tracing.
+const impl::TraceConfig CONFIG_TRACE_ALL{
+ .flags = impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS |
+ impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH,
+ .rules = {impl::TraceRule{.level = impl::TraceLevel::TRACE_LEVEL_COMPLETE,
+ .matchAllPackages = {},
+ .matchAnyPackages = {},
+ .matchSecure{},
+ .matchImeConnectionActive = {}}},
+};
+
+} // namespace
+
void AndroidInputEventProtoConverter::toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto) {
+ proto::AndroidMotionEvent& outProto,
+ bool isRedacted) {
outProto.set_event_id(event.id);
outProto.set_event_time_nanos(event.eventTime);
outProto.set_down_time_nanos(event.downTime);
outProto.set_source(event.source);
outProto.set_action(event.action);
outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId);
+ outProto.set_display_id(event.displayId.val());
outProto.set_classification(static_cast<int32_t>(event.classification));
- outProto.set_cursor_position_x(event.xCursorPosition);
- outProto.set_cursor_position_y(event.yCursorPosition);
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
+ if (!isRedacted) {
+ outProto.set_cursor_position_x(event.xCursorPosition);
+ outProto.set_cursor_position_y(event.yCursorPosition);
+ outProto.set_meta_state(event.metaState);
+ }
+
for (uint32_t i = 0; i < event.pointerProperties.size(); i++) {
auto* pointer = outProto.add_pointer();
@@ -49,36 +71,46 @@
const auto axis = bits.clearFirstMarkedBit();
auto axisEntry = pointer->add_axis_value();
axisEntry->set_axis(axis);
- axisEntry->set_value(coords.values[axisIndex]);
+
+ if (!isRedacted) {
+ axisEntry->set_value(coords.values[axisIndex]);
+ }
}
}
}
void AndroidInputEventProtoConverter::toProtoKeyEvent(const TracedKeyEvent& event,
- proto::AndroidKeyEvent& outProto) {
+ proto::AndroidKeyEvent& outProto,
+ bool isRedacted) {
outProto.set_event_id(event.id);
outProto.set_event_time_nanos(event.eventTime);
outProto.set_down_time_nanos(event.downTime);
outProto.set_source(event.source);
outProto.set_action(event.action);
outProto.set_device_id(event.deviceId);
- outProto.set_display_id(event.displayId);
- outProto.set_key_code(event.keyCode);
- outProto.set_scan_code(event.scanCode);
- outProto.set_meta_state(event.metaState);
+ outProto.set_display_id(event.displayId.val());
outProto.set_repeat_count(event.repeatCount);
outProto.set_flags(event.flags);
outProto.set_policy_flags(event.policyFlags);
+
+ if (!isRedacted) {
+ outProto.set_key_code(event.keyCode);
+ outProto.set_scan_code(event.scanCode);
+ outProto.set_meta_state(event.metaState);
+ }
}
void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(
- const InputTracingBackendInterface::WindowDispatchArgs& args,
- proto::AndroidWindowInputDispatchEvent& outProto) {
+ const WindowDispatchArgs& args, proto::AndroidWindowInputDispatchEvent& outProto,
+ bool isRedacted) {
std::visit([&](auto entry) { outProto.set_event_id(entry.id); }, args.eventEntry);
outProto.set_vsync_id(args.vsyncId);
outProto.set_window_id(args.windowId);
outProto.set_resolved_flags(args.resolvedFlags);
+ if (isRedacted) {
+ return;
+ }
if (auto* motion = std::get_if<TracedMotionEvent>(&args.eventEntry); motion != nullptr) {
for (size_t i = 0; i < motion->pointerProperties.size(); i++) {
auto* pointerProto = outProto.add_dispatched_pointer();
@@ -91,7 +123,8 @@
const auto& coords = motion->pointerCoords[i];
const auto coordsInWindow =
- MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords);
+ MotionEvent::calculateTransformedCoords(motion->source, motion->flags,
+ args.transform, coords);
auto bits = BitSet64(coords.bits);
for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) {
const uint32_t axis = bits.clearFirstMarkedBit();
@@ -106,4 +139,65 @@
}
}
+impl::TraceConfig AndroidInputEventProtoConverter::parseConfig(
+ proto::AndroidInputEventConfig::Decoder& protoConfig) {
+ if (protoConfig.has_mode() &&
+ protoConfig.mode() == proto::AndroidInputEventConfig::TRACE_MODE_TRACE_ALL) {
+ // User has requested the preset for maximal tracing
+ return CONFIG_TRACE_ALL;
+ }
+
+ impl::TraceConfig config;
+
+ // Parse trace flags
+ if (protoConfig.has_trace_dispatcher_input_events() &&
+ protoConfig.trace_dispatcher_input_events()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS;
+ }
+ if (protoConfig.has_trace_dispatcher_window_dispatch() &&
+ protoConfig.trace_dispatcher_window_dispatch()) {
+ config.flags |= impl::TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH;
+ }
+
+ // Parse trace rules
+ auto rulesIt = protoConfig.rules();
+ while (rulesIt) {
+ proto::AndroidInputEventConfig::TraceRule::Decoder protoRule{rulesIt->as_bytes()};
+ config.rules.emplace_back();
+ auto& rule = config.rules.back();
+
+ rule.level = protoRule.has_trace_level()
+ ? static_cast<impl::TraceLevel>(protoRule.trace_level())
+ : impl::TraceLevel::TRACE_LEVEL_NONE;
+
+ if (protoRule.has_match_all_packages()) {
+ auto pkgIt = protoRule.match_all_packages();
+ while (pkgIt) {
+ rule.matchAllPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_any_packages()) {
+ auto pkgIt = protoRule.match_any_packages();
+ while (pkgIt) {
+ rule.matchAnyPackages.emplace_back(pkgIt->as_std_string());
+ pkgIt++;
+ }
+ }
+
+ if (protoRule.has_match_secure()) {
+ rule.matchSecure = protoRule.match_secure();
+ }
+
+ if (protoRule.has_match_ime_connection_active()) {
+ rule.matchImeConnectionActive = protoRule.match_ime_connection_active();
+ }
+
+ rulesIt++;
+ }
+
+ return config;
+}
+
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
index 8a46f15..887913f 100644
--- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
+++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.h
@@ -16,9 +16,11 @@
#pragma once
+#include <perfetto/config/android/android_input_event_config.pbzero.h>
#include <perfetto/trace/android/android_input_event.pbzero.h>
#include "InputTracingBackendInterface.h"
+#include "InputTracingPerfettoBackendConfig.h"
namespace proto = perfetto::protos::pbzero;
@@ -30,10 +32,14 @@
class AndroidInputEventProtoConverter {
public:
static void toProtoMotionEvent(const TracedMotionEvent& event,
- proto::AndroidMotionEvent& outProto);
- static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto);
- static void toProtoWindowDispatchEvent(const InputTracingBackendInterface::WindowDispatchArgs&,
- proto::AndroidWindowInputDispatchEvent& outProto);
+ proto::AndroidMotionEvent& outProto, bool isRedacted);
+ static void toProtoKeyEvent(const TracedKeyEvent& event, proto::AndroidKeyEvent& outProto,
+ bool isRedacted);
+ static void toProtoWindowDispatchEvent(const WindowDispatchArgs&,
+ proto::AndroidWindowInputDispatchEvent& outProto,
+ bool isRedacted);
+
+ static impl::TraceConfig parseConfig(proto::AndroidInputEventConfig::Decoder& protoConfig);
};
} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index be09013..5d2b854 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -19,6 +19,7 @@
#include "InputTracer.h"
#include <android-base/logging.h>
+#include <private/android_filesystem_config.h>
namespace android::inputdispatcher::trace::impl {
@@ -30,7 +31,7 @@
using V::operator()...;
};
-TracedEvent createTracedEvent(const MotionEntry& e) {
+TracedEvent createTracedEvent(const MotionEntry& e, EventType type) {
return TracedMotionEvent{e.id,
e.eventTime,
e.policyFlags,
@@ -50,13 +51,45 @@
e.yCursorPosition,
e.downTime,
e.pointerProperties,
- e.pointerCoords};
+ e.pointerCoords,
+ type};
}
-TracedEvent createTracedEvent(const KeyEntry& e) {
+TracedEvent createTracedEvent(const KeyEntry& e, EventType type) {
return TracedKeyEvent{e.id, e.eventTime, e.policyFlags, e.deviceId, e.source,
e.displayId, e.action, e.keyCode, e.scanCode, e.metaState,
- e.downTime, e.flags, e.repeatCount};
+ e.downTime, e.flags, e.repeatCount, type};
+}
+
+void writeEventToBackend(const TracedEvent& event, const TracedEventMetadata metadata,
+ InputTracingBackendInterface& backend) {
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e, metadata); },
+ [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e, metadata); }},
+ event);
+}
+
+inline auto getId(const trace::TracedEvent& v) {
+ return std::visit([](const auto& event) { return event.id; }, v);
+}
+
+// Helper class to extract relevant information from InputTarget.
+struct InputTargetInfo {
+ gui::Uid uid;
+ bool isSecureWindow;
+};
+
+InputTargetInfo getTargetInfo(const InputTarget& target) {
+ if (target.windowHandle == nullptr) {
+ if (!target.connection->monitor) {
+ LOG(FATAL) << __func__ << ": Window is not set for non-monitor target";
+ }
+ // This is a global monitor, assume its target is the system.
+ return {.uid = gui::Uid{AID_SYSTEM}, .isSecureWindow = false};
+ }
+ 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
@@ -67,97 +100,186 @@
: mBackend(std::move(backend)) {}
std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
- TracedEvent traced;
+ // This is a newly traced inbound event. Create a new state to track it and its derived events.
+ auto eventState = std::make_shared<EventState>(*this);
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
+ eventState->events.emplace_back(createTracedEvent(motion, EventType::INBOUND));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- traced = createTracedEvent(key);
+ eventState->events.emplace_back(createTracedEvent(key, EventType::INBOUND));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
- return std::make_unique<EventTrackerImpl>(*this, std::move(traced));
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/false);
+}
+
+std::unique_ptr<EventTrackerInterface> InputTracer::createTrackerForSyntheticEvent() {
+ // Create a new EventState to track events derived from this tracker.
+ return std::make_unique<EventTrackerImpl>(std::make_shared<EventState>(*this),
+ /*isDerived=*/false);
}
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
- LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
+ auto& eventState = getState(cookie);
+ const InputTargetInfo& targetInfo = getTargetInfo(target);
+ if (eventState->isEventProcessingComplete) {
+ // Disallow adding new targets after eventProcessingComplete() is called.
+ if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
+ LOG(FATAL) << __func__ << ": Cannot add new target after eventProcessingComplete";
+ }
+ return;
}
- // TODO(b/210460522): Determine if the event is sensitive based on the target.
+ if (isDerivedCookie(cookie)) {
+ // Disallow adding new targets from a derived cookie.
+ if (eventState->metadata.targets.count(targetInfo.uid) == 0) {
+ LOG(FATAL) << __func__ << ": Cannot add new target from a derived cookie";
+ }
+ return;
+ }
+
+ eventState->metadata.targets.emplace(targetInfo.uid);
+ eventState->metadata.isSecure |= targetInfo.isSecureWindow;
}
-void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
- auto& cookieState = getState(cookie);
- if (!cookieState) {
+void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie,
+ nsecs_t processingTimestamp) {
+ if (isDerivedCookie(cookie)) {
+ LOG(FATAL) << "Event processing cannot be set from a derived cookie.";
+ }
+ auto& eventState = getState(cookie);
+ if (eventState->isEventProcessingComplete) {
LOG(FATAL) << "Traced event was already logged. "
"eventProcessingComplete() was likely called more than once.";
}
-
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- cookieState->event);
- cookieState.reset();
+ eventState->onEventProcessingComplete(processingTimestamp);
}
-void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
- const EventTrackerInterface* cookie) {
- const EventEntry& entry = *dispatchEntry.eventEntry;
+std::unique_ptr<EventTrackerInterface> InputTracer::traceDerivedEvent(
+ const EventEntry& entry, const EventTrackerInterface& originalEventCookie) {
+ // This is an event derived from an already-established event. Use the same state to track
+ // this event too.
+ auto eventState = getState(originalEventCookie);
- TracedEvent traced;
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
+ eventState->events.emplace_back(createTracedEvent(motion, EventType::SYNTHESIZED));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- traced = createTracedEvent(key);
+ eventState->events.emplace_back(createTracedEvent(key, EventType::SYNTHESIZED));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
- if (!cookie) {
- // This event was not tracked as an inbound event, so trace it now.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- traced);
+ if (eventState->isEventProcessingComplete) {
+ // It is possible for a derived event to be dispatched some time after the original event
+ // 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();
+ writeEventToBackend(event, eventState->metadata, *mBackend);
+ }
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
+}
+
+void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
+ const EventTrackerInterface& cookie) {
+ auto& eventState = getState(cookie);
+ const EventEntry& entry = *dispatchEntry.eventEntry;
+ const int32_t eventId = entry.id;
+ // TODO(b/328618922): Remove resolved key repeats after making repeatCount non-mutable.
+ // The KeyEntry's repeatCount is mutable and can be modified after an event is initially traced,
+ // so we need to find the repeatCount at the time of dispatching to trace it accurately.
+ int32_t resolvedKeyRepeatCount = 0;
+ if (entry.type == EventEntry::Type::KEY) {
+ resolvedKeyRepeatCount = static_cast<const KeyEntry&>(entry).repeatCount;
+ }
+
+ auto tracedEventIt =
+ std::find_if(eventState->events.begin(), eventState->events.end(),
+ [eventId](const auto& event) { return eventId == getId(event); });
+ if (tracedEventIt == eventState->events.end()) {
+ LOG(FATAL)
+ << __func__
+ << ": Failed to find a previously traced event that matches the dispatched event";
+ }
+
+ if (eventState->metadata.targets.count(dispatchEntry.targetUid) == 0) {
+ LOG(FATAL) << __func__ << ": Event is being dispatched to UID that it is not targeting";
}
// The vsyncId only has meaning if the event is targeting a window.
const int32_t windowId = dispatchEntry.windowId.value_or(0);
const int32_t vsyncId = dispatchEntry.windowId.has_value() ? dispatchEntry.vsyncId : 0;
- mBackend->traceWindowDispatch({std::move(traced), dispatchEntry.deliveryTime,
- dispatchEntry.resolvedFlags, dispatchEntry.targetUid, vsyncId,
- windowId, dispatchEntry.transform, dispatchEntry.rawTransform,
- /*hmac=*/{}});
+ // TODO(b/210460522): Pass HMAC into traceEventDispatch.
+ const WindowDispatchArgs windowDispatchArgs{*tracedEventIt,
+ dispatchEntry.deliveryTime,
+ dispatchEntry.resolvedFlags,
+ dispatchEntry.targetUid,
+ vsyncId,
+ windowId,
+ dispatchEntry.transform,
+ dispatchEntry.rawTransform,
+ /*hmac=*/{},
+ resolvedKeyRepeatCount};
+ if (eventState->isEventProcessingComplete) {
+ mBackend->traceWindowDispatch(std::move(windowDispatchArgs), eventState->metadata);
+ } else {
+ eventState->pendingDispatchArgs.emplace_back(std::move(windowDispatchArgs));
+ }
}
-std::optional<InputTracer::EventState>& InputTracer::getState(const EventTrackerInterface& cookie) {
+std::shared_ptr<InputTracer::EventState>& InputTracer::getState(
+ const EventTrackerInterface& cookie) {
return static_cast<const EventTrackerImpl&>(cookie).mState;
}
-// --- InputTracer::EventTrackerImpl ---
+bool InputTracer::isDerivedCookie(const EventTrackerInterface& cookie) {
+ return static_cast<const EventTrackerImpl&>(cookie).mIsDerived;
+}
-InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
- : mTracer(tracer), mState(event) {}
+// --- InputTracer::EventState ---
-InputTracer::EventTrackerImpl::~EventTrackerImpl() {
- if (!mState) {
+void InputTracer::EventState::onEventProcessingComplete(nsecs_t processingTimestamp) {
+ metadata.processingTimestamp = processingTimestamp;
+ metadata.isImeConnectionActive = tracer.mIsImeConnectionActive;
+
+ // Write all of the events known so far to the trace.
+ for (const auto& event : events) {
+ writeEventToBackend(event, metadata, *tracer.mBackend);
+ }
+ // Write all pending dispatch args to the trace.
+ for (const auto& windowDispatchArgs : pendingDispatchArgs) {
+ auto tracedEventIt =
+ std::find_if(events.begin(), events.end(),
+ [id = getId(windowDispatchArgs.eventEntry)](const auto& event) {
+ return id == getId(event);
+ });
+ if (tracedEventIt == events.end()) {
+ LOG(FATAL) << __func__
+ << ": Failed to find a previously traced event that matches the dispatched "
+ "event";
+ }
+ tracer.mBackend->traceWindowDispatch(windowDispatchArgs, metadata);
+ }
+ pendingDispatchArgs.clear();
+
+ isEventProcessingComplete = true;
+}
+
+InputTracer::EventState::~EventState() {
+ if (isEventProcessingComplete) {
// This event has already been written to the trace as expected.
return;
}
- // We're still holding on to the state, which means it hasn't yet been written to the trace.
- // Write it to the trace now.
- // TODO(b/210460522): Determine why/where the event is being destroyed before
- // eventProcessingComplete() is called.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
- mState->event);
- mState.reset();
+ // The event processing was never marked as complete, so do it now.
+ // We should never end up here in normal operation. However, in tests, it's possible that we
+ // stop and destroy InputDispatcher without waiting for it to finish processing events, at
+ // which point an event (and thus its EventState) may be destroyed before processing finishes.
+ onEventProcessingComplete(systemTime(CLOCK_MONOTONIC));
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index c8b25c9..96e619c 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -42,37 +42,55 @@
InputTracer& operator=(const InputTracer&) = delete;
std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override;
+ std::unique_ptr<EventTrackerInterface> createTrackerForSyntheticEvent() override;
void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override;
- void eventProcessingComplete(const EventTrackerInterface&) override;
- void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
+ void eventProcessingComplete(const EventTrackerInterface&,
+ nsecs_t processingTimestamp) override;
+ 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.
+ // The state of a tracked event, shared across all events derived from the original event.
struct EventState {
- const TracedEvent event;
- // TODO(b/210460522): Add additional args for tracking event sensitivity and
- // dispatch target UIDs.
+ explicit inline EventState(InputTracer& tracer) : tracer(tracer){};
+ ~EventState();
+
+ void onEventProcessingComplete(nsecs_t processingTimestamp);
+
+ InputTracer& tracer;
+ std::vector<TracedEvent> events;
+ bool isEventProcessingComplete{false};
+ // A queue to hold dispatch args from being traced until event processing is complete.
+ std::vector<WindowDispatchArgs> pendingDispatchArgs;
+ // The metadata should not be modified after event processing is complete.
+ TracedEventMetadata metadata{};
};
// Get the event state associated with a tracking cookie.
- std::optional<EventState>& getState(const EventTrackerInterface&);
+ std::shared_ptr<EventState>& getState(const EventTrackerInterface&);
+ bool isDerivedCookie(const EventTrackerInterface&);
// Implementation of the event tracker cookie. The cookie holds the event state directly for
// convenience to avoid the overhead of tracking the state separately in InputTracer.
class EventTrackerImpl : public EventTrackerInterface {
public:
- explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
- virtual ~EventTrackerImpl() override;
+ inline EventTrackerImpl(const std::shared_ptr<EventState>& state, bool isDerivedEvent)
+ : mState(state), mIsDerived(isDerivedEvent) {}
+ EventTrackerImpl(const EventTrackerImpl&) = default;
private:
- InputTracer& mTracer;
- // This event tracker cookie will only hold the state as long as it has not been written
- // to the trace. The state is released when the event is written to the trace.
- mutable std::optional<EventState> mState;
+ mutable std::shared_ptr<EventState> mState;
+ const bool mIsDerived;
- friend std::optional<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend std::shared_ptr<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend bool InputTracer::isDerivedCookie(const EventTrackerInterface&);
};
};
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index c6cd7de..f5e4e59 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -54,6 +54,14 @@
virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 0;
/**
+ * Create a trace tracker for a synthetic event that does not stem from an inbound input event.
+ * This includes things like generating cancellations or down events for various reasons,
+ * such as ANR, pilfering, transfer touch, etc. Any key or motion events generated for this
+ * synthetic event should be traced as a derived event using {@link #traceDerivedEvent}.
+ */
+ virtual std::unique_ptr<EventTrackerInterface> createTrackerForSyntheticEvent() = 0;
+
+ /**
* Notify the tracer that the traced event will be sent to the given InputTarget.
* The tracer may change how the event is logged depending on the target. For example,
* events targeting certain UIDs may be logged as sensitive events.
@@ -73,15 +81,34 @@
* outside of our control, such as how long apps take to respond, so we don't want to depend on
* that.
*/
- virtual void eventProcessingComplete(const EventTrackerInterface&) = 0;
+ virtual void eventProcessingComplete(const EventTrackerInterface&,
+ nsecs_t processingTimestamp) = 0;
+
+ /**
+ * Trace an input event that is derived from another event. This is used in cases where an event
+ * is modified from the original, such as when a touch is split across multiple windows, or
+ * when a HOVER_MOVE event is modified to be a HOVER_EXIT, etc. The original event's tracker
+ * must be provided, and a new EventTracker is returned that should be used to track the event's
+ * lifecycle.
+ *
+ * NOTE: The derived tracker cannot be used to change the targets of the original event, meaning
+ * it cannot be used with {@link #dispatchToTargetHint} or {@link eventProcessingComplete}.
+ */
+ virtual std::unique_ptr<EventTrackerInterface> traceDerivedEvent(
+ const EventEntry&, const EventTrackerInterface& originalEventTracker) = 0;
/**
* Trace an input event being successfully dispatched to a window. The dispatched event may
- * be a previously traced inbound event, or it may be a synthesized event that has not been
- * previously traced. For inbound events that were previously traced, the EventTracker cookie
- * must be provided. For events that were not previously traced, the cookie must be null.
+ * be a previously traced inbound event, or it may be a synthesized event. All dispatched events
+ * must have been previously traced, so the trace tracker associated with the event must be
+ * provided.
*/
- virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) = 0;
+ 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 b0eadfe..761d619 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -21,12 +21,27 @@
#include <ui/Transform.h>
#include <array>
+#include <set>
#include <variant>
#include <vector>
namespace android::inputdispatcher::trace {
/**
+ * Describes the type of this event being traced, with respect to InputDispatcher.
+ */
+enum class EventType {
+ // This is an event that was reported through the InputListener interface or was injected.
+ INBOUND,
+ // This is an event that was synthesized within InputDispatcher; either being derived
+ // from an inbound event (e.g. a split motion event), or synthesized completely
+ // (e.g. a CANCEL event generated when the inbound stream is not canceled).
+ SYNTHESIZED,
+
+ ftl_last = SYNTHESIZED,
+};
+
+/**
* A representation of an Android KeyEvent used by the tracing backend.
*/
struct TracedKeyEvent {
@@ -35,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;
@@ -43,6 +58,7 @@
nsecs_t downTime;
int32_t flags;
int32_t repeatCount;
+ EventType eventType;
};
/**
@@ -54,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;
@@ -69,11 +85,38 @@
nsecs_t downTime;
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
+ EventType eventType;
};
/** A representation of a traced input event. */
using TracedEvent = std::variant<TracedKeyEvent, TracedMotionEvent>;
+/** Additional information about an input event being traced. */
+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;
+ // The timestamp for when the dispatching decisions were made for the event by the system.
+ nsecs_t processingTimestamp;
+};
+
+/** Additional information about an input event being dispatched to a window. */
+struct WindowDispatchArgs {
+ TracedEvent eventEntry;
+ nsecs_t deliveryTime;
+ int32_t resolvedFlags;
+ gui::Uid targetUid;
+ int64_t vsyncId;
+ int32_t windowId;
+ ui::Transform transform;
+ ui::Transform rawTransform;
+ std::array<uint8_t, 32> hmac;
+ int32_t resolvedKeyRepeatCount;
+};
+
/**
* An interface for the tracing backend, used for setting a custom backend for testing.
*/
@@ -82,24 +125,13 @@
virtual ~InputTracingBackendInterface() = default;
/** Trace a KeyEvent. */
- virtual void traceKeyEvent(const TracedKeyEvent&) = 0;
+ virtual void traceKeyEvent(const TracedKeyEvent&, const TracedEventMetadata&) = 0;
/** Trace a MotionEvent. */
- virtual void traceMotionEvent(const TracedMotionEvent&) = 0;
+ virtual void traceMotionEvent(const TracedMotionEvent&, const TracedEventMetadata&) = 0;
/** Trace an event being sent to a window. */
- struct WindowDispatchArgs {
- TracedEvent eventEntry;
- nsecs_t deliveryTime;
- int32_t resolvedFlags;
- gui::Uid targetUid;
- int64_t vsyncId;
- int32_t windowId;
- ui::Transform transform;
- ui::Transform rawTransform;
- std::array<uint8_t, 32> hmac;
- };
- virtual void traceWindowDispatch(const WindowDispatchArgs&) = 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 6fcc015..77b5c2e 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp
@@ -21,9 +21,12 @@
#include "AndroidInputEventProtoConverter.h"
#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
#include <perfetto/trace/android/android_input_event.pbzero.h>
#include <perfetto/trace/android/winscope_extensions.pbzero.h>
#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h>
+#include <private/android_filesystem_config.h>
+#include <utils/String16.h>
namespace android::inputdispatcher::trace::impl {
@@ -31,29 +34,175 @@
constexpr auto INPUT_EVENT_TRACE_DATA_SOURCE_NAME = "android.input.inputevent";
+bool isPermanentlyAllowed(gui::Uid uid) {
+ switch (uid.val()) {
+ case AID_SYSTEM:
+ case AID_SHELL:
+ case AID_ROOT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+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 ---
-void PerfettoBackend::InputEventDataSource::OnStart(const perfetto::DataSourceBase::StartArgs&) {
- LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+PerfettoBackend::InputEventDataSource::InputEventDataSource() : mInstanceId(sNextInstanceId++) {}
+
+void PerfettoBackend::InputEventDataSource::OnSetup(const InputEventDataSource::SetupArgs& args) {
+ LOG(INFO) << "Setting up perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
+ const auto rawConfig = args.config->android_input_event_config_raw();
+ auto protoConfig = perfetto::protos::pbzero::AndroidInputEventConfig::Decoder{rawConfig};
+
+ mConfig = AndroidInputEventProtoConverter::parseConfig(protoConfig);
}
-void PerfettoBackend::InputEventDataSource::OnStop(const perfetto::DataSourceBase::StopArgs&) {
- LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME;
+void PerfettoBackend::InputEventDataSource::OnStart(const InputEventDataSource::StartArgs&) {
+ LOG(INFO) << "Starting perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
+}
+
+void PerfettoBackend::InputEventDataSource::OnStop(const InputEventDataSource::StopArgs&) {
+ LOG(INFO) << "Stopping perfetto trace for: " << INPUT_EVENT_TRACE_DATA_SOURCE_NAME
+ << ", instanceId: " << mInstanceId;
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) { ctx.Flush(); });
}
+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(packageManager, package));
+ }
+ for (const auto& package : rule.matchAnyPackages) {
+ mUidMap->emplace(package, getPackageUid(packageManager, package));
+ }
+ }
+}
+
+bool PerfettoBackend::InputEventDataSource::shouldIgnoreTracedInputEvent(
+ const EventType& type) const {
+ if (!getFlags().test(TraceFlag::TRACE_DISPATCHER_INPUT_EVENTS)) {
+ // Ignore all input events.
+ return true;
+ }
+ if (!getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH) &&
+ type != EventType::INBOUND) {
+ // When window dispatch tracing is disabled, ignore any events that are not inbound events.
+ return true;
+ }
+ return false;
+}
+
+TraceLevel PerfettoBackend::InputEventDataSource::resolveTraceLevel(
+ 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, metadata)) {
+ return rule.level;
+ }
+ }
+ // The event is not traced if it matched zero rules.
+ return TraceLevel::TRACE_LEVEL_NONE;
+}
+
+bool PerfettoBackend::InputEventDataSource::ruleMatches(const TraceRule& rule,
+ 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 != 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(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); });
+ })) {
+ return false;
+ }
+
+ // Match the event if any of its target packages are allowed in the "match any" list.
+ if (!rule.matchAnyPackages.empty() &&
+ !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); });
+ })) {
+ return false;
+ }
+
+ // The event matches all matchers specified in the rule.
+ return true;
+}
+
// --- 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() {
// 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.
@@ -65,37 +214,89 @@
});
}
-void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event) {
+void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event,
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
+ if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
+ tracePacket->set_timestamp(metadata.processingTimestamp);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
tracePacket->set_winscope_extensions());
auto* inputEvent = winscopeExtensions->set_android_input_event();
- auto* dispatchMotion = inputEvent->set_dispatcher_motion_event();
- AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion);
+ auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted()
+ : inputEvent->set_dispatcher_motion_event();
+ AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted);
});
}
-void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event) {
+void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event,
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
+ if (dataSource->shouldIgnoreTracedInputEvent(event.eventType)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
+ tracePacket->set_timestamp(metadata.processingTimestamp);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
tracePacket->set_winscope_extensions());
auto* inputEvent = winscopeExtensions->set_android_input_event();
- auto* dispatchKey = inputEvent->set_dispatcher_key_event();
- AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey);
+ auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted()
+ : inputEvent->set_dispatcher_key_event();
+ AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted);
});
}
-void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
+void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
+ const TracedEventMetadata& metadata) {
InputEventDataSource::Trace([&](InputEventDataSource::TraceContext ctx) {
+ auto dataSource = ctx.GetDataSourceLocked();
+ if (!dataSource.valid()) {
+ return;
+ }
+ dataSource->initializeUidMap();
+ if (!dataSource->getFlags().test(TraceFlag::TRACE_DISPATCHER_WINDOW_DISPATCH)) {
+ return;
+ }
+ const TraceLevel traceLevel = dataSource->resolveTraceLevel(metadata);
+ if (traceLevel == TraceLevel::TRACE_LEVEL_NONE) {
+ return;
+ }
+ const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED;
auto tracePacket = ctx.NewTracePacket();
+ tracePacket->set_timestamp(dispatchArgs.deliveryTime);
+ tracePacket->set_timestamp_clock_id(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>(
tracePacket->set_winscope_extensions());
- auto* inputEventProto = winscopeExtensions->set_android_input_event();
- auto* dispatchEventProto = inputEventProto->set_dispatcher_window_dispatch_event();
- AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs,
- *dispatchEventProto);
+ auto* inputEvent = winscopeExtensions->set_android_input_event();
+ auto* dispatchEvent = isRedacted
+ ? inputEvent->set_dispatcher_window_dispatch_event_redacted()
+ : inputEvent->set_dispatcher_window_dispatch_event();
+ AndroidInputEventProtoConverter::toProtoWindowDispatchEvent(dispatchArgs, *dispatchEvent,
+ isRedacted);
});
}
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
index fefcfb3..d0bab06 100644
--- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.h
@@ -18,8 +18,13 @@
#include "InputTracingBackendInterface.h"
+#include "InputTracingPerfettoBackendConfig.h"
+
+#include <android/content/pm/IPackageManagerNative.h>
+#include <ftl/flags.h>
#include <perfetto/tracing.h>
#include <mutex>
+#include <set>
namespace android::inputdispatcher::trace::impl {
@@ -45,22 +50,44 @@
*/
class PerfettoBackend : public InputTracingBackendInterface {
public:
- PerfettoBackend();
+ static bool sUseInProcessBackendForTest;
+ static std::function<sp<content::pm::IPackageManagerNative>()> sPackageManagerProvider;
+
+ explicit PerfettoBackend();
~PerfettoBackend() override = default;
- void traceKeyEvent(const TracedKeyEvent&) override;
- void traceMotionEvent(const TracedMotionEvent&) override;
- void traceWindowDispatch(const WindowDispatchArgs&) override;
-
- class InputEventDataSource : public perfetto::DataSource<InputEventDataSource> {
- public:
- void OnSetup(const SetupArgs&) override {}
- void OnStart(const StartArgs&) override;
- void OnStop(const StopArgs&) 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();
+
+ void OnSetup(const SetupArgs&) override;
+ void OnStart(const StartArgs&) override;
+ void OnStop(const StopArgs&) override;
+
+ void initializeUidMap();
+ bool shouldIgnoreTracedInputEvent(const EventType&) const;
+ inline ftl::Flags<TraceFlag> getFlags() const { return mConfig.flags; }
+ TraceLevel resolveTraceLevel(const TracedEventMetadata&) const;
+
+ private:
+ const int32_t mInstanceId;
+ TraceConfig mConfig;
+
+ bool ruleMatches(const TraceRule&, const TracedEventMetadata&) const;
+
+ std::optional<std::map<std::string, gui::Uid>> mUidMap;
+ };
+
static std::once_flag sDataSourceRegistrationFlag;
+ static std::atomic<int32_t> sNextInstanceId;
};
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h
new file mode 100644
index 0000000..536e32b
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackendConfig.h
@@ -0,0 +1,60 @@
+/*
+ * 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/enum.h>
+#include <ftl/flags.h>
+#include <perfetto/config/android/android_input_event_config.pbzero.h>
+#include <vector>
+
+namespace android::inputdispatcher::trace::impl {
+
+/** Flags representing the configurations that are enabled in the trace. */
+enum class TraceFlag : uint32_t {
+ // Trace details about input events processed by InputDispatcher.
+ TRACE_DISPATCHER_INPUT_EVENTS = 0x1,
+ // Trace details about an event being sent to a window by InputDispatcher.
+ TRACE_DISPATCHER_WINDOW_DISPATCH = 0x2,
+
+ ftl_last = TRACE_DISPATCHER_WINDOW_DISPATCH,
+};
+
+/** Representation of AndroidInputEventConfig::TraceLevel. */
+using TraceLevel = perfetto::protos::pbzero::AndroidInputEventConfig::TraceLevel;
+
+/** Representation of AndroidInputEventConfig::TraceRule. */
+struct TraceRule {
+ TraceLevel level;
+
+ std::vector<std::string> matchAllPackages;
+ std::vector<std::string> matchAnyPackages;
+ std::optional<bool> matchSecure;
+ std::optional<bool> matchImeConnectionActive;
+};
+
+/**
+ * A complete configuration for a tracing session.
+ *
+ * The trace rules are applied as documented in the perfetto config:
+ * /external/perfetto/protos/perfetto/config/android/android_input_event_config.proto
+ */
+struct TraceConfig {
+ ftl::Flags<TraceFlag> flags;
+ std::vector<TraceRule> rules;
+};
+
+} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
index 25bc227..3c3c15a 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.cpp
@@ -38,10 +38,10 @@
template <typename Backend>
ThreadedBackend<Backend>::ThreadedBackend(Backend&& innerBackend)
- : mTracerThread(
+ : mBackend(std::move(innerBackend)),
+ mTracerThread(
"InputTracer", [this]() { threadLoop(); },
- [this]() { mThreadWakeCondition.notify_all(); }),
- mBackend(std::move(innerBackend)) {}
+ [this]() { mThreadWakeCondition.notify_all(); }) {}
template <typename Backend>
ThreadedBackend<Backend>::~ThreadedBackend() {
@@ -53,23 +53,29 @@
}
template <typename Backend>
-void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event) {
+void ThreadedBackend<Backend>::traceMotionEvent(const TracedMotionEvent& event,
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(event);
+ mQueue.emplace_back(event, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
-void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event) {
+void ThreadedBackend<Backend>::traceKeyEvent(const TracedKeyEvent& event,
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(event);
+ mQueue.emplace_back(event, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
template <typename Backend>
-void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs) {
+void ThreadedBackend<Backend>::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs,
+ const TracedEventMetadata& metadata) {
std::scoped_lock lock(mLock);
- mQueue.emplace_back(dispatchArgs);
+ mQueue.emplace_back(dispatchArgs, metadata);
+ setIdleStatus(false);
mThreadWakeCondition.notify_all();
}
@@ -81,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;
}
@@ -93,17 +104,49 @@
// Trace the events into the backend without holding the lock to reduce the amount of
// work performed in the critical section.
- for (const auto& entry : entries) {
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend.traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e); },
+ for (const auto& [entry, traceArgs] : entries) {
+ std::visit(Visitor{[&](const TracedMotionEvent& e) {
+ mBackend.traceMotionEvent(e, traceArgs);
+ },
+ [&](const TracedKeyEvent& e) { mBackend.traceKeyEvent(e, traceArgs); },
[&](const WindowDispatchArgs& args) {
- mBackend.traceWindowDispatch(args);
+ mBackend.traceWindowDispatch(args, traceArgs);
}},
entry);
}
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 5776cf9..52a84c4 100644
--- a/services/inputflinger/dispatcher/trace/ThreadedBackend.h
+++ b/services/inputflinger/dispatcher/trace/ThreadedBackend.h
@@ -38,22 +38,38 @@
ThreadedBackend(Backend&& innerBackend);
~ThreadedBackend() override;
- void traceKeyEvent(const TracedKeyEvent&) override;
- void traceMotionEvent(const TracedMotionEvent&) override;
- void traceWindowDispatch(const WindowDispatchArgs&) 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;
- InputThread mTracerThread;
bool mThreadExit GUARDED_BY(mLock){false};
std::condition_variable mThreadWakeCondition;
Backend mBackend;
- using TraceEntry = std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>;
+ using TraceEntry =
+ std::pair<std::variant<TracedKeyEvent, TracedMotionEvent, WindowDispatchArgs>,
+ TracedEventMetadata>;
std::vector<TraceEntry> mQueue GUARDED_BY(mLock);
- using WindowDispatchArgs = InputTracingBackendInterface::WindowDispatchArgs;
+ 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..889ee09 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,
@@ -109,11 +106,15 @@
// The associations between input ports and display ports.
// Used to determine which DisplayViewport should be tied to which InputDevice.
- std::unordered_map<std::string, uint8_t> portAssociations;
+ std::unordered_map<std::string, uint8_t> inputPortToDisplayPortAssociations;
- // 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> inputPortToDisplayUniqueIdAssociations;
+
+ // 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> inputDeviceDescriptorToDisplayUniqueIdAssociations;
// 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::LogicalDisplayId::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;
@@ -312,9 +310,6 @@
/* Called by the heartbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
- /* Returns true if the input device is enabled. */
- virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
-
/* Makes the reader start processing events from the kernel. */
virtual status_t start() = 0;
@@ -369,7 +364,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,
@@ -396,6 +391,12 @@
/* Sysfs node change reported. Recreate device if required to incorporate the new sysfs nodes */
virtual void sysfsNodeChanged(const std::string& sysfsNodePath) = 0;
+
+ /* Get the ID of the InputDevice that was used most recently.
+ *
+ * Returns ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID if no device has been used since boot.
+ */
+ virtual DeviceId getLastUsedInputDeviceId() = 0;
};
// --- TouchAffineTransformation ---
@@ -445,10 +446,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 +475,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::LogicalDisplayId::INVALID) = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/NotifyArgs.h b/services/inputflinger/include/NotifyArgs.h
index 736b1e0..db417cf 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::LogicalDisplayId::INVALID};
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::LogicalDisplayId::INVALID};
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 e4363a4..5b94d57 100644
--- a/services/inputflinger/include/NotifyArgsBuilders.h
+++ b/services/inputflinger/include/NotifyArgsBuilders.h
@@ -19,9 +19,9 @@
#include <NotifyArgs.h>
#include <android/input.h>
#include <attestation/HmacKeyManager.h>
-#include <gui/constants.h>
#include <input/Input.h>
#include <input/InputEventBuilders.h>
+#include <input/Keyboard.h>
#include <utils/Timers.h> // for nsecs_t, systemTime
#include <vector>
@@ -30,8 +30,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 +55,7 @@
return *this;
}
- MotionArgsBuilder& displayId(int32_t displayId) {
+ MotionArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -97,7 +100,7 @@
return *this;
}
- NotifyMotionArgs build() {
+ NotifyMotionArgs build() const {
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
for (const PointerBuilder& pointer : mPointers) {
@@ -106,17 +109,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)) {
- mRawXCursorPosition = pointerCoords[0].getX();
- mRawYCursorPosition = pointerCoords[0].getY();
+ !MotionEvent::isValidCursorPosition(mRawXCursorPosition, mRawYCursorPosition) &&
+ BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_X) &&
+ BitSet64::hasBit(pointerCoords[0].bits, AMOTION_EVENT_AXIS_Y)) {
+ 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,
@@ -135,19 +138,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::LogicalDisplayId::DEFAULT};
uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
int32_t mActionButton{0};
int32_t mButtonState{0};
@@ -161,7 +165,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);
@@ -183,7 +187,7 @@
return *this;
}
- KeyArgsBuilder& displayId(int32_t displayId) {
+ KeyArgsBuilder& displayId(ui::LogicalDisplayId displayId) {
mDisplayId = displayId;
return *this;
}
@@ -203,8 +207,14 @@
return *this;
}
+ KeyArgsBuilder& metaState(int32_t metaState) {
+ mMetaState |= metaState;
+ mMetaState = normalizeMetaState(/*oldMetaState=*/mMetaState);
+ return *this;
+ }
+
NotifyKeyArgs build() const {
- return {InputEvent::nextId(),
+ return {mEventId,
mEventTime,
/*readTime=*/mEventTime,
mDeviceId,
@@ -220,12 +230,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::LogicalDisplayId::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..7a85c12 100644
--- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h
+++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h
@@ -53,7 +53,11 @@
* @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;
+
+ /* Returns true if any InputConnection is currently active. */
+ virtual bool isInputMethodConnectionActive() = 0;
};
} // namespace android
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index c44486f..e34ed0f 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,14 @@
/* 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 for the specified
+ * displayId. This flag can only be reset with resetSkipScreenshotFlags()
+ */
+ virtual void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) = 0;
+
+ /* Resets the flag to skip screenshot of the pointer indicators for all displays. */
+ virtual void clearSkipScreenshotFlags() = 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 4d8ffb6..2daf195 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";
}
@@ -231,6 +237,12 @@
mIsExternal = mClasses.test(InputDeviceClass::EXTERNAL);
mHasMic = mClasses.test(InputDeviceClass::MIC);
+ // Update keyboard type
+ if (mClasses.test(InputDeviceClass::KEYBOARD)) {
+ mContext->getKeyboardClassifier().notifyKeyboardChanged(mId, mIdentifier, mClasses.get());
+ mKeyboardType = mContext->getKeyboardClassifier().getKeyboardType(mId);
+ }
+
using Change = InputReaderConfiguration::Change;
if (!changes.any() || !isIgnored()) {
@@ -269,22 +281,42 @@
// 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.inputPortToDisplayPortAssociations;
+ 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.inputDeviceDescriptorToDisplayUniqueIdAssociations;
+ 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()) {
const std::unordered_map<std::string, uint8_t>& ports =
- readerConfig.portAssociations;
+ readerConfig.inputPortToDisplayPortAssociations;
const auto& displayPort = ports.find(inputPort);
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.inputPortToDisplayUniqueIdAssociations;
+ const auto& displayUniqueIdByPort = displayUniqueIdsByPort.find(inputPort);
+ if (displayUniqueIdByPort != displayUniqueIdsByPort.end()) {
+ mAssociatedDisplayUniqueIdByPort = displayUniqueIdByPort->second;
}
}
}
@@ -299,13 +331,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());
}
}
@@ -369,7 +409,7 @@
mDropUntilNextSync = true;
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
- out += mapper.process(rawEvent);
+ out += mapper.process(*rawEvent);
});
}
--count;
@@ -408,8 +448,10 @@
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::LogicalDisplayId::INVALID),
+ {mShouldSmoothScroll}, isEnabled());
+ outDeviceInfo.setKeyboardType(static_cast<int32_t>(mKeyboardType));
for_each_mapper(
[&outDeviceInfo](InputMapper& mapper) { mapper.populateDeviceInfo(outDeviceInfo); });
@@ -482,13 +524,9 @@
// Keyboard-like devices.
uint32_t keyboardSource = 0;
- int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC;
if (classes.test(InputDeviceClass::KEYBOARD)) {
keyboardSource |= AINPUT_SOURCE_KEYBOARD;
}
- if (classes.test(InputDeviceClass::ALPHAKEY)) {
- keyboardType = AINPUT_KEYBOARD_TYPE_ALPHABETIC;
- }
if (classes.test(InputDeviceClass::DPAD)) {
keyboardSource |= AINPUT_SOURCE_DPAD;
}
@@ -497,8 +535,8 @@
}
if (keyboardSource != 0) {
- mappers.push_back(createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig,
- keyboardSource, keyboardType));
+ mappers.push_back(
+ createInputMapper<KeyboardInputMapper>(contextPtr, readerConfig, keyboardSource));
}
// Cursor-like devices.
@@ -507,10 +545,7 @@
}
// Touchscreens and touchpad devices.
- static const bool ENABLE_TOUCHPAD_GESTURES_LIBRARY =
- sysprop::InputProperties::enable_touchpad_gestures_library().value_or(true);
- if (ENABLE_TOUCHPAD_GESTURES_LIBRARY && classes.test(InputDeviceClass::TOUCHPAD) &&
- classes.test(InputDeviceClass::TOUCH_MT)) {
+ 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));
@@ -668,14 +703,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(); });
}
@@ -698,6 +733,13 @@
return mController ? std::make_optional(mController->getEventHubId()) : std::nullopt;
}
+void InputDevice::setKeyboardType(KeyboardType keyboardType) {
+ if (mKeyboardType != keyboardType) {
+ mKeyboardType = keyboardType;
+ bumpGeneration();
+ }
+}
+
InputDeviceContext::InputDeviceContext(InputDevice& device, int32_t eventHubId)
: mDevice(device),
mContext(device.getContext()),
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 9608210..ab13ad4 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,6 +38,8 @@
namespace android {
+namespace {
+
/**
* Determines if the identifiers passed are a sub-devices. Sub-devices are physical devices
* that expose multiple input device paths such a keyboard that also has a touchpad input.
@@ -49,8 +51,8 @@
* inputs versus the same device plugged into multiple ports.
*/
-static bool isSubDevice(const InputDeviceIdentifier& identifier1,
- const InputDeviceIdentifier& identifier2) {
+bool isSubDevice(const InputDeviceIdentifier& identifier1,
+ const InputDeviceIdentifier& identifier2) {
return (identifier1.vendor == identifier2.vendor &&
identifier1.product == identifier2.product && identifier1.bus == identifier2.bus &&
identifier1.version == identifier2.version &&
@@ -58,7 +60,7 @@
identifier1.location == identifier2.location);
}
-static bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
+bool isStylusPointerGestureStart(const NotifyMotionArgs& motionArgs) {
const auto actionMasked = MotionEvent::getActionMasked(motionArgs.action);
if (actionMasked != AMOTION_EVENT_ACTION_HOVER_ENTER &&
actionMasked != AMOTION_EVENT_ACTION_DOWN &&
@@ -69,6 +71,28 @@
return isStylusToolType(motionArgs.pointerProperties[actionIndex].toolType);
}
+bool isNewGestureStart(const NotifyMotionArgs& motion) {
+ return motion.action == AMOTION_EVENT_ACTION_DOWN ||
+ motion.action == AMOTION_EVENT_ACTION_HOVER_ENTER;
+}
+
+bool isNewGestureStart(const NotifyKeyArgs& key) {
+ return key.action == AKEY_EVENT_ACTION_DOWN;
+}
+
+// Return the event's device ID if it marks the start of a new gesture.
+std::optional<DeviceId> getDeviceIdOfNewGesture(const NotifyArgs& args) {
+ if (const auto* motion = std::get_if<NotifyMotionArgs>(&args); motion != nullptr) {
+ return isNewGestureStart(*motion) ? std::make_optional(motion->deviceId) : std::nullopt;
+ }
+ if (const auto* key = std::get_if<NotifyKeyArgs>(&args); key != nullptr) {
+ return isNewGestureStart(*key) ? std::make_optional(key->deviceId) : std::nullopt;
+ }
+ return std::nullopt;
+}
+
+} // namespace
+
// --- InputReader ---
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
@@ -78,6 +102,7 @@
mEventHub(eventHub),
mPolicy(policy),
mNextListener(listener),
+ mKeyboardClassifier(std::make_unique<KeyboardClassifier>()),
mGlobalMetaState(AMETA_NONE),
mLedMetaState(AMETA_NONE),
mGeneration(1),
@@ -162,6 +187,11 @@
}
std::swap(notifyArgs, mPendingArgs);
+
+ // Keep track of the last used device
+ for (const NotifyArgs& args : notifyArgs) {
+ mLastUsedDeviceId = getDeviceIdOfNewGesture(args).value_or(mLastUsedDeviceId);
+ }
} // release lock
// Flush queued events out to the listener.
@@ -402,10 +432,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 +516,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;
@@ -890,18 +875,7 @@
return std::nullopt;
}
-bool InputReader::isInputDeviceEnabled(int32_t deviceId) {
- std::scoped_lock _l(mLock);
-
- InputDevice* device = findInputDeviceLocked(deviceId);
- if (device) {
- return device->isEnabled();
- }
- ALOGW("Ignoring invalid device id %" PRId32 ".", deviceId);
- 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 +889,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;
}
@@ -929,6 +902,11 @@
mEventHub->sysfsNodeChanged(sysfsNodePath);
}
+DeviceId InputReader::getLastUsedInputDeviceId() {
+ std::scoped_lock _l(mLock);
+ return mLastUsedDeviceId;
+}
+
void InputReader::dump(std::string& dump) {
std::scoped_lock _l(mLock);
@@ -1067,17 +1045,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);
@@ -1110,4 +1077,8 @@
return mIdGenerator.nextId();
}
+KeyboardClassifier& InputReader::ContextImpl::getKeyboardClassifier() {
+ return *mReader->mKeyboardClassifier;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp
index eabf591..49ad8b5 100644
--- a/services/inputflinger/reader/controller/PeripheralController.cpp
+++ b/services/inputflinger/reader/controller/PeripheralController.cpp
@@ -418,7 +418,11 @@
}
rawInfos.insert_or_assign(rawId, rawInfo.value());
// Check if this is a group LEDs for player ID
- std::regex lightPattern("([a-z]+)([0-9]+)");
+ // The name for the light has already been parsed and is the `function`
+ // value; for player ID lights the function is expected to be `player-#`.
+ // However, the Sony driver will use `sony#` instead on SIXAXIS
+ // gamepads.
+ std::regex lightPattern("(player|sony)-?([0-9]+)");
std::smatch results;
if (std::regex_match(rawInfo->name, results, lightPattern)) {
std::string commonName = results[1].str();
@@ -505,9 +509,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..7cf584d 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -85,64 +85,67 @@
/*
* Input device classes.
+ *
+ * These classes are duplicated in rust side here: /frameworks/native/libs/input/rust/input.rs.
+ * If any new classes are added, we need to add them in rust input side too.
*/
enum class InputDeviceClass : uint32_t {
/* The input device is a keyboard or has buttons. */
- KEYBOARD = 0x00000001,
+ KEYBOARD = android::os::IInputConstants::DEVICE_CLASS_KEYBOARD,
/* The input device is an alpha-numeric keyboard (not just a dial pad). */
- ALPHAKEY = 0x00000002,
+ ALPHAKEY = android::os::IInputConstants::DEVICE_CLASS_ALPHAKEY,
/* The input device is a touchscreen or a touchpad (either single-touch or multi-touch). */
- TOUCH = 0x00000004,
+ TOUCH = android::os::IInputConstants::DEVICE_CLASS_TOUCH,
/* The input device is a cursor device such as a trackball or mouse. */
- CURSOR = 0x00000008,
+ CURSOR = android::os::IInputConstants::DEVICE_CLASS_CURSOR,
/* The input device is a multi-touch touchscreen or touchpad. */
- TOUCH_MT = 0x00000010,
+ TOUCH_MT = android::os::IInputConstants::DEVICE_CLASS_TOUCH_MT,
/* The input device is a directional pad (implies keyboard, has DPAD keys). */
- DPAD = 0x00000020,
+ DPAD = android::os::IInputConstants::DEVICE_CLASS_DPAD,
/* The input device is a gamepad (implies keyboard, has BUTTON keys). */
- GAMEPAD = 0x00000040,
+ GAMEPAD = android::os::IInputConstants::DEVICE_CLASS_GAMEPAD,
/* The input device has switches. */
- SWITCH = 0x00000080,
+ SWITCH = android::os::IInputConstants::DEVICE_CLASS_SWITCH,
/* The input device is a joystick (implies gamepad, has joystick absolute axes). */
- JOYSTICK = 0x00000100,
+ JOYSTICK = android::os::IInputConstants::DEVICE_CLASS_JOYSTICK,
/* The input device has a vibrator (supports FF_RUMBLE). */
- VIBRATOR = 0x00000200,
+ VIBRATOR = android::os::IInputConstants::DEVICE_CLASS_VIBRATOR,
/* The input device has a microphone. */
- MIC = 0x00000400,
+ MIC = android::os::IInputConstants::DEVICE_CLASS_MIC,
/* The input device is an external stylus (has data we want to fuse with touch data). */
- EXTERNAL_STYLUS = 0x00000800,
+ EXTERNAL_STYLUS = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL_STYLUS,
/* The input device has a rotary encoder */
- ROTARY_ENCODER = 0x00001000,
+ ROTARY_ENCODER = android::os::IInputConstants::DEVICE_CLASS_ROTARY_ENCODER,
/* The input device has a sensor like accelerometer, gyro, etc */
- SENSOR = 0x00002000,
+ SENSOR = android::os::IInputConstants::DEVICE_CLASS_SENSOR,
/* The input device has a battery */
- BATTERY = 0x00004000,
+ BATTERY = android::os::IInputConstants::DEVICE_CLASS_BATTERY,
/* The input device has sysfs controllable lights */
- LIGHT = 0x00008000,
+ LIGHT = android::os::IInputConstants::DEVICE_CLASS_LIGHT,
/* The input device is a touchpad, requiring an on-screen cursor. */
- TOUCHPAD = 0x00010000,
+ TOUCHPAD = android::os::IInputConstants::DEVICE_CLASS_TOUCHPAD,
/* The input device is virtual (not a real device, not part of UI configuration). */
- VIRTUAL = 0x40000000,
+ VIRTUAL = android::os::IInputConstants::DEVICE_CLASS_VIRTUAL,
/* The input device is external (not built-in). */
- EXTERNAL = 0x80000000,
+ EXTERNAL = android::os::IInputConstants::DEVICE_CLASS_EXTERNAL,
};
enum class SysfsClass : uint32_t {
@@ -177,6 +180,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..4374ff5 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -43,7 +43,7 @@
public:
InputDevice(InputReaderContext* context, int32_t id, int32_t generation,
const InputDeviceIdentifier& identifier);
- ~InputDevice();
+ virtual ~InputDevice();
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() const { return mId; }
@@ -56,15 +56,18 @@
}
inline const std::string getLocation() const { return mIdentifier.location; }
inline ftl::Flags<InputDeviceClass> getClasses() const { return mClasses; }
- inline uint32_t getSources() const { return mSources; }
+ inline virtual uint32_t getSources() const { return mSources; }
inline bool hasEventHubDevices() const { return !mDevices.empty(); }
inline bool isExternal() { return mIsExternal; }
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;
@@ -76,6 +79,8 @@
inline bool isIgnored() { return !getMapperCount() && !mController; }
+ inline KeyboardType getKeyboardType() const { return mKeyboardType; }
+
bool isEnabled();
void dump(std::string& dump, const std::string& eventHubDevStr);
@@ -121,14 +126,16 @@
void addKeyRemapping(int32_t fromKeyCode, int32_t toKeyCode);
+ void setKeyboardType(KeyboardType keyboardType);
+
void bumpGeneration();
[[nodiscard]] NotifyDeviceResetArgs notifyReset(nsecs_t when);
- inline const PropertyMap& getConfiguration() { return mConfiguration; }
+ inline virtual const PropertyMap& getConfiguration() const { return mConfiguration; }
inline EventHubInterface* getEventHub() { return mContext->getEventHub(); }
- std::optional<int32_t> getAssociatedDisplayId();
+ std::optional<ui::LogicalDisplayId> getAssociatedDisplayId();
void updateLedState(bool reset);
@@ -193,8 +200,10 @@
uint32_t mSources;
bool mIsWaking;
bool mIsExternal;
+ KeyboardType mKeyboardType = KeyboardType::NONE;
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;
@@ -290,6 +299,7 @@
inline ftl::Flags<InputDeviceClass> getDeviceClasses() const {
return mEventHub->getDeviceClasses(mId);
}
+ inline uint32_t getDeviceSources() const { return mDevice.getSources(); }
inline InputDeviceIdentifier getDeviceIdentifier() const {
return mEventHub->getDeviceIdentifier(mId);
}
@@ -449,8 +459,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();
@@ -463,6 +476,10 @@
}
inline void bumpGeneration() { mDevice.bumpGeneration(); }
inline const PropertyMap& getConfiguration() const { return mDevice.getConfiguration(); }
+ inline KeyboardType getKeyboardType() const { return mDevice.getKeyboardType(); }
+ inline void setKeyboardType(KeyboardType keyboardType) {
+ return mDevice.setKeyboardType(keyboardType);
+ }
private:
InputDevice& mDevice;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 4c78db3..6f8c289 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>
@@ -62,8 +61,6 @@
std::vector<InputDeviceInfo> getInputDevices() const override;
- bool isInputDeviceEnabled(int32_t deviceId) override;
-
int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode) override;
int32_t getKeyCodeState(int32_t deviceId, uint32_t sourceMask, int32_t keyCode) override;
int32_t getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) override;
@@ -87,7 +84,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,
@@ -119,6 +116,8 @@
void sysfsNodeChanged(const std::string& sysfsNodePath) override;
+ DeviceId getLastUsedInputDeviceId() override;
+
protected:
// These members are protected so they can be instrumented by test cases.
virtual std::shared_ptr<InputDevice> createDeviceLocked(nsecs_t when, int32_t deviceId,
@@ -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)
@@ -161,6 +157,7 @@
void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock)
REQUIRES(mLock) override;
nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override;
+ KeyboardClassifier& getKeyboardClassifier() override;
} mContext;
friend class ContextImpl;
@@ -180,6 +177,10 @@
// The next stage that should receive the events generated inside InputReader.
InputListenerInterface& mNextListener;
+
+ // Classifier for keyboard/keyboard-like devices
+ std::unique_ptr<KeyboardClassifier> mKeyboardClassifier;
+
// As various events are generated inside InputReader, they are stored inside this list. The
// list can only be accessed with the lock, so the events inside it are well-ordered.
// Once the reader is done working, these events will be swapped into a temporary storage and
@@ -204,6 +205,9 @@
// records timestamp of the last key press on the physical keyboard
nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0};
+ // The input device that produced a new gesture most recently.
+ DeviceId mLastUsedDeviceId GUARDED_BY(mLock){ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID};
+
// low-level input event decoding and device management
[[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count)
REQUIRES(mLock);
@@ -230,13 +234,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..e0e0ac2 100644
--- a/services/inputflinger/reader/include/InputReaderContext.h
+++ b/services/inputflinger/reader/include/InputReaderContext.h
@@ -17,6 +17,7 @@
#pragma once
#include <input/InputDevice.h>
+#include <input/KeyboardClassifier.h>
#include "NotifyArgs.h"
#include <vector>
@@ -28,7 +29,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 +45,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;
@@ -68,6 +65,8 @@
virtual void setLastKeyDownTimestamp(nsecs_t when) = 0;
virtual nsecs_t getLastKeyDownTimestamp() = 0;
+
+ virtual KeyboardClassifier& getKeyboardClassifier() = 0;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp b/services/inputflinger/reader/mapper/CapturedTouchpadEventConverter.cpp
index 061c6a3..90685de 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>
@@ -156,8 +155,8 @@
mMotionAccumulator.finishSync();
}
- mCursorButtonAccumulator.process(&rawEvent);
- mMotionAccumulator.process(&rawEvent);
+ mCursorButtonAccumulator.process(rawEvent);
+ mMotionAccumulator.process(rawEvent);
return out;
}
@@ -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::LogicalDisplayId::INVALID, /*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 06f10e5..20cdb59 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() {
@@ -57,14 +55,14 @@
mRelY = 0;
}
-void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
+void CursorMotionAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_REL) {
+ switch (rawEvent.code) {
case REL_X:
- mRelX = rawEvent->value;
+ mRelX = rawEvent.value;
break;
case REL_Y:
- mRelY = rawEvent->value;
+ mRelY = rawEvent.value;
break;
}
}
@@ -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)));
@@ -228,16 +215,16 @@
return InputMapper::reset(when);
}
-std::list<NotifyArgs> CursorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> CursorInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
mCursorButtonAccumulator.process(rawEvent);
mCursorMotionAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
const auto [eventTime, readTime] =
applyBluetoothTimestampSmoothening(getDeviceContext().getDeviceIdentifier(),
- rawEvent->when, rawEvent->readTime,
+ rawEvent.when, rawEvent.readTime,
mLastEventTime);
out += sync(eventTime, readTime);
mLastEventTime = eventTime;
@@ -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;
@@ -486,12 +456,10 @@
}
void CursorInputMapper::configureOnPointerCapture(const InputReaderConfiguration& config) {
- if (config.pointerCaptureRequest.enable) {
+ if (config.pointerCaptureRequest.isEnable()) {
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::LogicalDisplayId::INVALID)) == 0);
mNewPointerVelocityControl.setCurve(
createAccelerationCurveForPointerSensitivity(config.mousePointerSpeed));
} else {
mOldPointerVelocityControl.setParameters(
(config.displaysWithMousePointerAccelerationDisabled.count(
- mDisplayId.value_or(ADISPLAY_ID_NONE)) == 0)
+ mDisplayId.value_or(ui::LogicalDisplayId::INVALID)) == 0)
? config.pointerVelocityControlParameters
: FLAT_VELOCITY_CONTROL_PARAMS);
}
@@ -538,40 +506,20 @@
void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfiguration& config) {
const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
- mDisplayId = ADISPLAY_ID_NONE;
+ mDisplayId = ui::LogicalDisplayId::INVALID;
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::LogicalDisplayId::INVALID) : std::nullopt;
}
mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) ||
@@ -579,14 +527,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..2108488 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;
@@ -37,7 +34,7 @@
CursorMotionAccumulator();
void reset(InputDeviceContext& deviceContext);
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
void finishSync();
inline int32_t getRelativeX() const { return mRelX; }
@@ -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;
@@ -65,11 +62,11 @@
const InputReaderConfiguration& readerConfig,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
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.
@@ -116,27 +113,20 @@
SimpleVelocityControl mWheelYVelocityControl;
// 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.
+ // LogicalDisplayId::INVALID 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/ExternalStylusInputMapper.cpp b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
index 987d2d0..3af1d04 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.cpp
@@ -61,13 +61,13 @@
return InputMapper::reset(when);
}
-std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> ExternalStylusInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
mSingleTouchMotionAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- out += sync(rawEvent->when);
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out += sync(rawEvent.when);
}
return out;
}
diff --git a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
index 97df02b..c040a7b 100644
--- a/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
+++ b/services/inputflinger/reader/mapper/ExternalStylusInputMapper.h
@@ -39,7 +39,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
private:
SingleTouchMotionAccumulator mSingleTouchMotionAccumulator;
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..2c51448 100644
--- a/services/inputflinger/reader/mapper/InputMapper.h
+++ b/services/inputflinger/reader/mapper/InputMapper.h
@@ -78,7 +78,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes);
[[nodiscard]] virtual std::list<NotifyArgs> reset(nsecs_t when);
- [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent* rawEvent) = 0;
+ [[nodiscard]] virtual std::list<NotifyArgs> process(const RawEvent& rawEvent) = 0;
[[nodiscard]] virtual std::list<NotifyArgs> timeoutExpired(nsecs_t when);
virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode);
@@ -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..41e018d 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.cpp
@@ -259,29 +259,29 @@
return InputMapper::reset(when);
}
-std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> JoystickInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
- switch (rawEvent->type) {
+ switch (rawEvent.type) {
case EV_ABS: {
- auto it = mAxes.find(rawEvent->code);
+ auto it = mAxes.find(rawEvent.code);
if (it != mAxes.end()) {
Axis& axis = it->second;
float newValue, highNewValue;
switch (axis.axisInfo.mode) {
case AxisInfo::MODE_INVERT:
- newValue = (axis.rawAxisInfo.maxValue - rawEvent->value) * axis.scale +
+ newValue = (axis.rawAxisInfo.maxValue - rawEvent.value) * axis.scale +
axis.offset;
highNewValue = 0.0f;
break;
case AxisInfo::MODE_SPLIT:
- if (rawEvent->value < axis.axisInfo.splitValue) {
- newValue = (axis.axisInfo.splitValue - rawEvent->value) * axis.scale +
+ if (rawEvent.value < axis.axisInfo.splitValue) {
+ newValue = (axis.axisInfo.splitValue - rawEvent.value) * axis.scale +
axis.offset;
highNewValue = 0.0f;
- } else if (rawEvent->value > axis.axisInfo.splitValue) {
+ } else if (rawEvent.value > axis.axisInfo.splitValue) {
newValue = 0.0f;
highNewValue =
- (rawEvent->value - axis.axisInfo.splitValue) * axis.highScale +
+ (rawEvent.value - axis.axisInfo.splitValue) * axis.highScale +
axis.highOffset;
} else {
newValue = 0.0f;
@@ -289,7 +289,7 @@
}
break;
default:
- newValue = rawEvent->value * axis.scale + axis.offset;
+ newValue = rawEvent.value * axis.scale + axis.offset;
highNewValue = 0.0f;
break;
}
@@ -300,9 +300,9 @@
}
case EV_SYN:
- switch (rawEvent->code) {
+ switch (rawEvent.code) {
case SYN_REPORT:
- out += sync(rawEvent->when, rawEvent->readTime, /*force=*/false);
+ out += sync(rawEvent.when, rawEvent.readTime, /*force=*/false);
break;
}
break;
@@ -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::LogicalDisplayId::INVALID;
if (getDeviceContext().getAssociatedViewport()) {
displayId = getDeviceContext().getAssociatedViewport()->displayId;
}
diff --git a/services/inputflinger/reader/mapper/JoystickInputMapper.h b/services/inputflinger/reader/mapper/JoystickInputMapper.h
index 313f092..621d38b 100644
--- a/services/inputflinger/reader/mapper/JoystickInputMapper.h
+++ b/services/inputflinger/reader/mapper/JoystickInputMapper.h
@@ -35,7 +35,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
private:
struct Axis {
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index 738517b..8eb6730 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -21,6 +21,7 @@
#include "KeyboardInputMapper.h"
#include <ftl/enum.h>
+#include <input/KeyboardClassifier.h>
#include <ui/Rotation.h>
namespace android {
@@ -96,11 +97,11 @@
KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
const InputReaderConfiguration& readerConfig,
- uint32_t source, int32_t keyboardType)
- : InputMapper(deviceContext, readerConfig), mSource(source), mKeyboardType(keyboardType) {}
+ uint32_t source)
+ : InputMapper(deviceContext, readerConfig), mMapperSource(source) {}
uint32_t KeyboardInputMapper::getSources() const {
- return mSource;
+ return mMapperSource;
}
ui::Rotation KeyboardInputMapper::getOrientation() {
@@ -110,11 +111,11 @@
return ui::ROTATION_0;
}
-int32_t KeyboardInputMapper::getDisplayId() {
+ui::LogicalDisplayId KeyboardInputMapper::getDisplayId() {
if (mViewport) {
return mViewport->displayId;
}
- return ADISPLAY_ID_NONE;
+ return ui::LogicalDisplayId::INVALID;
}
std::optional<KeyboardLayoutInfo> KeyboardInputMapper::getKeyboardLayoutInfo() const {
@@ -131,7 +132,6 @@
void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo& info) {
InputMapper::populateDeviceInfo(info);
- info.setKeyboardType(mKeyboardType);
info.setKeyCharacterMap(getDeviceContext().getKeyCharacterMap());
std::optional keyboardLayoutInfo = getKeyboardLayoutInfo();
@@ -143,7 +143,6 @@
void KeyboardInputMapper::dump(std::string& dump) {
dump += INDENT2 "Keyboard Input Mapper:\n";
dumpParameters(dump);
- dump += StringPrintf(INDENT3 "KeyboardType: %d\n", mKeyboardType);
dump += StringPrintf(INDENT3 "Orientation: %s\n", ftl::enum_string(getOrientation()).c_str());
dump += StringPrintf(INDENT3 "KeyDowns: %zu keys currently down\n", mKeyDowns.size());
dump += StringPrintf(INDENT3 "MetaState: 0x%0x\n", mMetaState);
@@ -237,15 +236,15 @@
return out;
}
-std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> KeyboardInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
- mHidUsageAccumulator.process(*rawEvent);
- switch (rawEvent->type) {
+ mHidUsageAccumulator.process(rawEvent);
+ switch (rawEvent.type) {
case EV_KEY: {
- int32_t scanCode = rawEvent->code;
+ int32_t scanCode = rawEvent.code;
if (isSupportedScanCode(scanCode)) {
- out += processKey(rawEvent->when, rawEvent->readTime, rawEvent->value != 0,
+ out += processKey(rawEvent.when, rawEvent.readTime, rawEvent.value != 0,
scanCode, mHidUsageAccumulator.consumeCurrentHidUsage());
}
break;
@@ -327,13 +326,24 @@
keyMetaState = mMetaState;
}
+ DeviceId deviceId = getDeviceId();
+
+ // On first down: Process key for keyboard classification (will send reconfiguration if the
+ // keyboard type change)
+ if (down && !keyDownIndex) {
+ KeyboardClassifier& classifier = getDeviceContext().getContext()->getKeyboardClassifier();
+ classifier.processKey(deviceId, scanCode, keyMetaState);
+ getDeviceContext().setKeyboardType(classifier.getKeyboardType(deviceId));
+ }
+
+ KeyboardType keyboardType = getDeviceContext().getKeyboardType();
// Any key down on an external keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
// For internal keyboards and devices for which the default wake behavior is explicitly
// prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
// wake key individually.
if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
- !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) {
+ !(keyboardType != KeyboardType::ALPHABETIC && isMediaKey(keyCode))) {
policyFlags |= POLICY_FLAG_WAKE;
}
@@ -341,8 +351,8 @@
policyFlags |= POLICY_FLAG_DISABLE_KEY_REPEAT;
}
- out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
- mSource, getDisplayId(), policyFlags,
+ out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when, readTime, deviceId,
+ getEventSource(), getDisplayId(), policyFlags,
down ? AKEY_EVENT_ACTION_DOWN : AKEY_EVENT_ACTION_UP, flags,
keyCode, scanCode, keyMetaState, downTime));
return out;
@@ -457,7 +467,7 @@
}
}
-std::optional<int32_t> KeyboardInputMapper::getAssociatedDisplayId() {
+std::optional<ui::LogicalDisplayId> KeyboardInputMapper::getAssociatedDisplayId() {
if (mViewport) {
return std::make_optional(mViewport->displayId);
}
@@ -468,12 +478,12 @@
std::list<NotifyArgs> out;
size_t n = mKeyDowns.size();
for (size_t i = 0; i < n; i++) {
- out.emplace_back(NotifyKeyArgs(getContext()->getNextId(), when,
- systemTime(SYSTEM_TIME_MONOTONIC), getDeviceId(), mSource,
- getDisplayId(), /*policyFlags=*/0, AKEY_EVENT_ACTION_UP,
- mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
- mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
- mKeyDowns[i].downTime));
+ out.emplace_back(
+ NotifyKeyArgs(getContext()->getNextId(), when, systemTime(SYSTEM_TIME_MONOTONIC),
+ getDeviceId(), getEventSource(), getDisplayId(), /*policyFlags=*/0,
+ AKEY_EVENT_ACTION_UP, mKeyDowns[i].flags | AKEY_EVENT_FLAG_CANCELED,
+ mKeyDowns[i].keyCode, mKeyDowns[i].scanCode, AMETA_NONE,
+ mKeyDowns[i].downTime));
}
mKeyDowns.clear();
mMetaState = AMETA_NONE;
@@ -483,18 +493,22 @@
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;
- }
// 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);
}
}
+uint32_t KeyboardInputMapper::getEventSource() const {
+ // For all input events generated by this mapper, use the source that's shared across all
+ // KeyboardInputMappers for this device in case there are more than one.
+ static constexpr auto ALL_KEYBOARD_SOURCES =
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+ const auto deviceSources = getDeviceContext().getDeviceSources();
+ LOG_ALWAYS_FATAL_IF((deviceSources & mMapperSource) != mMapperSource);
+ return deviceSources & ALL_KEYBOARD_SOURCES;
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index 500256b..2df0b85 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -36,7 +36,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
@@ -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:
@@ -60,8 +60,10 @@
int32_t flags{};
};
- uint32_t mSource{};
- int32_t mKeyboardType{};
+ // The keyboard source for this mapper. Events generated should use the source shared
+ // by all KeyboardInputMappers for this input device.
+ uint32_t mMapperSource{};
+
std::optional<KeyboardLayoutInfo> mKeyboardLayoutInfo;
std::vector<KeyDown> mKeyDowns{}; // keys that are down
@@ -85,13 +87,12 @@
} mParameters{};
KeyboardInputMapper(InputDeviceContext& deviceContext,
- const InputReaderConfiguration& readerConfig, uint32_t source,
- int32_t keyboardType);
+ const InputReaderConfiguration& readerConfig, uint32_t source);
void configureParameters();
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);
@@ -108,6 +109,7 @@
std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig);
[[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when);
void onKeyDownProcessed(nsecs_t downTime);
+ uint32_t getEventSource() const;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index bca9d91..1986fe2 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -40,7 +40,7 @@
return TouchInputMapper::reset(when);
}
-std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
mMultiTouchMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
index 5c173f3..cca2327 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.h
@@ -31,7 +31,7 @@
~MultiTouchInputMapper() override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
[[nodiscard]] std::list<NotifyArgs> reconfigure(nsecs_t when,
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
index 07ae5b1..27ff52f 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.cpp
@@ -101,12 +101,12 @@
return InputMapper::reset(when);
}
-std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> RotaryEncoderInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
mRotaryEncoderScrollAccumulator.process(rawEvent);
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- out += sync(rawEvent->when, rawEvent->readTime);
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out += sync(rawEvent.when, rawEvent.readTime);
}
return out;
}
@@ -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::LogicalDisplayId::INVALID;
if (mOrientation == ui::ROTATION_180) {
scroll = -scroll;
diff --git a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
index fe5d152..14c540b 100644
--- a/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
+++ b/services/inputflinger/reader/mapper/RotaryEncoderInputMapper.h
@@ -39,7 +39,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
private:
CursorScrollAccumulator mRotaryEncoderScrollAccumulator;
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
index a131e35..d7f2993 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp
@@ -251,35 +251,35 @@
mPrevMscTime = static_cast<uint32_t>(mscTime);
}
-std::list<NotifyArgs> SensorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SensorInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
- switch (rawEvent->type) {
+ switch (rawEvent.type) {
case EV_ABS: {
- auto it = mAxes.find(rawEvent->code);
+ auto it = mAxes.find(rawEvent.code);
if (it != mAxes.end()) {
Axis& axis = it->second;
- axis.newValue = rawEvent->value * axis.scale + axis.offset;
+ axis.newValue = rawEvent.value * axis.scale + axis.offset;
}
break;
}
case EV_SYN:
- switch (rawEvent->code) {
+ switch (rawEvent.code) {
case SYN_REPORT:
for (std::pair<const int32_t, Axis>& pair : mAxes) {
Axis& axis = pair.second;
axis.currentValue = axis.newValue;
}
- out += sync(rawEvent->when, /*force=*/false);
+ out += sync(rawEvent.when, /*force=*/false);
break;
}
break;
case EV_MSC:
- switch (rawEvent->code) {
+ switch (rawEvent.code) {
case MSC_TIMESTAMP:
// hardware timestamp is nano seconds
- processHardWareTimestamp(rawEvent->when, rawEvent->value);
+ processHardWareTimestamp(rawEvent.when, rawEvent.value);
break;
}
}
diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.h b/services/inputflinger/reader/mapper/SensorInputMapper.h
index a55dcd1..63bc151 100644
--- a/services/inputflinger/reader/mapper/SensorInputMapper.h
+++ b/services/inputflinger/reader/mapper/SensorInputMapper.h
@@ -40,7 +40,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
bool enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
std::chrono::microseconds maxBatchReportLatency) override;
void disableSensor(InputDeviceSensorType sensorType) override;
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
index ed0e270..140bb0c 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.cpp
@@ -30,7 +30,7 @@
return TouchInputMapper::reset(when);
}
-std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SingleTouchInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
mSingleTouchMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
index 7726bfb..bc38711 100644
--- a/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SingleTouchInputMapper.h
@@ -31,7 +31,7 @@
~SingleTouchInputMapper() override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
protected:
void syncTouch(nsecs_t when, RawState* outState) override;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 05338da..f131fb7 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -30,16 +30,16 @@
return AINPUT_SOURCE_SWITCH;
}
-std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> SwitchInputMapper::process(const RawEvent& rawEvent) {
std::list<NotifyArgs> out;
- switch (rawEvent->type) {
+ switch (rawEvent.type) {
case EV_SW:
- processSwitch(rawEvent->code, rawEvent->value);
+ processSwitch(rawEvent.code, rawEvent.value);
break;
case EV_SYN:
- if (rawEvent->code == SYN_REPORT) {
- out += sync(rawEvent->when);
+ if (rawEvent.code == SYN_REPORT) {
+ out += sync(rawEvent.when);
}
}
return out;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.h b/services/inputflinger/reader/mapper/SwitchInputMapper.h
index 2fb48bb..5d8aa2c 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.h
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.h
@@ -29,7 +29,7 @@
virtual ~SwitchInputMapper();
virtual uint32_t getSources() const override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode) override;
virtual void dump(std::string& dump) override;
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 3c26d1d..a383490 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,25 +532,19 @@
* 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) {
- return getDeviceContext().getAssociatedViewport();
- }
-
if (mDeviceMode == DeviceMode::POINTER) {
std::optional<DisplayViewport> viewport =
mConfig.getDisplayViewportById(mConfig.defaultPointerDisplayId);
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());
}
}
@@ -923,7 +918,7 @@
// Determine device mode.
if (mParameters.deviceType == Parameters::DeviceType::POINTER &&
- mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.enable) {
+ mConfig.pointerGesturesEnabled && !mConfig.pointerCaptureRequest.isEnable()) {
mSource = AINPUT_SOURCE_MOUSE;
mDeviceMode = DeviceMode::POINTER;
if (hasStylus()) {
@@ -939,8 +934,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 +1029,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.enable) ||
- // - 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.enable) {
- 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 +1085,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 +1361,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 +1387,6 @@
mPointerSimple.reset();
resetExternalStylus();
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- mPointerController->clearSpots();
- }
-
return out += InputMapper::reset(when);
}
@@ -1436,14 +1403,14 @@
mExternalStylusFusionTimeout = LLONG_MAX;
}
-std::list<NotifyArgs> TouchInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchInputMapper::process(const RawEvent& rawEvent) {
mCursorButtonAccumulator.process(rawEvent);
mCursorScrollAccumulator.process(rawEvent);
mTouchButtonAccumulator.process(rawEvent);
std::list<NotifyArgs> out;
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- out += sync(rawEvent->when, rawEvent->readTime);
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out += sync(rawEvent.when, rawEvent.readTime);
}
return out;
}
@@ -1574,11 +1541,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 +1608,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 +1639,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 +1823,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 +1850,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) {
@@ -2405,20 +2343,23 @@
if (mHaveTilt) {
float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale;
float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale;
- orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)));
+ orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)),
+ /*isDirectional=*/true);
tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle));
} else {
tilt = 0;
switch (mCalibration.orientationCalibration) {
case Calibration::OrientationCalibration::INTERPOLATED:
- orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale);
+ orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale,
+ /*isDirectional=*/true);
break;
case Calibration::OrientationCalibration::VECTOR: {
int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4);
int32_t c2 = signExtendNybble(in.orientation & 0x0f);
if (c1 != 0 || c2 != 0) {
- orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f);
+ orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f,
+ /*isDirectional=*/true);
float confidence = hypotf(c1, c2);
float scale = 1.0f + confidence / 16.0f;
touchMajor *= scale;
@@ -2549,54 +2490,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 +2628,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 +2636,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::LogicalDisplayId::INVALID, 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 +2687,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 +2822,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 +2830,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 +2844,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 +2872,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 +2904,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 +2939,6 @@
down = false;
}
- const auto [x, y] = mPointerController->getPosition();
-
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
mPointerGesture.currentGestureIdToIndex[mPointerGesture.activeGestureId] = 0;
@@ -3072,16 +2946,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 +2962,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 +3104,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 +3400,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 +3416,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 +3453,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 +3468,8 @@
hovering = false;
}
- const int32_t displayId = mPointerController->getDisplayId();
- return dispatchPointerSimple(when, readTime, policyFlags, down, hovering, displayId);
+ return dispatchPointerSimple(when, readTime, policyFlags, down, hovering,
+ ui::LogicalDisplayId::INVALID);
}
std::list<NotifyArgs> TouchInputMapper::abortPointerMouse(nsecs_t when, nsecs_t readTime,
@@ -3621,24 +3483,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 +3626,6 @@
mPointerSimple.lastCursorX, mPointerSimple.lastCursorY,
mPointerSimple.downTime,
/*videoFrames=*/{}));
- if (mPointerController != nullptr) {
- mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
- }
}
mPointerSimple.reset();
return out;
@@ -3826,36 +3675,24 @@
if (mCurrentStreamModifiedByExternalStylus) {
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;
+ if (mOrientedRanges.orientation.has_value()) {
+ flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION;
+ if (mOrientedRanges.tilt.has_value()) {
+ // In the current implementation, only devices that report a value for tilt supports
+ // directional orientation.
+ flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION;
}
}
+ const ui::LogicalDisplayId displayId =
+ getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID);
+
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 +3959,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::LogicalDisplayId::INVALID;
} 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..30c58a5 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 {
@@ -155,7 +174,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode) override;
int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode) override;
@@ -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::LogicalDisplayId::INVALID};
float lastCursorX;
float lastCursorY;
@@ -701,7 +718,7 @@
hovering = false;
downTime = 0;
source = 0;
- displayId = ADISPLAY_ID_NONE;
+ displayId = ui::LogicalDisplayId::INVALID;
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 eacc66e..24efae8 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -24,6 +24,7 @@
#include <mutex>
#include <optional>
+#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
#include <android/input.h>
@@ -47,8 +48,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
@@ -234,26 +233,19 @@
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) {
- ALOGW("Touchpad \"%s\" doesn't have a valid ABS_MT_SLOT axis, and probably won't work "
- "properly.",
- deviceContext.getName().c_str());
+ if (!slotAxisInfo.valid || slotAxisInfo.maxValue < 0) {
+ LOG(WARNING) << "Touchpad " << deviceContext.getName()
+ << " doesn't have a valid ABS_MT_SLOT axis, and probably won't work properly.";
+ slotAxisInfo.maxValue = 0;
}
mMotionAccumulator.configure(deviceContext, slotAxisInfo.maxValue + 1, true);
@@ -274,10 +266,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 +307,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 +320,7 @@
}
if (!changes.any() || changes.test(InputReaderConfiguration::Change::DISPLAY_INFO)) {
- mDisplayId = ADISPLAY_ID_NONE;
+ mDisplayId = ui::LogicalDisplayId::INVALID;
std::optional<DisplayViewport> resolvedViewport;
std::optional<FloatRect> boundsInLogicalDisplay;
if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) {
@@ -339,33 +328,13 @@
// 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::LogicalDisplayId::INVALID)
+ : std::nullopt;
}
mGestureConverter.setDisplayId(mDisplayId);
@@ -410,16 +379,15 @@
.setBoolValues({config.touchpadRightClickZoneEnabled});
}
std::list<NotifyArgs> out;
- if ((!changes.any() && config.pointerCaptureRequest.enable) ||
+ if ((!changes.any() && config.pointerCaptureRequest.isEnable()) ||
changes.test(InputReaderConfiguration::Change::POINTER_CAPTURE)) {
- mPointerCaptured = config.pointerCaptureRequest.enable;
+ mPointerCaptured = config.pointerCaptureRequest.isEnable();
// The motion ranges are going to change, so bump the generation to clear the cached ones.
bumpGeneration();
if (mPointerCaptured) {
// 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();
@@ -449,17 +417,17 @@
mResettingInterpreter = false;
}
-std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent& rawEvent) {
if (mPointerCaptured) {
- return mCapturedEventConverter.process(*rawEvent);
+ return mCapturedEventConverter.process(rawEvent);
}
if (mMotionAccumulator.getActiveSlotsCount() == 0) {
- mGestureStartTime = rawEvent->when;
+ mGestureStartTime = rawEvent.when;
}
std::optional<SelfContainedHardwareState> state = mStateConverter.processRawEvent(rawEvent);
if (state) {
updatePalmDetectionMetrics();
- return sendHardwareState(rawEvent->when, rawEvent->readTime, *state);
+ return sendHardwareState(rawEvent.when, rawEvent.readTime, *state);
} else {
return {};
}
@@ -529,7 +497,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..8baa63e 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.h
@@ -56,7 +56,7 @@
const InputReaderConfiguration& config,
ConfigurationChanges changes) override;
[[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
[[nodiscard]] std::list<NotifyArgs> timeoutExpired(nsecs_t when) override;
void consumeGesture(const Gesture* gesture);
@@ -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.
+ // LogicalDisplayId::INVALID 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/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 8d78d0f..a3a48ef 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -36,7 +36,7 @@
info.setVibrator(true);
}
-std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent* rawEvent) {
+std::list<NotifyArgs> VibratorInputMapper::process(const RawEvent& rawEvent) {
// TODO: Handle FF_STATUS, although it does not seem to be widely supported.
return {};
}
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.h b/services/inputflinger/reader/mapper/VibratorInputMapper.h
index 9079c73..7519682 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.h
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.h
@@ -30,7 +30,7 @@
virtual uint32_t getSources() const override;
virtual void populateDeviceInfo(InputDeviceInfo& deviceInfo) override;
- [[nodiscard]] std::list<NotifyArgs> process(const RawEvent* rawEvent) override;
+ [[nodiscard]] std::list<NotifyArgs> process(const RawEvent& rawEvent) override;
[[nodiscard]] std::list<NotifyArgs> vibrate(const VibrationSequence& sequence, ssize_t repeat,
int32_t token) override;
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
index 153236c..9e722d4 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.cpp
@@ -47,32 +47,32 @@
mBtnTask = 0;
}
-void CursorButtonAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
+void CursorButtonAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_KEY) {
+ switch (rawEvent.code) {
case BTN_LEFT:
- mBtnLeft = rawEvent->value;
+ mBtnLeft = rawEvent.value;
break;
case BTN_RIGHT:
- mBtnRight = rawEvent->value;
+ mBtnRight = rawEvent.value;
break;
case BTN_MIDDLE:
- mBtnMiddle = rawEvent->value;
+ mBtnMiddle = rawEvent.value;
break;
case BTN_BACK:
- mBtnBack = rawEvent->value;
+ mBtnBack = rawEvent.value;
break;
case BTN_SIDE:
- mBtnSide = rawEvent->value;
+ mBtnSide = rawEvent.value;
break;
case BTN_FORWARD:
- mBtnForward = rawEvent->value;
+ mBtnForward = rawEvent.value;
break;
case BTN_EXTRA:
- mBtnExtra = rawEvent->value;
+ mBtnExtra = rawEvent.value;
break;
case BTN_TASK:
- mBtnTask = rawEvent->value;
+ mBtnTask = rawEvent.value;
break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
index 6960644..256b2bb 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorButtonAccumulator.h
@@ -29,7 +29,7 @@
CursorButtonAccumulator();
void reset(const InputDeviceContext& deviceContext);
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
uint32_t getButtonState() const;
inline bool isLeftPressed() const { return mBtnLeft; }
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
index 0714694..f85cab2 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.cpp
@@ -39,14 +39,14 @@
mRelHWheel = 0;
}
-void CursorScrollAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_REL) {
- switch (rawEvent->code) {
+void CursorScrollAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_REL) {
+ switch (rawEvent.code) {
case REL_WHEEL:
- mRelWheel = rawEvent->value;
+ mRelWheel = rawEvent.value;
break;
case REL_HWHEEL:
- mRelHWheel = rawEvent->value;
+ mRelHWheel = rawEvent.value;
break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
index ae1b7a3..e563620 100644
--- a/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/CursorScrollAccumulator.h
@@ -31,7 +31,7 @@
void configure(InputDeviceContext& deviceContext);
void reset(InputDeviceContext& deviceContext);
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
void finishSync();
inline bool haveRelativeVWheel() const { return mHaveRelWheel; }
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b3f1700..4919068 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -45,12 +45,12 @@
mCurrentSlot = -1;
}
-void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
+void MultiTouchMotionAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_ABS) {
bool newSlot = false;
if (mUsingSlotsProtocol) {
- if (rawEvent->code == ABS_MT_SLOT) {
- mCurrentSlot = rawEvent->value;
+ if (rawEvent.code == ABS_MT_SLOT) {
+ mCurrentSlot = rawEvent.value;
newSlot = true;
}
} else if (mCurrentSlot < 0) {
@@ -72,12 +72,12 @@
if (!mUsingSlotsProtocol) {
slot.mInUse = true;
}
- if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) {
- warnIfNotInUse(*rawEvent, slot);
+ if (rawEvent.code == ABS_MT_POSITION_X || rawEvent.code == ABS_MT_POSITION_Y) {
+ warnIfNotInUse(rawEvent, slot);
}
- slot.populateAxisValue(rawEvent->code, rawEvent->value);
+ slot.populateAxisValue(rawEvent.code, rawEvent.value);
}
- } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
+ } else if (rawEvent.type == EV_SYN && rawEvent.code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
mCurrentSlot += 1;
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index a0f2147..388ed82 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -76,7 +76,7 @@
void configure(const InputDeviceContext& deviceContext, size_t slotCount,
bool usingSlotsProtocol);
void reset(const InputDeviceContext& deviceContext);
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
void finishSync();
size_t getActiveSlotsCount() const;
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
index 27b8e40..2b82ddf 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.cpp
@@ -45,29 +45,29 @@
mAbsTiltY = 0;
}
-void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) {
- if (rawEvent->type == EV_ABS) {
- switch (rawEvent->code) {
+void SingleTouchMotionAccumulator::process(const RawEvent& rawEvent) {
+ if (rawEvent.type == EV_ABS) {
+ switch (rawEvent.code) {
case ABS_X:
- mAbsX = rawEvent->value;
+ mAbsX = rawEvent.value;
break;
case ABS_Y:
- mAbsY = rawEvent->value;
+ mAbsY = rawEvent.value;
break;
case ABS_PRESSURE:
- mAbsPressure = rawEvent->value;
+ mAbsPressure = rawEvent.value;
break;
case ABS_TOOL_WIDTH:
- mAbsToolWidth = rawEvent->value;
+ mAbsToolWidth = rawEvent.value;
break;
case ABS_DISTANCE:
- mAbsDistance = rawEvent->value;
+ mAbsDistance = rawEvent.value;
break;
case ABS_TILT_X:
- mAbsTiltX = rawEvent->value;
+ mAbsTiltX = rawEvent.value;
break;
case ABS_TILT_Y:
- mAbsTiltY = rawEvent->value;
+ mAbsTiltY = rawEvent.value;
break;
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
index 93056f0..fb74bca 100644
--- a/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/SingleTouchMotionAccumulator.h
@@ -28,7 +28,7 @@
public:
SingleTouchMotionAccumulator();
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
void reset(InputDeviceContext& deviceContext);
inline int32_t getAbsoluteX() const { return mAbsX; }
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
index 8c4bed3..ba8577e 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.cpp
@@ -52,60 +52,60 @@
mHidUsageAccumulator.reset();
}
-void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
- mHidUsageAccumulator.process(*rawEvent);
+void TouchButtonAccumulator::process(const RawEvent& rawEvent) {
+ mHidUsageAccumulator.process(rawEvent);
- if (rawEvent->type == EV_KEY) {
- switch (rawEvent->code) {
+ if (rawEvent.type == EV_KEY) {
+ switch (rawEvent.code) {
case BTN_TOUCH:
- mBtnTouch = rawEvent->value;
+ mBtnTouch = rawEvent.value;
break;
case BTN_STYLUS:
- mBtnStylus = rawEvent->value;
+ mBtnStylus = rawEvent.value;
break;
case BTN_STYLUS2:
case BTN_0: // BTN_0 is what gets mapped for the HID usage
// Digitizers.SecondaryBarrelSwitch
- mBtnStylus2 = rawEvent->value;
+ mBtnStylus2 = rawEvent.value;
break;
case BTN_TOOL_FINGER:
- mBtnToolFinger = rawEvent->value;
+ mBtnToolFinger = rawEvent.value;
break;
case BTN_TOOL_PEN:
- mBtnToolPen = rawEvent->value;
+ mBtnToolPen = rawEvent.value;
break;
case BTN_TOOL_RUBBER:
- mBtnToolRubber = rawEvent->value;
+ mBtnToolRubber = rawEvent.value;
break;
case BTN_TOOL_BRUSH:
- mBtnToolBrush = rawEvent->value;
+ mBtnToolBrush = rawEvent.value;
break;
case BTN_TOOL_PENCIL:
- mBtnToolPencil = rawEvent->value;
+ mBtnToolPencil = rawEvent.value;
break;
case BTN_TOOL_AIRBRUSH:
- mBtnToolAirbrush = rawEvent->value;
+ mBtnToolAirbrush = rawEvent.value;
break;
case BTN_TOOL_MOUSE:
- mBtnToolMouse = rawEvent->value;
+ mBtnToolMouse = rawEvent.value;
break;
case BTN_TOOL_LENS:
- mBtnToolLens = rawEvent->value;
+ mBtnToolLens = rawEvent.value;
break;
case BTN_TOOL_DOUBLETAP:
- mBtnToolDoubleTap = rawEvent->value;
+ mBtnToolDoubleTap = rawEvent.value;
break;
case BTN_TOOL_TRIPLETAP:
- mBtnToolTripleTap = rawEvent->value;
+ mBtnToolTripleTap = rawEvent.value;
break;
case BTN_TOOL_QUADTAP:
- mBtnToolQuadTap = rawEvent->value;
+ mBtnToolQuadTap = rawEvent.value;
break;
case BTN_TOOL_QUINTTAP:
- mBtnToolQuintTap = rawEvent->value;
+ mBtnToolQuintTap = rawEvent.value;
break;
default:
- processMappedKey(rawEvent->code, rawEvent->value);
+ processMappedKey(rawEvent.code, rawEvent.value);
}
return;
}
diff --git a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
index e829692..c7adf84 100644
--- a/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/TouchButtonAccumulator.h
@@ -33,7 +33,7 @@
void configure();
void reset();
- void process(const RawEvent* rawEvent);
+ void process(const RawEvent& rawEvent);
uint32_t getButtonState() const;
ToolType getToolType() const;
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 764bb56..e8e7376 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,16 @@
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.clear();
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 +351,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 +381,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 +421,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 +439,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 +468,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 +492,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 +502,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 +510,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 +535,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 +564,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 +642,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/reader/mapper/gestures/GesturesLogging.cpp b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
index 81b4968..26028c5 100644
--- a/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GesturesLogging.cpp
@@ -26,15 +26,30 @@
extern "C" {
+namespace {
+
+/**
+ * Log details of each gesture output by the gestures library.
+ * Enable this via "adb shell setprop log.tag.TouchpadInputMapperGestures DEBUG" (requires
+ * restarting the shell)
+ */
+const bool DEBUG_TOUCHPAD_GESTURES =
+ __android_log_is_loggable(ANDROID_LOG_DEBUG, "TouchpadInputMapperGestures",
+ ANDROID_LOG_INFO);
+
+} // namespace
+
void gestures_log(int verb, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
if (verb == GESTURES_LOG_ERROR) {
LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, args);
- } else if (verb == GESTURES_LOG_INFO) {
- LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
- } else {
- LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+ } else if (DEBUG_TOUCHPAD_GESTURES) {
+ if (verb == GESTURES_LOG_INFO) {
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, args);
+ } else {
+ LOG_PRI_VA(ANDROID_LOG_DEBUG, LOG_TAG, fmt, args);
+ }
}
va_end(args);
}
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
index b89b7f3..6885adb 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.cpp
@@ -40,15 +40,15 @@
}
std::optional<SelfContainedHardwareState> HardwareStateConverter::processRawEvent(
- const RawEvent* rawEvent) {
+ const RawEvent& rawEvent) {
std::optional<SelfContainedHardwareState> out;
- if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
- out = produceHardwareState(rawEvent->when);
+ if (rawEvent.type == EV_SYN && rawEvent.code == SYN_REPORT) {
+ out = produceHardwareState(rawEvent.when);
mMotionAccumulator.finishSync();
mMscTimestamp = 0;
}
- if (rawEvent->type == EV_MSC && rawEvent->code == MSC_TIMESTAMP) {
- mMscTimestamp = rawEvent->value;
+ if (rawEvent.type == EV_MSC && rawEvent.code == MSC_TIMESTAMP) {
+ mMscTimestamp = rawEvent.value;
}
mCursorButtonAccumulator.process(rawEvent);
mMotionAccumulator.process(rawEvent);
diff --git a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
index 633448e..07e62c6 100644
--- a/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/HardwareStateConverter.h
@@ -44,7 +44,7 @@
HardwareStateConverter(const InputDeviceContext& deviceContext,
MultiTouchMotionAccumulator& motionAccumulator);
- std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent* event);
+ std::optional<SelfContainedHardwareState> processRawEvent(const RawEvent& event);
void reset();
private:
diff --git a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
index 69264f8..f4a5e0d 100644
--- a/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
+++ b/services/inputflinger/reader/mapper/gestures/PropertyProvider.cpp
@@ -90,7 +90,7 @@
// prefixed with "gestureProp." and have spaces replaced by underscores. So, for example, the
// configuration key "gestureProp.Palm_Width" refers to the "Palm Width" property.
const std::string gesturePropPrefix = "gestureProp.";
- for (const std::string key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
+ for (const std::string& key : idcProperties.getKeysWithPrefix(gesturePropPrefix)) {
std::string propertyName = key.substr(gesturePropPrefix.length());
for (size_t i = 0; i < propertyName.length(); i++) {
if (propertyName[i] == '_') {
diff --git a/services/inputflinger/rust/Android.bp b/services/inputflinger/rust/Android.bp
index 255c7eb..5b7cc2d 100644
--- a/services/inputflinger/rust/Android.bp
+++ b/services/inputflinger/rust/Android.bp
@@ -47,6 +47,7 @@
"liblog_rust",
"liblogger",
"libnix",
+ "libinput_rust",
],
host_supported: true,
}
diff --git a/services/inputflinger/rust/input_filter.rs b/services/inputflinger/rust/input_filter.rs
index a544fa3..8b44af3 100644
--- a/services/inputflinger/rust/input_filter.rs
+++ b/services/inputflinger/rust/input_filter.rs
@@ -31,6 +31,7 @@
use crate::input_filter_thread::InputFilterThread;
use crate::slow_keys_filter::SlowKeysFilter;
use crate::sticky_keys_filter::StickyKeysFilter;
+use input::ModifierState;
use log::{error, info};
use std::sync::{Arc, Mutex, RwLock};
@@ -169,12 +170,15 @@
Self(callbacks)
}
- pub fn modifier_state_changed(&self, modifier_state: u32, locked_modifier_state: u32) {
- let _ = self
- .0
- .read()
- .unwrap()
- .onModifierStateChanged(modifier_state as i32, locked_modifier_state as i32);
+ pub fn modifier_state_changed(
+ &self,
+ modifier_state: ModifierState,
+ locked_modifier_state: ModifierState,
+ ) {
+ let _ = self.0.read().unwrap().onModifierStateChanged(
+ modifier_state.bits() as i32,
+ locked_modifier_state.bits() as i32,
+ );
}
}
@@ -396,14 +400,17 @@
IInputThread::{BnInputThread, IInputThread, IInputThreadCallback::IInputThreadCallback},
KeyEvent::KeyEvent,
};
- use std::sync::{Arc, RwLock, RwLockWriteGuard};
+ use input::ModifierState;
+ 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_modifier_state: ModifierState,
+ last_locked_modifier_state: ModifierState,
last_event: Option<KeyEvent>,
- test_thread: Option<TestThread>,
+ test_thread: Option<FakeCppThread>,
}
#[derive(Default, Clone)]
@@ -426,25 +433,21 @@
pub fn clear(&mut self) {
self.inner().last_event = None;
- self.inner().last_modifier_state = 0;
- self.inner().last_locked_modifier_state = 0;
+ self.inner().last_modifier_state = ModifierState::None;
+ self.inner().last_locked_modifier_state = ModifierState::None;
}
- pub fn get_last_modifier_state(&self) -> u32 {
+ pub fn get_last_modifier_state(&self) -> ModifierState {
self.0.read().unwrap().last_modifier_state
}
- pub fn get_last_locked_modifier_state(&self) -> u32 {
+ pub fn get_last_locked_modifier_state(&self) -> ModifierState {
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
}
@@ -461,48 +464,110 @@
modifier_state: i32,
locked_modifier_state: i32,
) -> std::result::Result<(), binder::Status> {
- self.inner().last_modifier_state = modifier_state as u32;
- self.inner().last_locked_modifier_state = locked_modifier_state as u32;
+ self.inner().last_modifier_state =
+ ModifierState::from_bits(modifier_state as u32).unwrap();
+ self.inner().last_locked_modifier_state =
+ ModifierState::from_bits(locked_modifier_state as u32).unwrap();
Result::Ok(())
}
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/rust/sticky_keys_filter.rs b/services/inputflinger/rust/sticky_keys_filter.rs
index 6c2277c..6c7c7fb 100644
--- a/services/inputflinger/rust/sticky_keys_filter.rs
+++ b/services/inputflinger/rust/sticky_keys_filter.rs
@@ -23,6 +23,7 @@
use com_android_server_inputflinger::aidl::com::android::server::inputflinger::{
DeviceInfo::DeviceInfo, KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+use input::ModifierState;
use std::collections::HashSet;
// Modifier keycodes: values are from /frameworks/native/include/android/keycodes.h
@@ -40,20 +41,6 @@
const KEYCODE_FUNCTION: i32 = 119;
const KEYCODE_NUM_LOCK: i32 = 143;
-// Modifier states: values are from /frameworks/native/include/android/input.h
-const META_ALT_ON: u32 = 0x02;
-const META_ALT_LEFT_ON: u32 = 0x10;
-const META_ALT_RIGHT_ON: u32 = 0x20;
-const META_SHIFT_ON: u32 = 0x01;
-const META_SHIFT_LEFT_ON: u32 = 0x40;
-const META_SHIFT_RIGHT_ON: u32 = 0x80;
-const META_CTRL_ON: u32 = 0x1000;
-const META_CTRL_LEFT_ON: u32 = 0x2000;
-const META_CTRL_RIGHT_ON: u32 = 0x4000;
-const META_META_ON: u32 = 0x10000;
-const META_META_LEFT_ON: u32 = 0x20000;
-const META_META_RIGHT_ON: u32 = 0x40000;
-
pub struct StickyKeysFilter {
next: Box<dyn Filter + Send + Sync>,
listener: ModifierStateListener,
@@ -61,11 +48,11 @@
contributing_devices: HashSet<i32>,
/// State describing the current enabled modifiers. This contain both locked and non-locked
/// modifier state bits.
- modifier_state: u32,
+ modifier_state: ModifierState,
/// State describing the current locked modifiers. These modifiers will not be cleared on a
/// non-modifier key press. They will be cleared only if the locked modifier key is pressed
/// again.
- locked_modifier_state: u32,
+ locked_modifier_state: ModifierState,
}
impl StickyKeysFilter {
@@ -78,8 +65,8 @@
next,
listener,
contributing_devices: HashSet::new(),
- modifier_state: 0,
- locked_modifier_state: 0,
+ modifier_state: ModifierState::None,
+ locked_modifier_state: ModifierState::None,
}
}
}
@@ -93,12 +80,12 @@
// If non-ephemeral modifier key (i.e. non-modifier keys + toggle modifier keys like
// CAPS_LOCK, NUM_LOCK etc.), don't block key and pass in the sticky modifier state with
// the KeyEvent.
- let old_modifier_state = event.metaState as u32;
+ let old_modifier_state = ModifierState::from_bits(event.metaState as u32).unwrap();
let mut new_event = *event;
// Send the current modifier state with the key event before clearing non-locked
// modifier state
new_event.metaState =
- (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state) as i32;
+ (clear_ephemeral_modifier_state(old_modifier_state) | modifier_state).bits() as i32;
self.next.notify_key(&new_event);
if up && !is_modifier_key(event.keyCode) {
modifier_state =
@@ -110,10 +97,10 @@
// If ephemeral modifier key, capture the key and update the sticky modifier states
let modifier_key_mask = get_ephemeral_modifier_key_mask(event.keyCode);
let symmetrical_modifier_key_mask = get_symmetrical_modifier_key_mask(event.keyCode);
- if locked_modifier_state & modifier_key_mask != 0 {
+ if locked_modifier_state & modifier_key_mask != ModifierState::None {
locked_modifier_state &= !symmetrical_modifier_key_mask;
modifier_state &= !symmetrical_modifier_key_mask;
- } else if modifier_key_mask & modifier_state != 0 {
+ } else if modifier_key_mask & modifier_state != ModifierState::None {
locked_modifier_state |= modifier_key_mask;
modifier_state =
(modifier_state & !symmetrical_modifier_key_mask) | modifier_key_mask;
@@ -134,11 +121,12 @@
// Clear state if all contributing devices removed
self.contributing_devices.retain(|id| device_infos.iter().any(|x| *id == x.deviceId));
if self.contributing_devices.is_empty()
- && (self.modifier_state != 0 || self.locked_modifier_state != 0)
+ && (self.modifier_state != ModifierState::None
+ || self.locked_modifier_state != ModifierState::None)
{
- self.modifier_state = 0;
- self.locked_modifier_state = 0;
- self.listener.modifier_state_changed(0, 0);
+ self.modifier_state = ModifierState::None;
+ self.locked_modifier_state = ModifierState::None;
+ self.listener.modifier_state_changed(ModifierState::None, ModifierState::None);
}
self.next.notify_devices_changed(device_infos);
}
@@ -181,51 +169,53 @@
)
}
-fn get_ephemeral_modifier_key_mask(keycode: i32) -> u32 {
+fn get_ephemeral_modifier_key_mask(keycode: i32) -> ModifierState {
match keycode {
- KEYCODE_ALT_LEFT => META_ALT_LEFT_ON | META_ALT_ON,
- KEYCODE_ALT_RIGHT => META_ALT_RIGHT_ON | META_ALT_ON,
- KEYCODE_SHIFT_LEFT => META_SHIFT_LEFT_ON | META_SHIFT_ON,
- KEYCODE_SHIFT_RIGHT => META_SHIFT_RIGHT_ON | META_SHIFT_ON,
- KEYCODE_CTRL_LEFT => META_CTRL_LEFT_ON | META_CTRL_ON,
- KEYCODE_CTRL_RIGHT => META_CTRL_RIGHT_ON | META_CTRL_ON,
- KEYCODE_META_LEFT => META_META_LEFT_ON | META_META_ON,
- KEYCODE_META_RIGHT => META_META_RIGHT_ON | META_META_ON,
- _ => 0,
+ KEYCODE_ALT_LEFT => ModifierState::AltLeftOn | ModifierState::AltOn,
+ KEYCODE_ALT_RIGHT => ModifierState::AltRightOn | ModifierState::AltOn,
+ KEYCODE_SHIFT_LEFT => ModifierState::ShiftLeftOn | ModifierState::ShiftOn,
+ KEYCODE_SHIFT_RIGHT => ModifierState::ShiftRightOn | ModifierState::ShiftOn,
+ KEYCODE_CTRL_LEFT => ModifierState::CtrlLeftOn | ModifierState::CtrlOn,
+ KEYCODE_CTRL_RIGHT => ModifierState::CtrlRightOn | ModifierState::CtrlOn,
+ KEYCODE_META_LEFT => ModifierState::MetaLeftOn | ModifierState::MetaOn,
+ KEYCODE_META_RIGHT => ModifierState::MetaRightOn | ModifierState::MetaOn,
+ _ => ModifierState::None,
}
}
/// Modifier mask including both left and right versions of a modifier key.
-fn get_symmetrical_modifier_key_mask(keycode: i32) -> u32 {
+fn get_symmetrical_modifier_key_mask(keycode: i32) -> ModifierState {
match keycode {
- KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => META_ALT_LEFT_ON | META_ALT_RIGHT_ON | META_ALT_ON,
+ KEYCODE_ALT_LEFT | KEYCODE_ALT_RIGHT => {
+ ModifierState::AltLeftOn | ModifierState::AltRightOn | ModifierState::AltOn
+ }
KEYCODE_SHIFT_LEFT | KEYCODE_SHIFT_RIGHT => {
- META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON | META_SHIFT_ON
+ ModifierState::ShiftLeftOn | ModifierState::ShiftRightOn | ModifierState::ShiftOn
}
KEYCODE_CTRL_LEFT | KEYCODE_CTRL_RIGHT => {
- META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON | META_CTRL_ON
+ ModifierState::CtrlLeftOn | ModifierState::CtrlRightOn | ModifierState::CtrlOn
}
KEYCODE_META_LEFT | KEYCODE_META_RIGHT => {
- META_META_LEFT_ON | META_META_RIGHT_ON | META_META_ON
+ ModifierState::MetaLeftOn | ModifierState::MetaRightOn | ModifierState::MetaOn
}
- _ => 0,
+ _ => ModifierState::None,
}
}
-fn clear_ephemeral_modifier_state(modifier_state: u32) -> u32 {
+fn clear_ephemeral_modifier_state(modifier_state: ModifierState) -> ModifierState {
modifier_state
- & !(META_ALT_LEFT_ON
- | META_ALT_RIGHT_ON
- | META_ALT_ON
- | META_SHIFT_LEFT_ON
- | META_SHIFT_RIGHT_ON
- | META_SHIFT_ON
- | META_CTRL_LEFT_ON
- | META_CTRL_RIGHT_ON
- | META_CTRL_ON
- | META_META_LEFT_ON
- | META_META_RIGHT_ON
- | META_META_ON)
+ & !(ModifierState::AltLeftOn
+ | ModifierState::AltRightOn
+ | ModifierState::AltOn
+ | ModifierState::ShiftLeftOn
+ | ModifierState::ShiftRightOn
+ | ModifierState::ShiftOn
+ | ModifierState::CtrlLeftOn
+ | ModifierState::CtrlRightOn
+ | ModifierState::CtrlOn
+ | ModifierState::MetaLeftOn
+ | ModifierState::MetaRightOn
+ | ModifierState::MetaOn)
}
#[cfg(test)]
@@ -237,9 +227,7 @@
StickyKeysFilter, KEYCODE_ALT_LEFT, KEYCODE_ALT_RIGHT, KEYCODE_CAPS_LOCK,
KEYCODE_CTRL_LEFT, KEYCODE_CTRL_RIGHT, KEYCODE_FUNCTION, KEYCODE_META_LEFT,
KEYCODE_META_RIGHT, KEYCODE_NUM_LOCK, KEYCODE_SCROLL_LOCK, KEYCODE_SHIFT_LEFT,
- KEYCODE_SHIFT_RIGHT, KEYCODE_SYM, META_ALT_LEFT_ON, META_ALT_ON, META_ALT_RIGHT_ON,
- META_CTRL_LEFT_ON, META_CTRL_ON, META_CTRL_RIGHT_ON, META_META_LEFT_ON, META_META_ON,
- META_META_RIGHT_ON, META_SHIFT_LEFT_ON, META_SHIFT_ON, META_SHIFT_RIGHT_ON,
+ KEYCODE_SHIFT_RIGHT, KEYCODE_SYM,
};
use android_hardware_input_common::aidl::android::hardware::input::common::Source::Source;
use binder::Strong;
@@ -247,6 +235,7 @@
DeviceInfo::DeviceInfo, IInputFilter::IInputFilterCallbacks::IInputFilterCallbacks,
KeyEvent::KeyEvent, KeyEventAction::KeyEventAction,
};
+ use input::ModifierState;
use std::sync::{Arc, RwLock};
static DEVICE_ID: i32 = 1;
@@ -347,30 +336,30 @@
Arc::new(RwLock::new(Strong::new(Box::new(test_callbacks.clone())))),
);
let test_states = &[
- (KEYCODE_ALT_LEFT, META_ALT_ON | META_ALT_LEFT_ON),
- (KEYCODE_ALT_RIGHT, META_ALT_ON | META_ALT_RIGHT_ON),
- (KEYCODE_CTRL_LEFT, META_CTRL_ON | META_CTRL_LEFT_ON),
- (KEYCODE_CTRL_RIGHT, META_CTRL_ON | META_CTRL_RIGHT_ON),
- (KEYCODE_SHIFT_LEFT, META_SHIFT_ON | META_SHIFT_LEFT_ON),
- (KEYCODE_SHIFT_RIGHT, META_SHIFT_ON | META_SHIFT_RIGHT_ON),
- (KEYCODE_META_LEFT, META_META_ON | META_META_LEFT_ON),
- (KEYCODE_META_RIGHT, META_META_ON | META_META_RIGHT_ON),
+ (KEYCODE_ALT_LEFT, ModifierState::AltOn | ModifierState::AltLeftOn),
+ (KEYCODE_ALT_RIGHT, ModifierState::AltOn | ModifierState::AltRightOn),
+ (KEYCODE_CTRL_LEFT, ModifierState::CtrlOn | ModifierState::CtrlLeftOn),
+ (KEYCODE_CTRL_RIGHT, ModifierState::CtrlOn | ModifierState::CtrlRightOn),
+ (KEYCODE_SHIFT_LEFT, ModifierState::ShiftOn | ModifierState::ShiftLeftOn),
+ (KEYCODE_SHIFT_RIGHT, ModifierState::ShiftOn | ModifierState::ShiftRightOn),
+ (KEYCODE_META_LEFT, ModifierState::MetaOn | ModifierState::MetaLeftOn),
+ (KEYCODE_META_RIGHT, ModifierState::MetaOn | ModifierState::MetaRightOn),
];
for test_state in test_states.iter() {
test_filter.clear();
test_callbacks.clear();
sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
- assert_eq!(test_callbacks.get_last_modifier_state(), 0);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
// Re-send keys to lock it
sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_DOWN });
assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
assert_eq!(test_callbacks.get_last_modifier_state(), test_state.1);
@@ -382,8 +371,8 @@
assert_eq!(test_callbacks.get_last_locked_modifier_state(), test_state.1);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: test_state.0, ..BASE_KEY_UP });
- assert_eq!(test_callbacks.get_last_modifier_state(), 0);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
}
}
@@ -398,14 +387,17 @@
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_DOWN });
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEYCODE_CTRL_LEFT, ..BASE_KEY_UP });
- assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
- assert_eq!(test_callbacks.get_last_modifier_state(), 0);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
}
#[test]
@@ -427,20 +419,26 @@
assert_eq!(
test_callbacks.get_last_modifier_state(),
- META_SHIFT_LEFT_ON | META_SHIFT_ON | META_CTRL_LEFT_ON | META_CTRL_ON
+ ModifierState::ShiftLeftOn
+ | ModifierState::ShiftOn
+ | ModifierState::CtrlLeftOn
+ | ModifierState::CtrlOn
);
assert_eq!(
test_callbacks.get_last_locked_modifier_state(),
- META_CTRL_LEFT_ON | META_CTRL_ON
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
- assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
assert_eq!(
test_callbacks.get_last_locked_modifier_state(),
- META_CTRL_LEFT_ON | META_CTRL_ON
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
);
}
@@ -458,13 +456,13 @@
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_DOWN });
assert_eq!(
test_filter.last_event().unwrap().metaState as u32,
- META_CTRL_LEFT_ON | META_CTRL_ON
+ (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
);
sticky_keys_filter.notify_key(&KeyEvent { keyCode: KEY_A, ..BASE_KEY_UP });
assert_eq!(
test_filter.last_event().unwrap().metaState as u32,
- META_CTRL_LEFT_ON | META_CTRL_ON
+ (ModifierState::CtrlLeftOn | ModifierState::CtrlOn).bits()
);
}
@@ -499,15 +497,18 @@
});
sticky_keys_filter.notify_devices_changed(&[DeviceInfo { deviceId: 2, external: true }]);
- assert_eq!(test_callbacks.get_last_modifier_state(), META_CTRL_LEFT_ON | META_CTRL_ON);
+ assert_eq!(
+ test_callbacks.get_last_modifier_state(),
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
+ );
assert_eq!(
test_callbacks.get_last_locked_modifier_state(),
- META_CTRL_LEFT_ON | META_CTRL_ON
+ ModifierState::CtrlLeftOn | ModifierState::CtrlOn
);
sticky_keys_filter.notify_devices_changed(&[]);
- assert_eq!(test_callbacks.get_last_modifier_state(), 0);
- assert_eq!(test_callbacks.get_last_locked_modifier_state(), 0);
+ assert_eq!(test_callbacks.get_last_modifier_state(), ModifierState::None);
+ assert_eq!(test_callbacks.get_last_locked_modifier_state(), ModifierState::None);
}
fn setup_filter(
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index 2415e42..cf0d46a 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 de74067..cda067f 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::LogicalDisplayId::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,16 +156,12 @@
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) {
- mReaderConfiguration.pointerCaptureRequest.enable = enabled;
+ mReaderConfiguration.pointerCaptureRequest.window = enabled ? sp<BBinder>::make() : nullptr;
mReaderConfiguration.pointerCaptureRequest.seq = 1;
int32_t generation = mDevice->getGeneration();
std::list<NotifyArgs> args =
@@ -200,8 +195,6 @@
input_flags::enable_new_mouse_pointer_ballistics(false);
CursorInputMapperUnitTestBase::SetUp();
}
-
- bool isPointerChoreographerEnabled() override { return false; }
};
TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) {
@@ -325,12 +318,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 +332,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();
@@ -580,7 +534,6 @@
// need to be rotated.
mPropertyMap.addProperty("cursor.mode", "navigation");
mPropertyMap.addProperty("cursor.orientationAware", "1");
- createDevice();
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90);
mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
@@ -598,7 +551,6 @@
// Since InputReader works in the un-rotated coordinate space, only devices that are not
// orientation-aware are affected by display rotation.
mPropertyMap.addProperty("cursor.mode", "navigation");
- createDevice();
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0);
mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
@@ -649,334 +601,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,15 +636,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,14 +656,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);
mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
@@ -1057,13 +679,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 +766,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 +810,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 +835,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 +883,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 +895,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 +904,8 @@
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::LogicalDisplayId::INVALID),
WithCoords(0.0f, 0.0f)))));
}
@@ -1302,8 +916,6 @@
input_flags::enable_new_mouse_pointer_ballistics(true);
CursorInputMapperUnitTestBase::SetUp();
}
-
- bool isPointerChoreographerEnabled() override { return true; }
};
TEST_F(CursorInputMapperUnitTestWithNewBallistics, PointerCaptureDisablesVelocityProcessing) {
@@ -1342,7 +954,6 @@
mPropertyMap.addProperty("cursor.mode", "pointer");
DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation0);
mReaderConfiguration.setDisplayViewports({primaryViewport});
- createDevice();
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, primaryViewport);
mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
@@ -1380,7 +991,6 @@
mReaderConfiguration.setDisplayViewports({primaryViewport});
// Disable acceleration for the display.
mReaderConfiguration.displaysWithMousePointerAccelerationDisabled.emplace(DISPLAY_ID);
- createDevice();
// Don't associate the device with the display yet.
ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID,
@@ -1422,14 +1032,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 +1144,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..3df05f4
--- /dev/null
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp
@@ -0,0 +1,500 @@
+/*
+ * 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::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) {
+ mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching;
+}
+
+void FakeInputDispatcherPolicy::assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+
+ if (!mFocusedDisplayNotifiedCondition.wait_for(lock, 100ms,
+ [this, expectedDisplay]() REQUIRES(mLock) {
+ if (!mNotifiedFocusedDisplay.has_value() ||
+ mNotifiedFocusedDisplay.value() !=
+ expectedDisplay) {
+ return false;
+ }
+ return true;
+ })) {
+ ADD_FAILURE() << "Timed out waiting for notifyFocusedDisplayChanged(" << expectedDisplay
+ << ") to be called.";
+ }
+}
+
+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) {
+ if (mConsumeKeyBeforeDispatching) {
+ return -1;
+ }
+ 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;
+}
+
+void FakeInputDispatcherPolicy::notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) {
+ std::scoped_lock lock(mLock);
+ mNotifiedFocusedDisplay = displayId;
+ mFocusedDisplayNotifiedCondition.notify_all();
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
index fb2db06..a0f3ea9 100644
--- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h
+++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h
@@ -16,76 +16,199 @@
#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();
+ void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching);
+ void assertFocusedDisplayNotified(ui::LogicalDisplayId expectedDisplay);
+
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<ui::LogicalDisplayId> mNotifiedFocusedDisplay GUARDED_BY(mLock);
+ std::condition_variable mFocusedDisplayNotifiedCondition;
+
+ 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;
+
+ bool mConsumeKeyBeforeDispatching = false;
+
+ 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 notifyFocusedDisplayChanged(ui::LogicalDisplayId displayId) 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 {}
+ 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 9e93712..d2cb0ac 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;
@@ -129,7 +129,7 @@
void FakeInputReaderPolicy::addInputPortAssociation(const std::string& inputPort,
uint8_t displayPort) {
- mConfig.portAssociations.insert({inputPort, displayPort});
+ mConfig.inputPortToDisplayPortAssociations.insert({inputPort, displayPort});
}
void FakeInputReaderPolicy::addDeviceTypeAssociation(const std::string& inputPort,
@@ -139,7 +139,7 @@
void FakeInputReaderPolicy::addInputUniqueIdAssociation(const std::string& inputUniqueId,
const std::string& displayUniqueId) {
- mConfig.uniqueIdAssociations.insert({inputUniqueId, displayUniqueId});
+ mConfig.inputPortToDisplayUniqueIdAssociations.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;
}
@@ -178,16 +173,12 @@
transform = t;
}
-PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(bool enabled) {
- mConfig.pointerCaptureRequest = {enabled, mNextPointerCaptureSequenceNumber++};
+PointerCaptureRequest FakeInputReaderPolicy::setPointerCapture(const sp<IBinder>& window) {
+ mConfig.pointerCaptureRequest = {window, mNextPointerCaptureSequenceNumber++};
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 da5085d..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(bool enabled);
- void setShowTouches(bool enabled);
- void setDefaultPointerDisplayId(int32_t pointerDisplayId);
+ PointerCaptureRequest setPointerCapture(const sp<IBinder>& window);
+ 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 4655ee8..b46055e 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -37,10 +37,9 @@
return std::visit([](const auto& event) { return event.id; }, v);
}
-MotionEvent toInputEvent(
- const trace::TracedMotionEvent& e,
- const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
- const std::array<uint8_t, 32>& hmac) {
+MotionEvent toInputEvent(const trace::TracedMotionEvent& e,
+ const trace::WindowDispatchArgs& dispatchArgs,
+ const std::array<uint8_t, 32>& hmac) {
MotionEvent traced;
traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action, e.actionButton,
dispatchArgs.resolvedFlags, e.edgeFlags, e.metaState, e.buttonState,
@@ -51,13 +50,12 @@
return traced;
}
-KeyEvent toInputEvent(const trace::TracedKeyEvent& e,
- const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
+KeyEvent toInputEvent(const trace::TracedKeyEvent& e, const trace::WindowDispatchArgs& dispatchArgs,
const std::array<uint8_t, 32>& hmac) {
KeyEvent traced;
traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action,
- dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState, e.repeatCount,
- e.downTime, e.eventTime);
+ dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState,
+ dispatchArgs.resolvedKeyRepeatCount, e.downTime, e.eventTime);
return traced;
}
@@ -120,7 +118,7 @@
auto tracedDispatchesIt =
std::find_if(mTracedWindowDispatches.begin(), mTracedWindowDispatches.end(),
- [&](const WindowDispatchArgs& args) {
+ [&](const trace::WindowDispatchArgs& args) {
return args.windowId == expectedWindowId &&
getId(args.eventEntry) == expectedEvent.getId();
});
@@ -163,7 +161,8 @@
// --- FakeInputTracingBackend ---
-void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) {
+void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event,
+ const trace::TracedEventMetadata&) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -171,7 +170,8 @@
mTrace->mEventTracedCondition.notify_all();
}
-void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) {
+void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event,
+ const trace::TracedEventMetadata&) {
{
std::scoped_lock lock(mTrace->mLock);
mTrace->mTracedEvents.emplace(event.id, event);
@@ -179,7 +179,8 @@
mTrace->mEventTracedCondition.notify_all();
}
-void FakeInputTracingBackend::traceWindowDispatch(const WindowDispatchArgs& args) {
+void FakeInputTracingBackend::traceWindowDispatch(const trace::WindowDispatchArgs& args,
+ 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 1b3613d..cd4b507 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -59,8 +59,7 @@
std::mutex mLock;
std::condition_variable mEventTracedCondition;
std::unordered_map<uint32_t /*eventId*/, trace::TracedEvent> mTracedEvents GUARDED_BY(mLock);
- using WindowDispatchArgs = trace::InputTracingBackendInterface::WindowDispatchArgs;
- std::vector<WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock);
+ std::vector<trace::WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock);
std::vector<std::pair<std::variant<KeyEvent, MotionEvent>, int32_t /*windowId*/>>
mExpectedEvents GUARDED_BY(mLock);
@@ -83,9 +82,12 @@
private:
std::shared_ptr<VerifyingTrace> mTrace;
- void traceKeyEvent(const trace::TracedKeyEvent& entry) override;
- void traceMotionEvent(const trace::TracedMotionEvent& entry) override;
- void traceWindowDispatch(const WindowDispatchArgs& entry) override;
+ void traceKeyEvent(const trace::TracedKeyEvent& entry,
+ const trace::TracedEventMetadata&) override;
+ void traceMotionEvent(const trace::TracedMotionEvent& entry,
+ const trace::TracedEventMetadata&) override;
+ void traceWindowDispatch(const trace::WindowDispatchArgs& entry,
+ const trace::TracedEventMetadata&) override;
};
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index dc199e2..d0998ba 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::LogicalDisplayId::INVALID;
}
return *mDisplayId;
}
@@ -76,7 +76,17 @@
mCustomIconStyle = icon.style;
}
-void FakePointerController::assertViewportSet(int32_t displayId) {
+void FakePointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) {
+ mDisplaysToSkipScreenshotFlagChanged = true;
+ mDisplaysToSkipScreenshot.insert(displayId);
+}
+
+void FakePointerController::clearSkipScreenshotFlags() {
+ mDisplaysToSkipScreenshotFlagChanged = true;
+ mDisplaysToSkipScreenshot.clear();
+}
+
+void FakePointerController::assertViewportSet(ui::LogicalDisplayId displayId) {
ASSERT_TRUE(mDisplayId);
ASSERT_EQ(displayId, mDisplayId);
}
@@ -91,7 +101,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 +127,23 @@
ASSERT_EQ(std::nullopt, mCustomIconStyle);
}
+void FakePointerController::assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId) {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) != mDisplaysToSkipScreenshot.end());
+}
+
+void FakePointerController::assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId) {
+ ASSERT_TRUE(mDisplaysToSkipScreenshot.find(displayId) == mDisplaysToSkipScreenshot.end());
+}
+
+void FakePointerController::assertSkipScreenshotFlagChanged() {
+ ASSERT_TRUE(mDisplaysToSkipScreenshotFlagChanged);
+ mDisplaysToSkipScreenshotFlagChanged = false;
+}
+
+void FakePointerController::assertSkipScreenshotFlagNotChanged() {
+ ASSERT_FALSE(mDisplaysToSkipScreenshotFlagChanged);
+}
+
bool FakePointerController::isPointerShown() {
return mIsPointerShown;
}
@@ -150,7 +177,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..2c76c62 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,30 @@
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 setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
+ void clearSkipScreenshotFlags() 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 assertIsSkipScreenshotFlagSet(ui::LogicalDisplayId displayId);
+ void assertIsSkipScreenshotFlagNotSet(ui::LogicalDisplayId displayId);
+ void assertSkipScreenshotFlagChanged();
+ void assertSkipScreenshotFlagNotChanged();
bool isPointerShown();
private:
@@ -64,19 +70,21 @@
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;
+ bool mDisplaysToSkipScreenshotFlagChanged{false};
};
} // namespace android
diff --git a/services/inputflinger/tests/FakeWindowHandle.h b/services/inputflinger/tests/FakeWindowHandle.h
deleted file mode 100644
index fe25130..0000000
--- a/services/inputflinger/tests/FakeWindowHandle.h
+++ /dev/null
@@ -1,274 +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 "../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..b116521
--- /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::LogicalDisplayId::INVALID, 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::LogicalDisplayId::INVALID, 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::LogicalDisplayId::INVALID, 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::LogicalDisplayId::INVALID, 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..3a3238a
--- /dev/null
+++ b/services/inputflinger/tests/FakeWindows.h
@@ -0,0 +1,413 @@
+/*
+ * 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::LogicalDisplayId::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::LogicalDisplayId::DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionDown(
+ ui::LogicalDisplayId expectedDisplayId = ui::LogicalDisplayId::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::LogicalDisplayId::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 consumeMotionPointerDown(int32_t pointerIdx,
+ const ::testing::Matcher<MotionEvent>& matcher) {
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeMotionEvent(testing::AllOf(WithMotionAction(action), matcher));
+ }
+
+ inline void consumeMotionPointerUp(int32_t pointerIdx,
+ const ::testing::Matcher<MotionEvent>& matcher) {
+ const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ consumeMotionEvent(testing::AllOf(WithMotionAction(action), matcher));
+ }
+
+ inline void consumeMotionUp(
+ ui::LogicalDisplayId expectedDisplayId = ui::LogicalDisplayId::DEFAULT,
+ int32_t expectedFlags = 0) {
+ consumeMotionEvent(testing::AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithDisplayId(expectedDisplayId),
+ WithFlags(expectedFlags)));
+ }
+
+ inline void consumeMotionOutside(
+ ui::LogicalDisplayId expectedDisplayId = ui::LogicalDisplayId::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 2ff9c3c..f794da5 100644
--- a/services/inputflinger/tests/FocusResolver_test.cpp
+++ b/services/inputflinger/tests/FocusResolver_test.cpp
@@ -20,6 +20,7 @@
#define ASSERT_FOCUS_CHANGE(_changes, _oldFocus, _newFocus) \
{ \
+ ASSERT_TRUE(_changes.has_value()); \
ASSERT_EQ(_oldFocus, _changes->oldFocus); \
ASSERT_EQ(_newFocus, _changes->newFocus); \
}
@@ -73,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;
@@ -152,6 +153,39 @@
ASSERT_FOCUS_CHANGE(changes, /*from*/ invisibleWindowToken, /*to*/ nullptr);
}
+TEST(FocusResolverTest, FocusTransferToMirror) {
+ sp<IBinder> focusableWindowToken = sp<BBinder>::make();
+ auto window = sp<FakeWindowHandle>::make("Window", focusableWindowToken,
+ /*focusable=*/true, /*visible=*/true);
+ auto mirror = sp<FakeWindowHandle>::make("Mirror", focusableWindowToken,
+ /*focusable=*/true, /*visible=*/true);
+
+ FocusRequest request;
+ request.displayId = 42;
+ request.token = focusableWindowToken;
+ FocusResolver focusResolver;
+ std::optional<FocusResolver::FocusChanges> changes =
+ focusResolver.setFocusedWindow(request, {window, mirror});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ focusableWindowToken);
+
+ // The mirror window now comes on top, and the focus does not change
+ 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(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(ui::LogicalDisplayId{request.displayId}, {mirror});
+ ASSERT_FALSE(changes.has_value());
+
+ // All windows removed
+ changes = focusResolver.setInputWindows(ui::LogicalDisplayId{request.displayId}, {});
+ ASSERT_FOCUS_CHANGE(changes, /*from*/ focusableWindowToken, /*to*/ nullptr);
+}
+
TEST(FocusResolverTest, SetInputWindows) {
sp<IBinder> focusableWindowToken = sp<BBinder>::make();
std::vector<sp<WindowInfoHandle>> windows;
@@ -169,9 +203,13 @@
focusResolver.setFocusedWindow(request, windows);
ASSERT_EQ(focusableWindowToken, changes->newFocus);
+ // When there are no changes to the window, focus does not change
+ 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);
}
@@ -195,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);
}
@@ -219,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);
}
@@ -270,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);
}
@@ -378,21 +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);
-
- // Start with a focused window
- window->setFocusable(true);
- changes = focusResolver.setInputWindows(request.displayId, windows);
- ASSERT_FOCUS_CHANGE(changes, /*from*/ nullptr, /*to*/ windowToken);
+ 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 readded, the window does not get focus since the request was cleared.
- changes = focusResolver.setInputWindows(request.displayId, windows);
+ // When a display is re-added, the window does not get focus since the request was cleared.
+ 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..d0cd677 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"
@@ -51,13 +50,11 @@
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 +65,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 +87,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::LogicalDisplayId::DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -118,38 +100,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),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
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::LogicalDisplayId::DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -157,24 +132,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),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
TEST_F(GestureConverterTest, ButtonsChange) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
// Press left and right buttons at once
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -197,9 +169,9 @@
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY |
AMOTION_EVENT_BUTTON_SECONDARY)))));
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(ui::LogicalDisplayId::DEFAULT)))));
// Then release the left button
Gesture leftUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -210,9 +182,9 @@
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),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY), WithCoords(0, 0),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
// Finally release the right button
Gesture rightUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -228,16 +200,15 @@
VariantWith<NotifyMotionArgs>(
WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithButtonState(0),
- WithCoords(POINTER_X, POINTER_Y),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -250,14 +221,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(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
// Press the button
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -274,22 +245,19 @@
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY)))));
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(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT)))));
// Release the button
Gesture upGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
@@ -305,17 +273,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),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -323,31 +290,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::LogicalDisplayId::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::LogicalDisplayId::DEFAULT)))));
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 1, 1,
GESTURES_FLING_START);
@@ -355,18 +321,19 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -382,52 +349,53 @@
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::LogicalDisplayId::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)))));
+ WithDisplayId(ui::LogicalDisplayId::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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -445,13 +413,13 @@
ASSERT_THAT(args,
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::NONE),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -473,10 +441,47 @@
EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()), WithGestureScrollDistance(0, 0, EPSILON));
}
+TEST_F(GestureConverterTest, Scroll_ClearsFakeFingerPositionOnSubsequentScrollGestures) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 15, -10);
+ std::list<NotifyArgs> args =
+ converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
+
+ Gesture continueGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -2, -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 flingGestureEnd(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, 0,
+ GESTURES_FLING_TAP_DOWN);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGestureEnd);
+
+ // Start a second scoll gesture, and ensure the fake finger is reset to (0, 0), instead of
+ // continuing from the position where the last scroll gesture's fake finger ended.
+ Gesture secondScrollStart(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 2,
+ 14);
+ args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, secondScrollStart);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT)),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0, 0),
+ WithGestureScrollDistance(0, 0, EPSILON))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(2, 14),
+ WithGestureScrollDistance(-2, -14, EPSILON)))));
+}
+
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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/0);
@@ -497,7 +502,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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/5,
/*dy=*/5);
@@ -524,7 +529,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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dx= */ 0,
/* dy= */ 10);
@@ -535,7 +540,7 @@
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithGestureSwipeFingerCount(3), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
// Three fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -581,7 +586,7 @@
WithGestureOffset(0, -0.005, EPSILON), WithGestureSwipeFingerCount(3),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithPointerCount(3u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::LogicalDisplayId::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 +624,27 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::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::LogicalDisplayId::DEFAULT))));
// Three fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -681,7 +688,7 @@
ASSERT_THAT(arg,
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
WithGestureOffset(0, -0.005, EPSILON), WithPointerCount(3u),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::LogicalDisplayId::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 +713,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u))),
VariantWith<NotifyMotionArgs>(
- WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER))));
- ASSERT_THAT(args, Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ADISPLAY_ID_DEFAULT))));
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)))));
+ ASSERT_THAT(args,
+ Each(VariantWith<NotifyMotionArgs>(WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureFourFingerSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dx= */ 10, /* dy= */ 0);
@@ -724,7 +732,7 @@
Each(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithGestureSwipeFingerCount(4), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
// Four fake fingers should be created. We don't actually care where they are, so long as they
// move appropriately.
@@ -779,7 +787,7 @@
WithGestureOffset(0.005, 0, EPSILON), WithGestureSwipeFingerCount(4),
WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
WithPointerCount(4u), WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::LogicalDisplayId::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 +836,18 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -847,20 +856,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::LogicalDisplayId::DEFAULT)))));
Gesture updateGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* dz= */ 0.8, GESTURES_ZOOM_UPDATE);
@@ -870,10 +877,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::LogicalDisplayId::DEFAULT)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -893,17 +899,18 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
TEST_F(GestureConverterTest, Pinch_Outwards) {
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_START);
@@ -912,33 +919,30 @@
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::LogicalDisplayId::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)))));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
Gesture endGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* dz= */ 1,
GESTURES_ZOOM_END);
@@ -958,17 +962,18 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -993,7 +998,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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -1020,7 +1025,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::LogicalDisplayId::DEFAULT);
Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
@@ -1044,15 +1049,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithButtonState(0)))));
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(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
(void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, startGesture);
@@ -1061,24 +1066,25 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
/*dy=*/10);
@@ -1112,14 +1118,15 @@
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
WithMotionClassification(MotionClassification::NONE)))));
ASSERT_THAT(args,
- Each(VariantWith<NotifyMotionArgs>(AllOf(WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
GESTURES_ZOOM_START);
@@ -1141,17 +1148,18 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture tapDownGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/*vx=*/0.f, /*vy=*/0.f, GESTURES_FLING_TAP_DOWN);
@@ -1159,20 +1167,17 @@
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(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture scrollGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
std::list<NotifyArgs> args =
@@ -1192,19 +1197,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(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1241,17 +1246,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),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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::LogicalDisplayId::DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1278,15 +1283,16 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::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),
@@ -1300,9 +1306,9 @@
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)))));
+ AllOf(WithButtonState(0), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
}
TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled,
@@ -1315,7 +1321,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1344,7 +1350,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
Gesture flingGesture(kGestureFling, currentTime, currentTime, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1426,7 +1432,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
Gesture flingGesture(kGestureFling, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* vx= */ 0,
/* vy= */ 0, GESTURES_FLING_TAP_DOWN);
@@ -1438,6 +1444,7 @@
/* 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),
@@ -1452,10 +1459,10 @@
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)))));
+ Each(VariantWith<NotifyMotionArgs>(
+ AllOf(WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
Gesture buttonUpGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
/* down= */ GESTURES_BUTTON_NONE,
@@ -1466,18 +1473,23 @@
ElementsAre(VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
- WithButtonState(0), WithPressure(1.0f))),
+ WithButtonState(0), WithCoords(0, 0),
+ WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(1.0f),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT))),
VariantWith<NotifyMotionArgs>(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithButtonState(0), WithPressure(0.0f))),
+ WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT))),
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)))));
+ WithCoords(0, 0), WithRelativeMotion(0, 0),
+ WithToolType(ToolType::FINGER), WithButtonState(0),
+ WithPressure(0.0f),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)))));
// Future taps should be re-enabled
ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
@@ -1490,7 +1502,7 @@
InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
- converter.setDisplayId(ADISPLAY_ID_DEFAULT);
+ converter.setDisplayId(ui::LogicalDisplayId::DEFAULT);
Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10);
std::list<NotifyArgs> args =
@@ -1506,1433 +1518,7 @@
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);
-
- 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)))));
-
- 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)))));
-
- 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), WithCoords(0, 0),
- WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(1.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
- WithCoords(0, 0), WithRelativeMotion(0.f, 0.f),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))),
- VariantWith<NotifyMotionArgs>(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER),
- WithCoords(0, 0), WithRelativeMotion(0, 0),
- WithToolType(ToolType::FINGER), WithButtonState(0),
- WithPressure(0.0f),
- WithDisplayId(ADISPLAY_ID_DEFAULT)))));
-
- // Future taps should be re-enabled
- ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps());
-}
-
-TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, 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(GestureConverterTestWithChoreographer, 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::LogicalDisplayId::DEFAULT);
// Start a move gesture at gestureStartTime
Gesture moveGesture(kGestureMove, gestureStartTime, gestureStartTime, -5, 10);
diff --git a/services/inputflinger/tests/HardwareStateConverter_test.cpp b/services/inputflinger/tests/HardwareStateConverter_test.cpp
index ff9bd9e..34c81fc 100644
--- a/services/inputflinger/tests/HardwareStateConverter_test.cpp
+++ b/services/inputflinger/tests/HardwareStateConverter_test.cpp
@@ -81,7 +81,7 @@
event.type = type;
event.code = code;
event.value = value;
- std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(&event);
+ std::optional<SelfContainedHardwareState> schs = mConverter->processRawEvent(event);
EXPECT_FALSE(schs.has_value());
}
@@ -93,7 +93,7 @@
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
- return mConverter->processRawEvent(&event);
+ return mConverter->processRawEvent(event);
}
std::shared_ptr<FakeEventHub> mFakeEventHub;
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index 85e055d..28699b8 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::LogicalDisplayId::INVALID);
info.addSource(sources);
return info;
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index dc13fed..2056372 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>
@@ -33,6 +35,7 @@
#include <gtest/gtest.h>
#include <input/BlockingQueue.h>
#include <input/Input.h>
+#include <input/InputConsumer.h>
#include <input/PrintTools.h>
#include <linux/input.h>
#include <sys/epoll.h>
@@ -56,6 +59,8 @@
using namespace ftl::flag_operators;
using testing::AllOf;
using testing::Not;
+using testing::Pointee;
+using testing::UnorderedElementsAre;
namespace {
@@ -67,8 +72,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 = ui::LogicalDisplayId::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);
@@ -105,10 +110,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};
@@ -116,24 +117,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;
@@ -144,537 +128,66 @@
static KeyEvent getTestKeyEvent() {
KeyEvent event;
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
- ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID, 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(bool enabled) {
- std::unique_lock lock(mLock);
- base::ScopedLockAssertion assumeLocked(mLock);
-
- if (!mPointerCaptureChangedCondition.wait_for(lock, 100ms,
- [this, enabled]() REQUIRES(mLock) {
- return mPointerCaptureRequest->enable ==
- enabled;
- })) {
- ADD_FAILURE() << "Timed out waiting for setPointerCapture(" << 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->enable);
- }
- 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;
- }
+ 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
@@ -682,12 +195,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
*/
@@ -707,7 +243,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);
}
};
@@ -716,8 +252,8 @@
KeyEvent event;
// Rejects undefined key actions.
- event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE,
- INVALID_HMAC,
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID, INVALID_HMAC,
/*action=*/-1, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME,
ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
@@ -726,9 +262,9 @@
<< "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,
- INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0, AKEYCODE_A, KEY_A, AMETA_NONE, 0,
- ARBITRARY_TIME, ARBITRARY_TIME);
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID, INVALID_HMAC, AKEY_EVENT_ACTION_MULTIPLE, 0,
+ AKEYCODE_A, KEY_A, AMETA_NONE, 0, ARBITRARY_TIME, ARBITRARY_TIME);
ASSERT_EQ(InputEventInjectionResult::FAILED,
mDispatcher->injectInputEvent(&event, /*targetUid=*/{}, InputEventInjectionSync::NONE,
0ms, 0))
@@ -899,627 +435,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)));
- }
-
- inline void consumeMotionPointerDown(int32_t pointerIdx,
- const ::testing::Matcher<MotionEvent>& matcher) {
- const int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeMotionEvent(testing::AllOf(WithMotionAction(action), matcher));
- }
-
- 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)));
- }
-
- inline void consumeMotionPointerUp(int32_t pointerIdx,
- const ::testing::Matcher<MotionEvent>& matcher) {
- const int32_t action = AMOTION_EVENT_ACTION_POINTER_UP |
- (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- consumeMotionEvent(testing::AllOf(WithMotionAction(action), matcher));
- }
-
- 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);
}
@@ -1531,22 +456,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),
@@ -1556,7 +481,7 @@
void consumeMotionPointerDown(int32_t pointerIdx) {
int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
(pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
- mInputReceiver.consumeEvent(InputEventType::MOTION, action, ADISPLAY_ID_DEFAULT,
+ mInputReceiver.consumeEvent(InputEventType::MOTION, action, ui::LogicalDisplayId::DEFAULT,
/*expectedFlags=*/0);
}
@@ -1574,7 +499,7 @@
static InputEventInjectionResult injectKey(
InputDispatcher& dispatcher, int32_t action, int32_t repeatCount,
- int32_t displayId = ADISPLAY_ID_NONE,
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID,
InputEventInjectionSync syncMode = InputEventInjectionSync::WAIT_FOR_RESULT,
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
bool allowKeyRepeat = true, std::optional<gui::Uid> targetUid = {},
@@ -1596,30 +521,34 @@
static void assertInjectedKeyTimesOut(InputDispatcher& dispatcher) {
InputEventInjectionResult result =
- injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_NONE,
- InputEventInjectionSync::WAIT_FOR_RESULT, CONSUME_TIMEOUT_NO_EVENT_EXPECTED);
+ injectKey(dispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::INVALID, 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::LogicalDisplayId::INVALID) {
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::LogicalDisplayId::INVALID) {
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::LogicalDisplayId::INVALID) {
return injectKey(dispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0, displayId);
}
@@ -1633,7 +562,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},
@@ -1659,18 +588,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::LogicalDisplayId::INVALID) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
@@ -1680,30 +610,8 @@
return args;
}
-static NotifyKeyArgs generateSystemShortcutArgs(int32_t action,
- int32_t displayId = ADISPLAY_ID_NONE) {
- nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- // Define a valid key event.
- NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
- AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C,
- AMETA_META_ON, currentTime);
-
- return args;
-}
-
-static NotifyKeyArgs generateAssistantKeyArgs(int32_t action,
- int32_t displayId = ADISPLAY_ID_NONE) {
- nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
- // Define a valid key event.
- NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID,
- AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST,
- KEY_ASSISTANT, AMETA_NONE, currentTime);
-
- return args;
-}
-
[[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) {
@@ -1740,7 +648,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}});
}
@@ -1758,9 +667,9 @@
*/
TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher,
- "Window that breaks its input channel", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Window that breaks its input channel",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -1771,16 +680,18 @@
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
using InputDispatcherDeathTest = InputDispatcherTest;
@@ -1794,8 +705,9 @@
ScopedSilentDeath _silentDeath;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
ASSERT_DEATH(mDispatcher->onWindowInfosChanged(
{{*window->getInfo(), *window->getInfo()}, {}, 0, 0}),
"Incorrect WindowInfosUpdate provided");
@@ -1803,17 +715,19 @@
TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
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::LogicalDisplayId::INVALID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
/**
@@ -1823,18 +737,19 @@
*/
TEST_F(InputDispatcherTest, SetInputWindowOnceWithSingleTouchWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
/**
@@ -1842,37 +757,40 @@
*/
TEST_F(InputDispatcherTest, SetInputWindowTwice_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
// The foreground window should receive the first touch down event.
TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowTop = sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> windowSecond =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged(
{{*windowTop->getInfo(), *windowSecond->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Top window should receive the touch down event. Second window should not receive anything.
- windowTop->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ windowTop->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
windowSecond->assertNoEvents();
}
@@ -1886,10 +804,12 @@
TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> foregroundWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
foregroundWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> wallpaperWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaperWindow->setIsWallpaper(true);
mDispatcher->onWindowInfosChanged(
@@ -1903,7 +823,7 @@
// Both foreground window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
@@ -1913,13 +833,13 @@
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionEvent(WithMotionAction(ACTION_MOVE));
- wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionMove(ui::LogicalDisplayId::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(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -1929,8 +849,8 @@
*/
TEST_F(InputDispatcherTest, CancelAfterPointer0Up) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// First touch pointer down on right window
@@ -1969,30 +889,32 @@
TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> foregroundWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
foregroundWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> wallpaperWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaperWindow->setIsWallpaper(true);
mDispatcher->onWindowInfosChanged(
{{*foregroundWindow->getInfo(), *wallpaperWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both foreground window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 200}))
+ ui::LogicalDisplayId::DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
foregroundWindow->consumeMotionMove();
- wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Wallpaper closes its channel, but the window remains.
wallpaperWindow->destroyReceiver();
@@ -2004,6 +926,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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ui::LogicalDisplayId::DEFAULT);
+ rightForegroundWindow->setFrame(Rect(100, 0, 200, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> middleForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle foreground window",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::DEFAULT);
+ middleWallpaperWindow->setFrame(Rect(100, 0, 200, 100));
+ middleWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ui::LogicalDisplayId::DEFAULT);
+ rightForegroundWindow->setFrame(Rect(200, 0, 300, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftForegroundWindow->setFrame(Rect(0, 0, 100, 100));
+ leftForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> leftWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left wallpaper window",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWallpaperWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> middleForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle foreground window",
+ ui::LogicalDisplayId::DEFAULT);
+ middleForegroundWindow->setFrame(Rect(100, 0, 200, 100));
+ middleForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> middleWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Middle wallpaper window",
+ ui::LogicalDisplayId::DEFAULT);
+ middleWallpaperWindow->setFrame(Rect(100, 0, 200, 100));
+ middleWallpaperWindow->setIsWallpaper(true);
+
+ sp<FakeWindowHandle> rightForegroundWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right foreground window",
+ ui::LogicalDisplayId::DEFAULT);
+ rightForegroundWindow->setFrame(Rect(200, 0, 300, 100));
+ rightForegroundWindow->setDupTouchToWallpaper(true);
+ sp<FakeWindowHandle> rightWallpaperWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right wallpaper window",
+ ui::LogicalDisplayId::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,
@@ -2016,12 +1233,14 @@
TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> foregroundWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
foregroundWindow->setDupTouchToWallpaper(true);
foregroundWindow->setPreventSplitting(GetParam());
sp<FakeWindowHandle> wallpaperWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaperWindow->setIsWallpaper(true);
mDispatcher->onWindowInfosChanged(
@@ -2029,13 +1248,13 @@
// Touch down on top window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both top window and its wallpaper should receive the touch down
foregroundWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Second finger down on the top window
const MotionEvent secondFingerDownEvent =
@@ -2048,14 +1267,13 @@
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);
+ wallpaperWindow->consumeMotionPointerDown(/*pointerIndex=*/1, ui::LogicalDisplayId::DEFAULT,
+ EXPECTED_WALLPAPER_FLAGS);
const MotionEvent secondFingerUpEvent =
MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(100))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
@@ -2064,14 +1282,17 @@
injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- foregroundWindow->consumeMotionPointerUp(0);
- wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ foregroundWindow->consumeMotionPointerUp(/*pointerIdx=*/0,
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT));
+ wallpaperWindow->consumeMotionPointerUp(/*pointerIdx=*/0,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(EXPECTED_WALLPAPER_FLAGS)));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_UP,
AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
.x(100)
@@ -2079,8 +1300,8 @@
.build(),
INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ foregroundWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
+ wallpaperWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -2093,18 +1314,19 @@
*/
TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
leftWindow->setDupTouchToWallpaper(true);
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
rightWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> wallpaperWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaperWindow->setFrame(Rect(0, 0, 400, 200));
wallpaperWindow->setIsWallpaper(true);
@@ -2116,13 +1338,13 @@
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both foreground window and its wallpaper should receive the touch down
leftWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Second finger down on the right window
const MotionEvent secondFingerDownEvent =
@@ -2138,16 +1360,16 @@
leftWindow->consumeMotionMove();
// Since the touch is split, right window gets ACTION_DOWN
- rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- wallpaperWindow->consumeMotionPointerDown(/*pointerIndex=*/1, ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags);
+ rightWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ wallpaperWindow->consumeMotionPointerDown(/*pointerIndex=*/1, ui::LogicalDisplayId::DEFAULT,
+ 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(ui::LogicalDisplayId::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.
@@ -2174,18 +1396,19 @@
*/
TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
leftWindow->setDupTouchToWallpaper(true);
leftWindow->setSlippery(true);
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
sp<FakeWindowHandle> wallpaperWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaperWindow->setIsWallpaper(true);
mDispatcher->onWindowInfosChanged(
@@ -2196,23 +1419,23 @@
// Touch down on left window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both foreground window and its wallpaper should receive the touch down
leftWindow->consumeMotionDown();
- wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaperWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Move to right window, the left window should receive cancel.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {201, 100}))
+ ui::LogicalDisplayId::DEFAULT, {201, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
leftWindow->consumeMotionCancel();
- rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ rightWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ wallpaperWindow->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
}
/**
@@ -2233,14 +1456,14 @@
*/
TEST_F(InputDispatcherTest, TwoPointerCancelInconsistentPolicy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> spyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -2311,8 +1534,8 @@
TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 300, 300));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2369,14 +1592,16 @@
* 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);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
mDispatcher->onWindowInfosChanged(
@@ -2465,6 +1690,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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::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.
@@ -2475,8 +1793,8 @@
*/
TEST_F(InputDispatcherTest, HoverWhileWindowAppears) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
@@ -2491,7 +1809,7 @@
// Now, an obscuring window appears!
sp<FakeWindowHandle> obscuringWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
/*createInputChannel=*/false);
obscuringWindow->setFrame(Rect(0, 0, 200, 200));
obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
@@ -2524,8 +1842,8 @@
*/
TEST_F(InputDispatcherTest, HoverMoveWhileWindowAppears) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
// Only a single window is present at first
@@ -2540,7 +1858,7 @@
// Now, an obscuring window appears!
sp<FakeWindowHandle> obscuringWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window",
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
/*createInputChannel=*/false);
obscuringWindow->setFrame(Rect(0, 0, 200, 200));
obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
@@ -2582,8 +1900,8 @@
*/
TEST_F(InputDispatcherTest, HoverMoveAndScroll) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2612,9 +1930,10 @@
* 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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2654,16 +1973,65 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
- sp<FakeWindowHandle> spyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
spyWindow->setFrame(Rect(0, 0, 200, 200));
spyWindow->setTrustedOverlay(true);
spyWindow->setSpy(true);
@@ -2712,13 +2080,77 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2763,13 +2195,68 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2816,14 +2303,69 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2868,13 +2410,62 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -2912,20 +2503,72 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
mDispatcher->onWindowInfosChanged(
@@ -2997,16 +2640,98 @@
/**
* 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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::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) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
mDispatcher->onWindowInfosChanged(
@@ -3056,21 +2781,22 @@
* 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 =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
@@ -3126,6 +2852,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",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::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.
@@ -3134,20 +2937,21 @@
* 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 =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
mDispatcher->onWindowInfosChanged(
@@ -3201,6 +3005,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",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 400, 400));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::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.
@@ -3208,10 +3090,11 @@
* 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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -3281,13 +3164,92 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -3317,6 +3279,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",
+ ui::LogicalDisplayId::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.
@@ -3330,14 +3326,15 @@
* 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);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 200, 200));
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(200, 0, 400, 200));
mDispatcher->onWindowInfosChanged(
@@ -3402,14 +3399,91 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 200, 200));
+
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -3468,6 +3542,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",
+ ui::LogicalDisplayId::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.
*
@@ -3480,12 +3609,13 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> sbtRightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher,
- "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ "Stylus blocks touch (right) window",
+ ui::LogicalDisplayId::DEFAULT);
sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
sbtRightWindow->setGlobalStylusBlocksTouch(true);
@@ -3552,12 +3682,13 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> sbtRightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher,
- "Stylus blocks touch (right) window", ADISPLAY_ID_DEFAULT);
+ "Stylus blocks touch (right) window",
+ ui::LogicalDisplayId::DEFAULT);
sbtRightWindow->setFrame(Rect(100, 100, 200, 200));
sbtRightWindow->setGlobalStylusBlocksTouch(true);
@@ -3618,13 +3749,13 @@
*/
TEST_F(InputDispatcherTest, StylusHoverAndDownNoInputChannel) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> spyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setNoInputChannel(true);
window->setFrame(Rect(0, 0, 200, 200));
@@ -3677,8 +3808,8 @@
TEST_F(InputDispatcherTest, StaleStylusHoverGestureIsComplete) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -3713,15 +3844,16 @@
* 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);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -3810,6 +3942,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",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 200, 200));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::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.
@@ -3934,14 +4174,14 @@
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowLeft =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowLeft = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
windowLeft->setFrame(Rect(0, 0, 600, 800));
- sp<FakeWindowHandle> windowRight =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowRight = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
windowRight->setFrame(Rect(600, 0, 1200, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged(
{{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
@@ -4001,7 +4241,7 @@
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
.build()));
- windowLeft->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ windowLeft->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
// Move mouse cursor back to right window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -4022,10 +4262,11 @@
* 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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 600, 800));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4076,19 +4317,78 @@
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",
+ ui::LogicalDisplayId::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>();
- sp<FakeWindowHandle> spyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
@@ -4108,19 +4408,20 @@
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 =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 600, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*spyWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
// Send mouse cursor to the window
@@ -4214,15 +4515,111 @@
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",
+ ui::LogicalDisplayId::DEFAULT);
+ spyWindow->setFrame(Rect(0, 0, 600, 800));
+ spyWindow->setTrustedOverlay(true);
+ spyWindow->setSpy(true);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
+ window->setFrame(Rect(0, 0, 600, 800));
+
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::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) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 1200, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4269,7 +4666,7 @@
.buttonState(0)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(400))
.build()));
- window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
// We already canceled the hovering implicitly by injecting the "DOWN" event without lifting the
// hover first. Therefore, injection of HOVER_EXIT is inconsistent, and should fail.
@@ -4288,11 +4685,11 @@
*/
TEST_F(InputDispatcherTest, HoverExitIsSentToRemovedWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 1200, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4316,11 +4713,11 @@
REQUIRES_FLAGS_DISABLED(ACONFIG_FLAG(com::android::input::flags,
a11y_crash_on_inconsistent_event_stream))) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 1200, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4339,10 +4736,11 @@
/**
* 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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4370,14 +4768,46 @@
}
/**
+ * 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",
+ ui::LogicalDisplayId::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);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4408,11 +4838,50 @@
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",
+ ui::LogicalDisplayId::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 =
sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
windowDefaultDisplay->setFrame(Rect(0, 0, 600, 800));
sp<FakeWindowHandle> windowSecondDisplay =
sp<FakeWindowHandle>::make(application, mDispatcher, "SecondDisplay",
@@ -4428,7 +4897,7 @@
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(300).y(600))
.build()));
windowDefaultDisplay->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER));
@@ -4447,7 +4916,7 @@
injectMotionEvent(*mDispatcher,
MotionEventBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
AINPUT_SOURCE_MOUSE)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.pointer(PointerBuilder(0, ToolType::MOUSE).x(400).y(700))
.build()));
windowDefaultDisplay->consumeMotionEvent(
@@ -4459,14 +4928,14 @@
TEST_F(InputDispatcherTest, DispatchMouseEventsUnderCursor) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowLeft =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowLeft = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
windowLeft->setFrame(Rect(0, 0, 600, 800));
- sp<FakeWindowHandle> windowRight =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowRight = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
windowRight->setFrame(Rect(600, 0, 1200, 800));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged(
{{*windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
@@ -4475,15 +4944,16 @@
// left window. This event should be dispatched to the left window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, {610, 400}, {599, 400}));
- windowLeft->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT, {610, 400}, {599, 400}));
+ windowLeft->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
windowRight->assertNoEvents();
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4491,41 +4961,44 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
// Window should receive key down event.
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
// When device reset happens, that key stream should be terminated with FLAG_CANCELED
// on the app side.
mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT, AKEY_EVENT_FLAG_CANCELED);
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT, AKEY_EVENT_FLAG_CANCELED);
}
TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
// Window should receive motion down event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// When device reset happens, that motion stream should be terminated with ACTION_CANCEL
// on the app side.
mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
}
TEST_F(InputDispatcherTest, NotifyDeviceResetCancelsHoveringStream) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4548,8 +5021,9 @@
TEST_F(InputDispatcherTest, InterceptKeyByPolicy) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4557,13 +5031,14 @@
window->consumeFocusEvent(true);
- const NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ const NotifyKeyArgs keyArgs =
+ generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
const std::chrono::milliseconds interceptKeyTimeout = 50ms;
const nsecs_t injectTime = keyArgs.eventTime;
mFakePolicy->setInterceptKeyTimeout(interceptKeyTimeout);
mDispatcher->notifyKey(keyArgs);
// The dispatching time should be always greater than or equal to intercept key timeout.
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
ASSERT_TRUE((systemTime(SYSTEM_TIME_MONOTONIC) - injectTime) >=
std::chrono::nanoseconds(interceptKeyTimeout).count());
}
@@ -4573,8 +5048,9 @@
*/
TEST_F(InputDispatcherTest, InterceptKeyIfKeyUp) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4582,15 +5058,15 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
// Set a value that's significantly larger than the default consumption timeout. If the
// implementation is correct, the actual value doesn't matter; it won't slow down the test.
mFakePolicy->setInterceptKeyTimeout(600ms);
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
// Window should receive key event immediately when same key up.
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
}
/**
@@ -4602,13 +5078,13 @@
*/
TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect{0, 0, 100, 100});
sp<FakeWindowHandle> outsideWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Outside Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
outsideWindow->setFrame(Rect{100, 100, 200, 200});
outsideWindow->setWatchOutsideTouch(true);
// outsideWindow must be above 'window' to receive ACTION_OUTSIDE events when 'window' is tapped
@@ -4616,8 +5092,8 @@
// Tap on first window.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {PointF{50, 50}}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {PointF{50, 50}}));
window->consumeMotionDown();
// The coordinates of the tap in 'outsideWindow' are relative to its top left corner.
// Therefore, we should offset them by (100, 100) relative to the screen's top left corner.
@@ -4626,12 +5102,102 @@
// Ensure outsideWindow doesn't get any more events for the gesture.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {PointF{51, 51}}));
+ ui::LogicalDisplayId::DEFAULT, {PointF{51, 51}}));
window->consumeMotionMove();
outsideWindow->assertNoEvents();
}
/**
+ * 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",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::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.
@@ -4639,32 +5205,33 @@
TEST_F(InputDispatcherTest, ActionOutsideSentOnlyWhenAWindowIsTouched) {
// There are three windows that do not overlap. `window` wants to WATCH_OUTSIDE_TOUCH.
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "First Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setWatchOutsideTouch(true);
window->setFrame(Rect{0, 0, 100, 100});
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setFrame(Rect{100, 100, 200, 200});
sp<FakeWindowHandle> thirdWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Third Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
thirdWindow->setFrame(Rect{200, 200, 300, 300});
mDispatcher->onWindowInfosChanged(
{{*window->getInfo(), *secondWindow->getInfo(), *thirdWindow->getInfo()}, {}, 0, 0});
// First pointer lands outside all windows. `window` does not get ACTION_OUTSIDE.
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {PointF{-10, -10}}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {PointF{-10, -10}}));
window->assertNoEvents();
secondWindow->assertNoEvents();
// The second pointer lands inside `secondWindow`, which should receive a DOWN event.
// Now, `window` should get ACTION_OUTSIDE.
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{PointF{-10, -10}, PointF{105, 105}}));
const std::map<int32_t, PointF> expectedPointers{{0, PointF{-10, -10}}, {1, PointF{105, 105}}};
window->consumeMotionEvent(
@@ -4675,7 +5242,8 @@
// The third pointer lands inside `thirdWindow`, which should receive a DOWN event. There is
// no ACTION_OUTSIDE sent to `window` because one has already been sent for this gesture.
mDispatcher->notifyMotion(
- generateMotionArgs(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT,
{PointF{-10, -10}, PointF{105, 105}, PointF{205, 205}}));
window->assertNoEvents();
secondWindow->consumeMotionMove();
@@ -4684,8 +5252,9 @@
TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4693,13 +5262,15 @@
window->consumeFocusEvent(true);
- const NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
- const NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ const NotifyKeyArgs keyDown =
+ generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
+ const NotifyKeyArgs keyUp =
+ generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyKey(keyDown);
mDispatcher->notifyKey(keyUp);
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
// All windows are removed from the display. Ensure that we can no longer dispatch to it.
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
@@ -4713,22 +5284,23 @@
TEST_F(InputDispatcherTest, NonSplitTouchableWindowReceivesMultiTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
// Ensure window is non-split and have some transform.
window->setPreventSplitting(true);
window->setWindowOffset(20, 40);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(-30).y(-50))
@@ -4759,12 +5331,12 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left splittable Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setPreventSplitting(false);
leftWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> rightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right non-splittable Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setPreventSplitting(true);
rightWindow->setFrame(Rect(100, 100, 200, 200));
mDispatcher->onWindowInfosChanged(
@@ -4796,13 +5368,13 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Left window (prevent splitting)",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
leftWindow->setPreventSplitting(true);
sp<FakeWindowHandle> rightWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(100, 0, 200, 100));
mDispatcher->onWindowInfosChanged(
@@ -4853,12 +5425,12 @@
TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeOnlySentToTrustedOverlays) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
sp<FakeWindowHandle> trustedOverlay =
sp<FakeWindowHandle>::make(application, mDispatcher, "Trusted Overlay",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
trustedOverlay->setSpy(true);
trustedOverlay->setTrustedOverlay(true);
@@ -4921,8 +5493,8 @@
TEST_F(InputDispatcherTest, TouchpadThreeFingerSwipeNotSentToSingleWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -4978,8 +5550,8 @@
*/
TEST_F(InputDispatcherTest, MultiplePointersWithRotatingWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 400, 400));
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -5044,13 +5616,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> spyWindowDefaultDisplay =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
spyWindowDefaultDisplay->setTrustedOverlay(true);
spyWindowDefaultDisplay->setSpy(true);
sp<FakeWindowHandle> windowDefaultDisplay =
sp<FakeWindowHandle>::make(application, mDispatcher, "DefaultDisplay",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
windowDefaultDisplay->setWindowTransform(1, 0, 0, 1);
sp<FakeWindowHandle> windowSecondDisplay = windowDefaultDisplay->clone(SECOND_DISPLAY_ID);
@@ -5064,10 +5637,10 @@
0,
0});
- // Send down to ADISPLAY_ID_DEFAULT
+ // Send down to ui::LogicalDisplayId::DEFAULT
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 100}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spyWindowDefaultDisplay->consumeMotionDown();
@@ -5080,10 +5653,10 @@
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
- // coordinates are both 100, otherwise if the cancel event is sent to windowSecondDisplay of
- // SECOND_DISPLAY_ID, the x and y coordinates are 200
+ // The cancel event is sent to windowDefaultDisplay of the ui::LogicalDisplayId::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));
EXPECT_EQ(100, event->getY(0));
}
@@ -5102,7 +5675,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;
@@ -5127,7 +5700,7 @@
// respectively.
ui::Transform displayTransform;
displayTransform.set(2, 0, 0, 4);
- addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+ addDisplayInfo(ui::LogicalDisplayId::DEFAULT, displayTransform);
std::shared_ptr<FakeApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
@@ -5135,13 +5708,13 @@
// Add two windows to the display. Their frames are represented in the display space.
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setFrame(Rect(0, 0, 100, 200), displayTransform);
addWindow(firstWindow);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setFrame(Rect(100, 200, 200, 400), displayTransform);
addWindow(secondWindow);
return {std::move(firstWindow), std::move(secondWindow)};
@@ -5158,8 +5731,8 @@
// selected so that if the hit test was performed with the point and the bounds being in
// different coordinate spaces, the event would end up in the incorrect window.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {PointF{75, 55}}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {PointF{75, 55}}));
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
@@ -5172,7 +5745,7 @@
// Send down to the first window. The point is represented in the logical display space. The
// point is selected so that if the hit test was done in logical display space, then it would
// end up in the incorrect window.
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
PointF{75 * 2, 55 * 4});
firstWindow->consumeMotionDown();
@@ -5191,7 +5764,7 @@
const vec2 untransformedPoint = injectedEventTransform.inverse().transform(expectedPoint);
MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER)
.x(untransformedPoint.x)
@@ -5210,9 +5783,9 @@
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// Send down to the second window.
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {PointF{150, 220}}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {PointF{150, 220}}));
firstWindow->assertNoEvents();
std::unique_ptr<MotionEvent> event = secondWindow->consumeMotionEvent();
@@ -5234,17 +5807,17 @@
auto [firstWindow, secondWindow] = setupScaledDisplayScenario();
// The monitor will always receive events in the logical display's coordinate space, because
// it does not have a window.
- FakeMonitorReceiver monitor{*mDispatcher, "Monitor", ADISPLAY_ID_DEFAULT};
+ FakeMonitorReceiver monitor{*mDispatcher, "Monitor", ui::LogicalDisplayId::DEFAULT};
// Send down to the first window.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ ui::LogicalDisplayId::DEFAULT, {PointF{50, 100}}));
firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
monitor.consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
// Second pointer goes down on second window.
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{PointF{50, 100}, PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 80)));
const std::map<int32_t, PointF> expectedMonitorPointers{{0, PointF{100, 400}},
@@ -5265,7 +5838,7 @@
// Send down to the first window.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {PointF{50, 100}}));
+ ui::LogicalDisplayId::DEFAULT, {PointF{50, 100}}));
firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(100, 400)));
// The pointer is transferred to the second window, and the second window receives it in the
@@ -5280,13 +5853,15 @@
// Send hover move to the second window, and ensure it shows up as hover enter.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
WithCoords(100, 80), WithRawCoords(300, 880)));
// Touch down at the same location and ensure a hover exit is synthesized.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
WithRawCoords(300, 880)));
secondWindow->consumeMotionEvent(
@@ -5311,14 +5886,16 @@
// Send hover move to the second window, and ensure it shows up as hover enter.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
WithCoords(100, 80), WithRawCoords(300, 880)));
// Touch down at the same location and ensure a hover exit is synthesized for the correct
// display.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
WithRawCoords(300, 880)));
secondWindow->consumeMotionEvent(
@@ -5332,7 +5909,8 @@
// Send hover enter to second window
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
WithCoords(100, 80), WithRawCoords(300, 880)));
@@ -5360,17 +5938,18 @@
// Send hover enter to second window
mDispatcher->notifyMotion(generateMotionArgs(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{150, 220}}));
+ ui::LogicalDisplayId::DEFAULT,
+ {PointF{150, 220}}));
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_ENTER),
WithCoords(100, 80), WithRawCoords(300, 880),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
mDispatcher->cancelCurrentTouch();
// Ensure the cancelation happens with the correct displayId and the correct coordinates.
secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_EXIT), WithCoords(100, 80),
WithRawCoords(300, 880),
- WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
secondWindow->assertNoEvents();
firstWindow->assertNoEvents();
}
@@ -5397,13 +5976,13 @@
const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
logicalDisplayWidth, logicalDisplayHeight);
- addDisplayInfo(ADISPLAY_ID_DEFAULT, displayTransform);
+ addDisplayInfo(ui::LogicalDisplayId::DEFAULT, displayTransform);
// Create a window with its bounds determined in the logical display.
const Rect frameInLogicalDisplay(100, 100, 200, 300);
const Rect frameInDisplay = displayTransform.inverse().transform(frameInLogicalDisplay);
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(frameInDisplay, displayTransform);
addWindow(window);
@@ -5413,14 +5992,14 @@
for (const auto pointInsideWindow : insidePoints) {
const vec2 p = displayTransform.inverse().transform(pointInsideWindow);
const PointF pointInDisplaySpace{p.x, p.y};
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
window->consumeMotionDown();
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
window->consumeMotionUp();
}
@@ -5430,17 +6009,105 @@
for (const auto pointOutsideWindow : outsidePoints) {
const vec2 p = displayTransform.inverse().transform(pointOutsideWindow);
const PointF pointInDisplaySpace{p.x, p.y};
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
}
window->assertNoEvents();
}
+// This test verifies the occlusion detection for all rotations of the display by tapping
+// in different locations on the display, specifically points close to the four corners of a
+// window.
+TEST_P(InputDispatcherDisplayOrientationFixture, BlockUntrustClickInDifferentOrientations) {
+ constexpr static int32_t displayWidth = 400;
+ constexpr static int32_t displayHeight = 800;
+
+ std::shared_ptr<FakeApplicationHandle> untrustedWindowApplication =
+ std::make_shared<FakeApplicationHandle>();
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+
+ const auto rotation = GetParam();
+
+ // Set up the display with the specified rotation.
+ const bool isRotated = rotation == ui::ROTATION_90 || rotation == ui::ROTATION_270;
+ const int32_t logicalDisplayWidth = isRotated ? displayHeight : displayWidth;
+ const int32_t logicalDisplayHeight = isRotated ? displayWidth : displayHeight;
+ const ui::Transform displayTransform(ui::Transform::toRotationFlags(rotation),
+ logicalDisplayWidth, logicalDisplayHeight);
+ addDisplayInfo(ui::LogicalDisplayId::DEFAULT, displayTransform);
+
+ // Create a window that not trusted.
+ const Rect untrustedWindowFrameInLogicalDisplay(100, 100, 200, 300);
+
+ const Rect untrustedWindowFrameInDisplay =
+ displayTransform.inverse().transform(untrustedWindowFrameInLogicalDisplay);
+
+ sp<FakeWindowHandle> untrustedWindow =
+ sp<FakeWindowHandle>::make(untrustedWindowApplication, mDispatcher, "UntrustedWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ untrustedWindow->setFrame(untrustedWindowFrameInDisplay, displayTransform);
+ untrustedWindow->setTrustedOverlay(false);
+ untrustedWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ untrustedWindow->setTouchable(false);
+ untrustedWindow->setAlpha(1.0f);
+ untrustedWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
+ addWindow(untrustedWindow);
+
+ // Create a simple app window below the untrusted window.
+ const Rect simpleAppWindowFrameInLogicalDisplay(0, 0, 300, 600);
+ const Rect simpleAppWindowFrameInDisplay =
+ displayTransform.inverse().transform(simpleAppWindowFrameInLogicalDisplay);
+
+ sp<FakeWindowHandle> simpleAppWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "SimpleAppWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ simpleAppWindow->setFrame(simpleAppWindowFrameInDisplay, displayTransform);
+ simpleAppWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202});
+ addWindow(simpleAppWindow);
+
+ // The following points in logical display space should be inside the untrusted window, so
+ // the simple window could not receive events that coordinate is these point.
+ static const std::array<vec2, 4> untrustedPoints{
+ {{100, 100}, {199.99, 100}, {100, 299.99}, {199.99, 299.99}}};
+
+ for (const auto untrustedPoint : untrustedPoints) {
+ const vec2 p = displayTransform.inverse().transform(untrustedPoint);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
+ }
+ untrustedWindow->assertNoEvents();
+ simpleAppWindow->assertNoEvents();
+ // The following points in logical display space should be outside the untrusted window, so
+ // the simple window should receive events that coordinate is these point.
+ static const std::array<vec2, 5> trustedPoints{
+ {{200, 100}, {100, 300}, {200, 300}, {100, 99.99}, {99.99, 100}}};
+ for (const auto trustedPoint : trustedPoints) {
+ const vec2 p = displayTransform.inverse().transform(trustedPoint);
+ const PointF pointInDisplaySpace{p.x, p.y};
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
+ simpleAppWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInDisplaySpace}));
+ simpleAppWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ }
+ untrustedWindow->assertNoEvents();
+}
+
// Run the precision tests for all rotations.
INSTANTIATE_TEST_SUITE_P(InputDispatcherDisplayOrientationTests,
InputDispatcherDisplayOrientationFixture,
@@ -5462,13 +6129,14 @@
// Create a couple of windows
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> wallpaper =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper",
+ ui::LogicalDisplayId::DEFAULT);
wallpaper->setIsWallpaper(true);
// Add the windows to the dispatcher, and ensure the first window is focused
mDispatcher->onWindowInfosChanged(
@@ -5478,12 +6146,13 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// Dispatcher reports pointer down outside focus for the wallpaper
mFakePolicy->assertOnPointerDownEquals(wallpaper->getToken());
@@ -5493,17 +6162,19 @@
ASSERT_TRUE(success);
// 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);
+ secondWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ wallpaper->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
// There should not be any changes to the focused window when transferring touch
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertOnPointerDownWasNotCalled());
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets no events and the second gets up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
wallpaper->assertNoEvents();
}
@@ -5523,14 +6194,15 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
// Create a couple of windows + a spy window
- sp<FakeWindowHandle> spyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spyWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
spyWindow->setTrustedOverlay(true);
spyWindow->setSpy(true);
- sp<FakeWindowHandle> firstWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "First", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> firstWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "First",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> secondWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
// Add the windows to the dispatcher
mDispatcher->onWindowInfosChanged(
@@ -5538,7 +6210,8 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
// Only the first window and spy should get the down event
spyWindow->consumeMotionDown();
firstWindow->consumeMotionDown();
@@ -5550,15 +6223,17 @@
ASSERT_TRUE(success);
// The first window gets cancel and the second gets down
firstWindow->consumeMotionCancel();
- secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets no events and the second+spy get up
firstWindow->assertNoEvents();
spyWindow->consumeMotionUp();
- secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
@@ -5569,11 +6244,11 @@
// Create a couple of windows
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setPreventSplitting(true);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setPreventSplitting(true);
// Add the windows to the dispatcher
@@ -5582,15 +6257,16 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {touchPoint}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {touchPoint}));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send pointer down to the first window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}));
+ ui::LogicalDisplayId::DEFAULT,
+ {touchPoint, touchPoint}));
// Only the first window should get the pointer down event
firstWindow->consumeMotionPointerDown(1);
secondWindow->assertNoEvents();
@@ -5601,24 +6277,29 @@
ASSERT_TRUE(success);
// The first window gets cancel and the second gets down and pointer down
firstWindow->consumeMotionCancel();
- secondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
- secondWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT,
+ secondWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
// Send pointer up to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {touchPoint, touchPoint}));
+ ui::LogicalDisplayId::DEFAULT,
+ {touchPoint, touchPoint}));
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
+ WithPointerCount(2)));
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets nothing and the second gets up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) {
@@ -5627,19 +6308,21 @@
// Create a couple of windows
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setDupTouchToWallpaper(true);
sp<FakeWindowHandle> wallpaper1 =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1",
+ ui::LogicalDisplayId::DEFAULT);
wallpaper1->setIsWallpaper(true);
sp<FakeWindowHandle> wallpaper2 =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2",
+ ui::LogicalDisplayId::DEFAULT);
wallpaper2->setIsWallpaper(true);
// Add the windows to the dispatcher
mDispatcher->onWindowInfosChanged({{*firstWindow->getInfo(), *wallpaper1->getInfo(),
@@ -5650,12 +6333,13 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
- wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+ wallpaper1->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
wallpaper2->assertNoEvents();
// Transfer touch focus to the second window
@@ -5665,20 +6349,22 @@
// 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);
- wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ wallpaper1->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT, EXPECTED_WALLPAPER_FLAGS);
+ wallpaper2->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ 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,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets no events and the second gets up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
wallpaper1->assertNoEvents();
- wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT,
- expectedWallpaperFlags | AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ wallpaper2->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ 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
@@ -5690,7 +6376,7 @@
[&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> /*ignored*/,
sp<IBinder> destChannelToken) {
return dispatcher->transferTouchOnDisplay(destChannelToken,
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
},
[&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from,
sp<IBinder> to) {
@@ -5703,12 +6389,12 @@
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
@@ -5720,15 +6406,15 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInFirst}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInFirst}));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send down to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{pointInFirst, pointInSecond}));
// The first window gets a move and the second a down
firstWindow->consumeMotionMove();
@@ -5738,24 +6424,27 @@
mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken());
// The first window gets cancel and the new gets pointer down (it already saw down)
firstWindow->consumeMotionCancel();
- secondWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT,
+ secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
// Send pointer up to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{pointInFirst, pointInSecond}));
// The first window gets nothing and the second gets pointer up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT,
- AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE),
+ WithPointerCount(2)));
// Send up event to the second window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets nothing and the second gets up
firstWindow->assertNoEvents();
- secondWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
// Same as TransferTouch_TwoPointersSplitTouch, but using 'transferTouchOnDisplay' api.
@@ -5767,12 +6456,12 @@
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
@@ -5784,23 +6473,23 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInFirst}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInFirst}));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send down to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{pointInFirst, pointInSecond}));
// The first window gets a move and the second a down
firstWindow->consumeMotionMove();
secondWindow->consumeMotionDown();
// Transfer touch focus to the second window
- const bool transferred =
- mDispatcher->transferTouchOnDisplay(secondWindow->getToken(), ADISPLAY_ID_DEFAULT);
+ const bool transferred = mDispatcher->transferTouchOnDisplay(secondWindow->getToken(),
+ ui::LogicalDisplayId::DEFAULT);
// The 'transferTouchOnDisplay' call should not succeed, because there are 2 touched windows
ASSERT_FALSE(transferred);
firstWindow->assertNoEvents();
@@ -5809,7 +6498,7 @@
// The rest of the dispatch should proceed as normal
// Send pointer up to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{pointInFirst, pointInSecond}));
// The first window gets MOVE and the second gets pointer up
firstWindow->consumeMotionMove();
@@ -5817,7 +6506,7 @@
// Send up event to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets nothing and the second gets up
firstWindow->consumeMotionUp();
secondWindow->assertNoEvents();
@@ -5829,13 +6518,16 @@
TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> firstWindowInPrimary =
- sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1",
+ ui::LogicalDisplayId::DEFAULT);
firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> secondWindowInPrimary =
- sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2",
+ ui::LogicalDisplayId::DEFAULT);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> mirrorWindowInPrimary = firstWindowInPrimary->clone(ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> mirrorWindowInPrimary =
+ firstWindowInPrimary->clone(ui::LogicalDisplayId::DEFAULT);
mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
sp<FakeWindowHandle> firstWindowInSecondary = firstWindowInPrimary->clone(SECOND_DISPLAY_ID);
@@ -5854,35 +6546,36 @@
0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Window should receive motion event.
- firstWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ firstWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// Transfer touch
ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(),
secondWindowInPrimary->getToken()));
// The first window gets cancel.
firstWindowInPrimary->consumeMotionCancel();
- secondWindowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT,
+ secondWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {150, 50}))
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
- secondWindowInPrimary->consumeMotionMove(ADISPLAY_ID_DEFAULT,
+ secondWindowInPrimary->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
firstWindowInPrimary->assertNoEvents();
- secondWindowInPrimary->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ secondWindowInPrimary->consumeMotionUp(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
// Same as TransferTouch_CloneSurface, but this touch on the secondary display and use
@@ -5890,13 +6583,16 @@
TEST_F(InputDispatcherTest, TransferTouchOnDisplay_CloneSurface) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> firstWindowInPrimary =
- sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W1",
+ ui::LogicalDisplayId::DEFAULT);
firstWindowInPrimary->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> secondWindowInPrimary =
- sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "D_1_W2",
+ ui::LogicalDisplayId::DEFAULT);
secondWindowInPrimary->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> mirrorWindowInPrimary = firstWindowInPrimary->clone(ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> mirrorWindowInPrimary =
+ firstWindowInPrimary->clone(ui::LogicalDisplayId::DEFAULT);
mirrorWindowInPrimary->setFrame(Rect(0, 100, 100, 200));
sp<FakeWindowHandle> firstWindowInSecondary = firstWindowInPrimary->clone(SECOND_DISPLAY_ID);
@@ -5949,8 +6645,9 @@
TEST_F(InputDispatcherTest, FocusedWindow_ReceivesFocusEventAndKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -5958,10 +6655,10 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
// Window should receive key down event.
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
// Should have poked user activity
mDispatcher->waitForIdle();
@@ -5970,8 +6667,9 @@
TEST_F(InputDispatcherTest, FocusedWindow_DisableUserActivity) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setDisableUserActivity(true);
window->setFocusable(true);
@@ -5980,20 +6678,22 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
// Window should receive key down event.
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
- // Should have poked user activity
+ // Should have not poked user activity
mDispatcher->waitForIdle();
mFakePolicy->assertUserActivityNotPoked();
}
-TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) {
+TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyConsumedKey) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -6001,41 +6701,24 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mFakePolicy->setConsumeKeyBeforeDispatching(true);
+
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
mDispatcher->waitForIdle();
- // System key is not passed down
+ // Key is not passed down
window->assertNoEvents();
// Should have poked user activity
mFakePolicy->assertUserActivityPoked();
}
-TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) {
+TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserActivity) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
-
- window->setFocusable(true);
- mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- setFocusedWindow(window);
-
- window->consumeFocusEvent(true);
-
- mDispatcher->notifyKey(generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
- mDispatcher->waitForIdle();
-
- // System key is not passed down
- window->assertNoEvents();
-
- // Should have poked user activity
- mFakePolicy->assertUserActivityPoked();
-}
-
-TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setDisableUserActivity(true);
window->setFocusable(true);
@@ -6044,7 +6727,10 @@
window->consumeFocusEvent(true);
- mDispatcher->notifyKey(generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mFakePolicy->setConsumeKeyBeforeDispatching(true);
+
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
mDispatcher->waitForIdle();
// System key is not passed down
@@ -6054,20 +6740,54 @@
mFakePolicy->assertUserActivityPoked();
}
+class DisableUserActivityInputDispatcherTest : public InputDispatcherTest,
+ public ::testing::WithParamInterface<bool> {};
+
+TEST_P(DisableUserActivityInputDispatcherTest, NotPassedToUserUserActivity) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
+
+ window->setDisableUserActivity(GetParam());
+
+ window->setFocusable(true);
+ mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
+ setFocusedWindow(window);
+
+ window->consumeFocusEvent(true);
+
+ mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .keyCode(AKEYCODE_A)
+ .policyFlags(0)
+ .build());
+ mDispatcher->waitForIdle();
+
+ // Key is not passed down
+ window->assertNoEvents();
+
+ // Should not have poked user activity
+ mFakePolicy->assertUserActivityNotPoked();
+}
+
+INSTANTIATE_TEST_CASE_P(DisableUserActivity, DisableUserActivityInputDispatcherTest,
+ ::testing::Bool());
+
TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {100, 100}))
+ ui::LogicalDisplayId::DEFAULT, {100, 100}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
// Should have poked user activity
mDispatcher->waitForIdle();
@@ -6076,12 +6796,13 @@
TEST_F(InputDispatcherTest, UnfocusedWindow_DoesNotReceiveFocusEventOrKeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
mDispatcher->waitForIdle();
window->assertNoEvents();
@@ -6090,19 +6811,21 @@
// If a window is touchable, but does not have focus, it should receive motion events, but not keys
TEST_F(InputDispatcherTest, UnfocusedWindow_ReceivesMotionsButNotKeys) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
// Send key
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
// Send motion
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
// Window should receive only the motion event
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents(); // Key event or focus event will not be received
}
@@ -6111,12 +6834,12 @@
sp<FakeWindowHandle> firstWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "First Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
firstWindow->setFrame(Rect(0, 0, 600, 400));
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
secondWindow->setFrame(Rect(0, 400, 600, 800));
// Add the windows to the dispatcher
@@ -6128,15 +6851,15 @@
// Send down to the first window
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInFirst}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInFirst}));
// Only the first window should get the down event
firstWindow->consumeMotionDown();
secondWindow->assertNoEvents();
// Send down to the second window
mDispatcher->notifyMotion(generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT,
+ ui::LogicalDisplayId::DEFAULT,
{pointInFirst, pointInSecond}));
// The first window gets a move and the second a down
firstWindow->consumeMotionMove();
@@ -6144,17 +6867,17 @@
// Send pointer cancel to the second window
NotifyMotionArgs pointerUpMotionArgs =
- generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {pointInFirst, pointInSecond});
+ generateMotionArgs(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {pointInFirst, pointInSecond});
pointerUpMotionArgs.flags |= AMOTION_EVENT_FLAG_CANCELED;
mDispatcher->notifyMotion(pointerUpMotionArgs);
// The first window gets move and the second gets cancel.
- firstWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
- secondWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ firstWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ secondWindow->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
// Send up event.
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
// The first window gets up and the second gets nothing.
firstWindow->consumeMotionUp();
secondWindow->assertNoEvents();
@@ -6163,8 +6886,8 @@
TEST_F(InputDispatcherTest, SendTimeline_DoesNotCrashDispatcher) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
@@ -6187,28 +6910,29 @@
*/
TEST_F(InputDispatcherMonitorTest, MonitorTouchIsCanceledWhenForegroundWindowDisappears) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
// Both the foreground window and the global monitor should receive the touch down
window->consumeMotionDown();
- monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 200}))
+ ui::LogicalDisplayId::DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionMove();
- monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
// Now the foreground window goes away
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
@@ -6219,41 +6943,47 @@
// cause a cancel for the monitor, as well.
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {120, 200}))
+ ui::LogicalDisplayId::DEFAULT, {120, 200}))
<< "Injection should fail because the window was removed";
window->assertNoEvents();
// Global monitor now gets the cancel
- monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionCancel(ui::LogicalDisplayId::DEFAULT);
}
TEST_F(InputDispatcherMonitorTest, ReceivesMotionEvents) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ monitor.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
TEST_F(InputDispatcherMonitorTest, MonitorCannotPilferPointers) {
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// Pilfer pointers from the monitor.
// This should not do anything and the window should continue to receive events.
@@ -6261,27 +6991,30 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT))
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
- window->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
+ window->consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
}
TEST_F(InputDispatcherMonitorTest, NoWindowTransform) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
window->setWindowOffset(20, 40);
window->setWindowTransform(0, 1, -1, 0);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
std::unique_ptr<MotionEvent> event = monitor.consumeMotion();
ASSERT_NE(nullptr, event);
// Even though window has transform, gesture monitor must not.
@@ -6290,10 +7023,12 @@
TEST_F(InputDispatcherMonitorTest, InjectionFailsWithNoWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Injection should fail if there is a monitor, but no touchable window";
monitor.assertNoEvents();
}
@@ -6311,20 +7046,21 @@
*/
TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCanceledWhenAnotherEmptyDisplayReceiveEvents) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "The down event injected into the first display should succeed";
window->consumeMotionDown();
- monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
@@ -6335,19 +7071,19 @@
// Continue to inject event to first display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 220}))
+ ui::LogicalDisplayId::DEFAULT, {110, 220}))
<< "The move event injected into the first display should succeed";
window->consumeMotionMove();
- monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{110, 220}))
<< "The up event injected into the first display should succeed";
window->consumeMotionUp();
- monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents();
monitor.assertNoEvents();
@@ -6371,24 +7107,25 @@
*/
TEST_F(InputDispatcherMonitorTest, MonitorTouchIsNotCancelWhenAnotherDisplayMonitorTouchCanceled) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> secondWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "SecondForeground",
SECOND_DISPLAY_ID);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
FakeMonitorReceiver secondMonitor = FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// There is a foreground window on both displays.
mDispatcher->onWindowInfosChanged({{*window->getInfo(), *secondWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "The down event injected into the first display should succeed";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ monitor.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
@@ -6416,11 +7153,11 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 200}))
+ ui::LogicalDisplayId::DEFAULT, {110, 200}))
<< "The move event injected into the first display should succeed";
window->consumeMotionMove();
- monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID,
@@ -6429,12 +7166,12 @@
"touchable window";
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{110, 220}))
<< "The up event injected into the first display should succeed";
- window->consumeMotionUp(ADISPLAY_ID_DEFAULT);
- monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
+ monitor.consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents();
monitor.assertNoEvents();
@@ -6452,22 +7189,23 @@
*/
TEST_F(InputDispatcherMonitorTest, MonitorTouchCancelEventWithDisplayTransform) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Foreground",
+ ui::LogicalDisplayId::DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
ui::Transform transform;
transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
gui::DisplayInfo displayInfo;
- displayInfo.displayId = ADISPLAY_ID_DEFAULT;
+ displayInfo.displayId = ui::LogicalDisplayId::DEFAULT;
displayInfo.transform = transform;
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "The down event injected should succeed";
window->consumeMotionDown();
@@ -6477,7 +7215,7 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 220}))
+ ui::LogicalDisplayId::DEFAULT, {110, 220}))
<< "The move event injected should succeed";
window->consumeMotionMove();
@@ -6493,19 +7231,19 @@
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 220}))
+ ui::LogicalDisplayId::DEFAULT, {110, 220}))
<< "The move event injected should failed";
// Now foreground should not receive any events, but monitor should receive a cancel event
// with transform that same as display's display.
std::unique_ptr<MotionEvent> cancelMotionEvent = monitor.consumeMotion();
EXPECT_EQ(transform, cancelMotionEvent->getTransform());
- EXPECT_EQ(ADISPLAY_ID_DEFAULT, cancelMotionEvent->getDisplayId());
+ EXPECT_EQ(ui::LogicalDisplayId::DEFAULT, cancelMotionEvent->getDisplayId());
EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, cancelMotionEvent->getAction());
// Other event inject to this display should fail.
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 220}))
+ ui::LogicalDisplayId::DEFAULT, {110, 220}))
<< "The up event injected should fail because the touched window was removed";
window->assertNoEvents();
monitor.assertNoEvents();
@@ -6513,18 +7251,19 @@
TEST_F(InputDispatcherTest, TestMoveEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Fake Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyMotion(motionArgs);
// Window should receive motion down event.
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
motionArgs.id += 1;
@@ -6533,7 +7272,7 @@
motionArgs.pointerCoords[0].getX() - 10);
mDispatcher->notifyMotion(motionArgs);
- window->consumeMotionMove(ADISPLAY_ID_DEFAULT, /*expectedFlags=*/0);
+ window->consumeMotionMove(ui::LogicalDisplayId::DEFAULT, /*expectedFlags=*/0);
}
/**
@@ -6543,12 +7282,13 @@
*/
TEST_F(InputDispatcherTest, TouchModeState_IsSentToApps) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
const WindowInfo& windowInfo = *window->getInfo();
// Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
SCOPED_TRACE("Check default value of touch mode");
@@ -6563,7 +7303,7 @@
SCOPED_TRACE("Disable touch mode");
mDispatcher->setInTouchMode(false, windowInfo.ownerPid, windowInfo.ownerUid,
- /*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
+ /*hasPermission=*/true, ui::LogicalDisplayId::DEFAULT);
window->consumeTouchModeEvent(false);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -6577,7 +7317,7 @@
SCOPED_TRACE("Enable touch mode again");
mDispatcher->setInTouchMode(true, windowInfo.ownerPid, windowInfo.ownerUid,
- /*hasPermission=*/true, ADISPLAY_ID_DEFAULT);
+ /*hasPermission=*/true, ui::LogicalDisplayId::DEFAULT);
window->consumeTouchModeEvent(true);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -6589,10 +7329,11 @@
TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
@@ -6627,23 +7368,24 @@
TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
ui::Transform transform;
transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1});
gui::DisplayInfo displayInfo;
- displayInfo.displayId = ADISPLAY_ID_DEFAULT;
+ displayInfo.displayId = ui::LogicalDisplayId::DEFAULT;
displayInfo.transform = transform;
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {displayInfo}, 0, 0});
const NotifyMotionArgs motionArgs =
generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyMotion(motionArgs);
std::unique_ptr<MotionEvent> event = window->consumeMotionEvent();
@@ -6703,7 +7445,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;
@@ -6730,11 +7472,12 @@
TEST_F(InputDispatcherTest, SetFocusedWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowTop = sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> windowSecond =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
// Top window is also focusable but is not granted focus.
windowTop->setFocusable(true);
@@ -6748,15 +7491,15 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ui::LogicalDisplayId::INVALID);
windowTop->assertNoEvents();
}
TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestInvalidChannel) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
// Release channel for window is no longer valid.
@@ -6773,10 +7516,10 @@
TEST_F(InputDispatcherTest, SetFocusedWindow_DropRequestNoFocusableWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(false);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
@@ -6790,11 +7533,12 @@
TEST_F(InputDispatcherTest, SetFocusedWindow_CheckFocusedToken) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowTop = sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> windowSecond =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
windowTop->setFocusable(true);
windowSecond->setFocusable(true);
@@ -6813,16 +7557,17 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Focused window should receive event.
- windowSecond->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowSecond->consumeKeyDown(ui::LogicalDisplayId::INVALID);
}
TEST_F(InputDispatcherTest, SetFocusedWindow_TransferFocusTokenNotFocusable) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> windowTop =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> windowTop = sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> windowSecond =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
windowTop->setFocusable(true);
windowSecond->setFocusable(false);
@@ -6836,18 +7581,18 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
// Event should be dropped.
- windowTop->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowTop->consumeKeyDown(ui::LogicalDisplayId::INVALID);
windowSecond->assertNoEvents();
}
TEST_F(InputDispatcherTest, SetFocusedWindow_DeferInvisibleWindow) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
sp<FakeWindowHandle> previousFocusedWindow =
sp<FakeWindowHandle>::make(application, mDispatcher, "previousFocusedWindow",
- ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
previousFocusedWindow->setFocusable(true);
@@ -6864,7 +7609,7 @@
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
- ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::NONE));
// Window does not get focus event or key down.
window->assertNoEvents();
@@ -6876,14 +7621,14 @@
// Window receives focus event.
window->consumeFocusEvent(true);
// Focused window receives key down.
- window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
}
TEST_F(InputDispatcherTest, DisplayRemoved) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "window", ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "window",
+ ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
// window is granted focus.
window->setFocusable(true);
@@ -6892,7 +7637,7 @@
window->consumeFocusEvent(true);
// When a display is removed window loses focus.
- mDispatcher->displayRemoved(ADISPLAY_ID_DEFAULT);
+ mDispatcher->displayRemoved(ui::LogicalDisplayId::DEFAULT);
window->consumeFocusEvent(false);
}
@@ -6924,10 +7669,11 @@
constexpr gui::Uid SLIPPERY_UID{WINDOW_UID.val() + 1};
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
sp<FakeWindowHandle> slipperyExitWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
slipperyExitWindow->setSlippery(true);
// Make sure this one overlaps the bottom window
slipperyExitWindow->setFrame(Rect(25, 25, 75, 75));
@@ -6936,7 +7682,8 @@
slipperyExitWindow->setOwnerInfo(SLIPPERY_PID, SLIPPERY_UID);
sp<FakeWindowHandle> slipperyEnterWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
slipperyExitWindow->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged(
@@ -6944,20 +7691,20 @@
// Use notifyMotion instead of injecting to avoid dealing with injection permissions
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {{50, 50}}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {{50, 50}}));
slipperyExitWindow->consumeMotionDown();
slipperyExitWindow->setFrame(Rect(70, 70, 100, 100));
mDispatcher->onWindowInfosChanged(
{{*slipperyExitWindow->getInfo(), *slipperyEnterWindow->getInfo()}, {}, 0, 0});
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_MOVE,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {{51, 51}}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {{51, 51}}));
slipperyExitWindow->consumeMotionCancel();
- slipperyEnterWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT,
+ slipperyEnterWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
}
@@ -6972,12 +7719,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> leftSlipperyWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftSlipperyWindow->setSlippery(true);
leftSlipperyWindow->setFrame(Rect(0, 0, 100, 100));
sp<FakeWindowHandle> rightDropTouchesWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightDropTouchesWindow->setFrame(Rect(100, 0, 200, 100));
rightDropTouchesWindow->setDropInput(true);
@@ -7008,12 +7757,14 @@
TEST_F(InputDispatcherTest, InjectedTouchSlips) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> originalWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Original", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Original",
+ ui::LogicalDisplayId::DEFAULT);
originalWindow->setFrame(Rect(0, 0, 200, 200));
originalWindow->setSlippery(true);
sp<FakeWindowHandle> appearingWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Appearing", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Appearing",
+ ui::LogicalDisplayId::DEFAULT);
appearingWindow->setFrame(Rect(0, 0, 200, 200));
mDispatcher->onWindowInfosChanged({{*originalWindow->getInfo()}, {}, 0, 0});
@@ -7048,24 +7799,175 @@
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",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ui::LogicalDisplayId::DEFAULT);
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window",
+ ui::LogicalDisplayId::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) {
+ 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 window",
+ ui::LogicalDisplayId::DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> middleWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "middle window",
+ ui::LogicalDisplayId::DEFAULT);
+ middleWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ui::LogicalDisplayId::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>();
- sp<FakeWindowHandle> leftWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> leftWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Left",
+ ui::LogicalDisplayId::DEFAULT);
leftWindow->setFrame(Rect(0, 0, 100, 100));
leftWindow->setOwnerInfo(gui::Pid{1}, Uid{101});
sp<FakeWindowHandle> rightSpy =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right spy",
+ ui::LogicalDisplayId::DEFAULT);
rightSpy->setFrame(Rect(100, 0, 200, 100));
rightSpy->setOwnerInfo(gui::Pid{2}, Uid{102});
rightSpy->setSpy(true);
rightSpy->setTrustedOverlay(true);
- sp<FakeWindowHandle> rightWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> rightWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Right",
+ ui::LogicalDisplayId::DEFAULT);
rightWindow->setFrame(Rect(100, 0, 200, 100));
rightWindow->setOwnerInfo(gui::Pid{3}, Uid{103});
@@ -7130,8 +8032,8 @@
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
window->setFrame(Rect(0, 0, 100, 100));
window->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
@@ -7140,14 +8042,14 @@
ASSERT_NO_FATAL_FAILURE(window->consumeFocusEvent(true));
mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build());
- ASSERT_NO_FATAL_FAILURE(window->consumeKeyDown(ADISPLAY_ID_DEFAULT));
+ ASSERT_NO_FATAL_FAILURE(window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT));
mDispatcher->waitForIdle();
ASSERT_NO_FATAL_FAILURE(
mFakePolicy->assertNotifyDeviceInteractionWasCalled(DEVICE_ID, {gui::Uid{101}}));
// The UP actions are not treated as device interaction.
mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).build());
- ASSERT_NO_FATAL_FAILURE(window->consumeKeyUp(ADISPLAY_ID_DEFAULT));
+ ASSERT_NO_FATAL_FAILURE(window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT));
mDispatcher->waitForIdle();
ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled());
}
@@ -7156,13 +8058,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
left->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Right Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> right =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
+ ui::LogicalDisplayId::DEFAULT);
right->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> spy =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window",
+ ui::LogicalDisplayId::DEFAULT);
spy->setFrame(Rect(0, 0, 200, 100));
spy->setTrustedOverlay(true);
spy->setSpy(true);
@@ -7171,8 +8074,9 @@
{{*spy->getInfo(), *left->getInfo(), *right->getInfo()}, {}, 0, 0});
// Send hover move to the left window, and ensure hover enter is synthesized with a new eventId.
- NotifyMotionArgs notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{50, 50}});
+ NotifyMotionArgs notifyArgs =
+ generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ui::LogicalDisplayId::DEFAULT, {PointF{50, 50}});
mDispatcher->notifyMotion(notifyArgs);
std::unique_ptr<MotionEvent> leftEnter = left->consumeMotionEvent(
@@ -7185,8 +8089,8 @@
WithEventIdSource(IdGenerator::Source::INPUT_DISPATCHER)));
// Send move to the right window, and ensure hover exit and enter are synthesized with new ids.
- notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
- {PointF{150, 50}});
+ notifyArgs = generateMotionArgs(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS,
+ ui::LogicalDisplayId::DEFAULT, {PointF{150, 50}});
mDispatcher->notifyMotion(notifyArgs);
std::unique_ptr<MotionEvent> leftExit = left->consumeMotionEvent(
@@ -7201,6 +8105,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",
+ ui::LogicalDisplayId::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",
+ ui::LogicalDisplayId::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;
@@ -7211,7 +8169,8 @@
mApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window",
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
@@ -7523,7 +8482,8 @@
void setUpWindow() {
mApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Fake Window",
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
@@ -7532,13 +8492,14 @@
}
void sendAndConsumeKeyDown(int32_t deviceId) {
- NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ NotifyKeyArgs keyArgs =
+ generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT);
keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Otherwise it won't generate repeat event
mDispatcher->notifyKey(keyArgs);
// Window should receive key down event.
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
}
void expectKeyRepeatOnce(int32_t repeatCount) {
@@ -7548,15 +8509,23 @@
}
void sendAndConsumeKeyUp(int32_t deviceId) {
- NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT);
+ NotifyKeyArgs keyArgs =
+ generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT);
keyArgs.deviceId = deviceId;
keyArgs.policyFlags |= POLICY_FLAG_TRUSTED; // Unless it won't generate repeat event
mDispatcher->notifyKey(keyArgs);
// Window should receive key down event.
- mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT,
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT,
/*expectedFlags=*/0);
}
+
+ void injectKeyRepeat(int32_t repeatCount) {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, repeatCount,
+ ui::LogicalDisplayId::DEFAULT))
+ << "Inject key event should return InputEventInjectionResult::SUCCEEDED";
+ }
};
TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_ReceivesKeyRepeat) {
@@ -7615,7 +8584,7 @@
sendAndConsumeKeyDown(DEVICE_ID);
expectKeyRepeatOnce(/*repeatCount=*/1);
mDispatcher->notifyDeviceReset({/*id=*/10, /*eventTime=*/20, DEVICE_ID});
- mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT,
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT,
AKEY_EVENT_FLAG_CANCELED | AKEY_EVENT_FLAG_LONG_PRESS);
mWindow->assertNoEvents();
}
@@ -7645,6 +8614,17 @@
}
}
+TEST_F(InputDispatcherKeyRepeatTest, FocusedWindow_CorrectRepeatCountWhenInjectKeyRepeat) {
+ injectKeyRepeat(0);
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
+ for (int32_t repeatCount = 1; repeatCount <= 2; ++repeatCount) {
+ expectKeyRepeatOnce(repeatCount);
+ }
+ injectKeyRepeat(1);
+ // Expect repeatCount to be 3 instead of 1
+ expectKeyRepeatOnce(3);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
@@ -7652,11 +8632,11 @@
InputDispatcherTest::SetUp();
application1 = std::make_shared<FakeApplicationHandle>();
- windowInPrimary =
- sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1", ADISPLAY_ID_DEFAULT);
+ windowInPrimary = sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1",
+ ui::LogicalDisplayId::DEFAULT);
// Set focus window for primary display, but focused display would be second one.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application1);
windowInPrimary->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*windowInPrimary->getInfo()}, {}, 0, 0});
@@ -7669,6 +8649,8 @@
// Set focus to second display window.
// Set focus display to second one.
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
+
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocusable(true);
@@ -7697,9 +8679,10 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayTouch) {
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
windowInSecondary->assertNoEvents();
// Test touch down on second display.
@@ -7713,22 +8696,22 @@
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, SetInputWindow_MultiDisplayFocus) {
// Test inject a key down with display id specified.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ui::LogicalDisplayId::DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- windowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
windowInSecondary->assertNoEvents();
// Test inject a key down without display id specified.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDownNoRepeat(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
- windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ui::LogicalDisplayId::INVALID);
// 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::LogicalDisplayId::INVALID, AKEY_EVENT_FLAG_CANCELED);
// Test inject a key down, should timeout because of no target window.
ASSERT_NO_FATAL_FAILURE(assertInjectedKeyTimesOut(*mDispatcher));
@@ -7740,16 +8723,17 @@
// Test per-display input monitors for motion event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
FakeMonitorReceiver monitorInSecondary =
FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ monitorInPrimary.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
windowInSecondary->assertNoEvents();
monitorInSecondary.assertNoEvents();
@@ -7773,19 +8757,20 @@
// 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::LogicalDisplayId::INVALID))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
- windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
- monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeMotionDown(ui::LogicalDisplayId::INVALID);
+ monitorInSecondary.consumeMotionDown(ui::LogicalDisplayId::INVALID);
}
// Test per-display input monitors for key event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
// Input monitor per display.
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
FakeMonitorReceiver monitorInSecondary =
FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
@@ -7794,13 +8779,14 @@
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
- windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
+ windowInSecondary->consumeKeyDown(ui::LogicalDisplayId::INVALID);
+ monitorInSecondary.consumeKeyDown(ui::LogicalDisplayId::INVALID);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CanFocusWindowOnUnfocusedDisplay) {
sp<FakeWindowHandle> secondWindowInPrimary =
- sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1_W2", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(application1, mDispatcher, "D_1_W2",
+ ui::LogicalDisplayId::DEFAULT);
secondWindowInPrimary->setFocusable(true);
mDispatcher->onWindowInfosChanged(
{{*windowInPrimary->getInfo(), *secondWindowInPrimary->getInfo(),
@@ -7814,25 +8800,26 @@
// Test inject a key down.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDown(*mDispatcher, ui::LogicalDisplayId::DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
windowInPrimary->assertNoEvents();
windowInSecondary->assertNoEvents();
- secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ secondWindowInPrimary->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
}
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
FakeMonitorReceiver monitorInPrimary =
- FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
FakeMonitorReceiver monitorInSecondary =
FakeMonitorReceiver(*mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ monitorInPrimary.consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// Test touch down on second display.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -7843,15 +8830,15 @@
// Trigger cancel touch.
mDispatcher->cancelCurrentTouch();
- windowInPrimary->consumeMotionCancel(ADISPLAY_ID_DEFAULT);
- monitorInPrimary.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+ windowInPrimary->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT);
+ monitorInPrimary.consumeMotionCancel(ui::LogicalDisplayId::DEFAULT);
windowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID);
monitorInSecondary.consumeMotionCancel(SECOND_DISPLAY_ID);
// Test inject a move motion event, no window/monitor should receive the event.
ASSERT_EQ(InputEventInjectionResult::FAILED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {110, 200}))
+ ui::LogicalDisplayId::DEFAULT, {110, 200}))
<< "Inject motion event should return InputEventInjectionResult::FAILED";
windowInPrimary->assertNoEvents();
monitorInPrimary.assertNoEvents();
@@ -7874,11 +8861,11 @@
// Send a key down on primary display
mDispatcher->notifyKey(
KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT)
.build());
- windowInPrimary->consumeKeyEvent(
- AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ windowInPrimary->consumeKeyEvent(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
windowInSecondary->assertNoEvents();
// Send a key down on second display
@@ -7894,7 +8881,7 @@
// Send a valid key up event on primary display that will be dropped because it is stale
NotifyKeyArgs staleKeyUp =
KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT)
.build();
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms;
@@ -7906,7 +8893,7 @@
// Therefore, windowInPrimary should get the cancel event and windowInSecondary should not
// receive any events.
windowInPrimary->consumeKeyEvent(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
- WithDisplayId(ADISPLAY_ID_DEFAULT),
+ WithDisplayId(ui::LogicalDisplayId::DEFAULT),
WithFlags(AKEY_EVENT_FLAG_CANCELED)));
windowInSecondary->assertNoEvents();
}
@@ -7919,10 +8906,10 @@
mDispatcher->notifyMotion(
MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200))
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.build());
windowInPrimary->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
windowInSecondary->assertNoEvents();
// Send touch down on second display.
@@ -7938,7 +8925,7 @@
// inject a valid MotionEvent on primary display that will be stale when it arrives.
NotifyMotionArgs staleMotionUp =
MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200))
.build();
static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms;
@@ -7954,7 +8941,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;
@@ -7993,19 +8980,19 @@
// Test InputFilter for MotionEvent
TEST_F(InputFilterTest, MotionEvent_InputFilter) {
// Since the InputFilter is disabled by default, check if touch events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(ui::LogicalDisplayId::DEFAULT, /*expectToBeFiltered=*/false);
testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
// Enable InputFilter
mDispatcher->setInputFilterEnabled(true);
// Test touch on both primary and second display, and check if both events are filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true);
+ testNotifyMotion(ui::LogicalDisplayId::DEFAULT, /*expectToBeFiltered=*/true);
testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true);
// Disable InputFilter
mDispatcher->setInputFilterEnabled(false);
// Test touch on both primary and second display, and check if both events aren't filtered.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/false);
+ testNotifyMotion(ui::LogicalDisplayId::DEFAULT, /*expectToBeFiltered=*/false);
testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/false);
}
@@ -8034,7 +9021,7 @@
secondDisplayTransform.set({-6.6, -5.5, -4.4, -3.3, -2.2, -1.1, 0, 0, 1});
std::vector<gui::DisplayInfo> displayInfos(2);
- displayInfos[0].displayId = ADISPLAY_ID_DEFAULT;
+ displayInfos[0].displayId = ui::LogicalDisplayId::DEFAULT;
displayInfos[0].transform = firstDisplayTransform;
displayInfos[1].displayId = SECOND_DISPLAY_ID;
displayInfos[1].transform = secondDisplayTransform;
@@ -8045,7 +9032,8 @@
mDispatcher->setInputFilterEnabled(true);
// Ensure the correct transforms are used for the displays.
- testNotifyMotion(ADISPLAY_ID_DEFAULT, /*expectToBeFiltered=*/true, firstDisplayTransform);
+ testNotifyMotion(ui::LogicalDisplayId::DEFAULT, /*expectToBeFiltered=*/true,
+ firstDisplayTransform);
testNotifyMotion(SECOND_DISPLAY_ID, /*expectToBeFiltered=*/true, secondDisplayTransform);
}
@@ -8064,9 +9052,9 @@
std::shared_ptr<InputApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Test Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mWindow->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
@@ -8079,8 +9067,8 @@
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,
- KEY_A, AMETA_NONE, /*repeatCount=*/0, eventTime, eventTime);
+ ui::LogicalDisplayId::INVALID, 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;
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -8160,20 +9148,20 @@
std::make_shared<FakeApplicationHandle>();
application->setDispatchingTimeout(100ms);
mWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "TestWindow",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFrame(Rect(0, 0, 100, 100));
mWindow->setDispatchingTimeout(100ms);
mWindow->setFocusable(true);
// Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
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)
@@ -8194,50 +9182,55 @@
mDispatcher->setMinTimeBetweenUserActivityPokes(50ms);
// First event of type TOUCH. Should poke.
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(50));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(50), USER_ACTIVITY_EVENT_TOUCH,
+ ui::LogicalDisplayId::DEFAULT}});
// 80ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(130));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(130), USER_ACTIVITY_EVENT_TOUCH,
+ ui::LogicalDisplayId::DEFAULT}});
// First event of type OTHER. Should poke (despite being within 50ns of previous TOUCH event).
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
- milliseconds_to_nanoseconds(135));
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(135));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(135), USER_ACTIVITY_EVENT_OTHER,
+ ui::LogicalDisplayId::DEFAULT}});
// Within 50ns of previous TOUCH event. Should NOT poke.
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(140));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous OTHER event. Should NOT poke.
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
- milliseconds_to_nanoseconds(150));
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(150));
mFakePolicy->assertUserActivityNotPoked();
// Within 50ns of previous TOUCH event (which was at time 130). Should NOT poke.
// Note that STYLUS is mapped to TOUCH user activity, since it's a pointer-type source.
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(160));
mFakePolicy->assertUserActivityNotPoked();
// 65ns > 50ns has passed since previous OTHER event. Should poke.
- notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER, ADISPLAY_ID_DEFAULT,
- milliseconds_to_nanoseconds(200));
+ notifyAndConsumeMotion(ACTION_SCROLL, AINPUT_SOURCE_ROTARY_ENCODER,
+ ui::LogicalDisplayId::DEFAULT, milliseconds_to_nanoseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_OTHER,
+ ui::LogicalDisplayId::DEFAULT}});
// 170ns > 50ns has passed since previous TOUCH event. Should poke.
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_STYLUS, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(300));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(300), USER_ACTIVITY_EVENT_TOUCH,
+ ui::LogicalDisplayId::DEFAULT}});
// Assert that there's no more user activity poke event.
mFakePolicy->assertUserActivityNotPoked();
@@ -8247,19 +9240,21 @@
InputDispatcherUserActivityPokeTests, DefaultMinPokeTimeOf100MsUsed,
REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
rate_limit_user_activity_poke_in_dispatcher))) {
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(200));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(200), USER_ACTIVITY_EVENT_TOUCH,
+ ui::LogicalDisplayId::DEFAULT}});
- notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(280));
mFakePolicy->assertUserActivityNotPoked();
- notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ notifyAndConsumeMotion(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
milliseconds_to_nanoseconds(340));
mFakePolicy->assertUserActivityPoked(
- {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH, ADISPLAY_ID_DEFAULT}});
+ {{milliseconds_to_nanoseconds(340), USER_ACTIVITY_EVENT_TOUCH,
+ ui::LogicalDisplayId::DEFAULT}});
}
TEST_F_WITH_FLAGS(
@@ -8268,10 +9263,12 @@
rate_limit_user_activity_poke_in_dispatcher))) {
mDispatcher->setMinTimeBetweenUserActivityPokes(0ms);
- notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20);
+ notifyAndConsumeMotion(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
+ 20);
mFakePolicy->assertUserActivityPoked();
- notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 30);
+ notifyAndConsumeMotion(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
+ 30);
mFakePolicy->assertUserActivityPoked();
}
@@ -8281,16 +9278,16 @@
std::shared_ptr<FakeApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
- mUnfocusedWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Top",
+ ui::LogicalDisplayId::DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
- mFocusedWindow =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Second",
+ ui::LogicalDisplayId::DEFAULT);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
// Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
@@ -8318,8 +9315,8 @@
// the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_Success) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {20, 20}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mUnfocusedWindow->consumeMotionDown();
@@ -8332,7 +9329,7 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonPointerSource) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TRACKBALL, ui::LogicalDisplayId::DEFAULT,
{20, 20}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -8345,9 +9342,9 @@
// have focus. Ensure no window received the onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDownNoRepeat(*mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDownNoRepeat(*mDispatcher, ui::LogicalDisplayId::DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mFocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownWasNotCalled();
@@ -8358,8 +9355,8 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_TOUCH_POINT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, FOCUSED_WINDOW_TOUCH_POINT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mFocusedWindow->consumeMotionDown();
@@ -8378,7 +9375,8 @@
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, event))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mUnfocusedWindow->consumeAnyMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mUnfocusedWindow->consumeAnyMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownWasNotCalled();
@@ -8395,10 +9393,10 @@
std::shared_ptr<FakeApplicationHandle> application =
std::make_shared<FakeApplicationHandle>();
mWindow1 = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window 1",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mWindow1->setFrame(Rect(0, 0, 100, 100));
- mWindow2 = mWindow1->clone(ADISPLAY_ID_DEFAULT);
+ mWindow2 = mWindow1->clone(ui::LogicalDisplayId::DEFAULT);
mWindow2->setFrame(Rect(100, 100, 200, 200));
mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0});
@@ -8439,7 +9437,7 @@
const std::vector<PointF>& touchedPoints,
std::vector<PointF> expectedPoints) {
mDispatcher->notifyMotion(generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, touchedPoints));
+ ui::LogicalDisplayId::DEFAULT, touchedPoints));
consumeMotionEvent(touchedWindow, action, expectedPoints);
}
@@ -8586,14 +9584,14 @@
// Touch down in window 1
mDispatcher->notifyMotion(generateMotionArgs(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{50, 50}}));
+ ui::LogicalDisplayId::DEFAULT, {{50, 50}}));
consumeMotionEvent(mWindow1, ACTION_DOWN, {{50, 50}});
// Move touch to be above window 2. Even though window 1 is slippery, touch should not slip.
// That means the gesture should continue normally, without any ACTION_CANCEL or ACTION_DOWN
// getting generated.
mDispatcher->notifyMotion(generateMotionArgs(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {{150, 150}}));
+ ui::LogicalDisplayId::DEFAULT, {{150, 150}}));
consumeMotionEvent(mWindow1, ACTION_MOVE, {{150, 150}});
}
@@ -8628,13 +9626,13 @@
mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(100ms);
mWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "TestWindow",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFrame(Rect(0, 0, 30, 30));
mWindow->setDispatchingTimeout(100ms);
mWindow->setFocusable(true);
// Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApplication);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
setFocusedWindow(mWindow);
@@ -8665,8 +9663,8 @@
}
sp<FakeWindowHandle> addSpyWindow() {
- sp<FakeWindowHandle> spy =
- sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Spy",
+ ui::LogicalDisplayId::DEFAULT);
spy->setTrustedOverlay(true);
spy->setFocusable(false);
spy->setSpy(true);
@@ -8688,7 +9686,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::LogicalDisplayId::INVALID);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -8699,15 +9697,16 @@
mWindow->consumeFocusEvent(false);
InputEventInjectionResult result =
- injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, CONSUME_TIMEOUT_EVENT_EXPECTED,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::NONE,
+ CONSUME_TIMEOUT_EVENT_EXPECTED,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
// Key will not go to window because we have no focused window.
// The 'no focused window' ANR timer should start instead.
// Now, the focused application goes away.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, nullptr);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, nullptr);
// The key should get dropped and there should be no ANR.
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -8719,8 +9718,8 @@
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, OnPointerDown_BasicAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION));
const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
@@ -8729,7 +9728,7 @@
mWindow->finishEvent(*sequenceNum);
mWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
}
@@ -8753,8 +9752,8 @@
// taps on the window work as normal
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
mDispatcher->waitForIdle();
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -8763,8 +9762,9 @@
// We specify the injection timeout to be smaller than the application timeout, to ensure that
// 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);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, 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);
@@ -8787,11 +9787,13 @@
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,
- INVALID_HMAC, AKEY_EVENT_ACTION_DOWN, /*flags=*/0, AKEYCODE_A, KEY_A,
- AMETA_NONE, /*repeatCount=*/1, eventTime, eventTime);
+ event.initialize(InputEvent::nextId(), DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID, INVALID_HMAC, AKEY_EVENT_ACTION_DOWN,
+ /*flags=*/0, AKEYCODE_A, KEY_A, AMETA_NONE, /*repeatCount=*/0, eventTime,
+ eventTime);
- const int32_t policyFlags = POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER;
+ const int32_t policyFlags =
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_DISABLE_KEY_REPEAT;
InputEventInjectionResult result =
mDispatcher->injectInputEvent(&event, /*targetUid=*/{},
@@ -8810,7 +9812,7 @@
TEST_F(InputDispatcherSingleWindowAnr, NoFocusedWindow_DoesNotSendDuplicateAnr) {
const std::chrono::duration appTimeout = 400ms;
mApplication->setDispatchingTimeout(appTimeout);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApplication);
mWindow->setFocusable(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0});
@@ -8822,8 +9824,9 @@
const std::chrono::duration eventInjectionTimeout = 100ms;
ASSERT_LT(eventInjectionTimeout, appTimeout);
const InputEventInjectionResult result =
- injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::WAIT_FOR_RESULT, eventInjectionTimeout,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::WAIT_FOR_RESULT,
+ eventInjectionTimeout,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::TIMED_OUT, result)
<< "result=" << ftl::enum_string(result);
@@ -8871,20 +9874,20 @@
TEST_F(InputDispatcherSingleWindowAnr, Anr_HandlesEventsWithIdenticalTimestamps) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// Now send ACTION_UP, with identical timestamp
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, WINDOW_LOCATION,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION,
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
500ms, InputEventInjectionSync::WAIT_FOR_RESULT, currentTime);
// We have now sent down and up. Let's consume first event and then ANR on the second.
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(timeout, mWindow);
}
@@ -8894,8 +9897,8 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION));
mWindow->consumeMotionDown();
const auto [sequenceNum, _] = spy->receiveEvent(); // ACTION_DOWN
@@ -8905,7 +9908,7 @@
spy->finishEvent(*sequenceNum);
spy->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyWindowResponsiveWasCalled(spy->getToken(), mWindow->getPid());
}
@@ -8916,9 +9919,10 @@
sp<FakeWindowHandle> spy = addSpyWindow();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT));
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
- ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher, ADISPLAY_ID_DEFAULT));
+ injectKeyDown(*mDispatcher, ui::LogicalDisplayId::DEFAULT));
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectKeyUp(*mDispatcher, ui::LogicalDisplayId::DEFAULT));
// Stuck on the ACTION_UP
const std::chrono::duration timeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
@@ -8926,10 +9930,10 @@
// New tap will go to the spy window, but not to the window
tapOnWindow();
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ spy->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
- mWindow->consumeKeyUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mWindow->assertNoEvents();
@@ -8942,8 +9946,8 @@
sp<FakeWindowHandle> spy = addSpyWindow();
tapOnWindow();
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ spy->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
mWindow->consumeMotionDown();
// Stuck on the ACTION_UP
@@ -8952,10 +9956,10 @@
// New tap will go to the spy window, but not to the window
tapOnWindow();
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ spy->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
- mWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); // still the previous motion
+ mWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT); // still the previous motion
mDispatcher->waitForIdle();
mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
mWindow->assertNoEvents();
@@ -8965,13 +9969,14 @@
TEST_F(InputDispatcherSingleWindowAnr, UnresponsiveMonitorAnr) {
mDispatcher->setMonitorDispatchingTimeoutForTest(SPY_TIMEOUT);
- FakeMonitorReceiver monitor = FakeMonitorReceiver(*mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitor =
+ FakeMonitorReceiver(*mDispatcher, "M_1", ui::LogicalDisplayId::DEFAULT);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION));
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
const std::optional<uint32_t> consumeSeq = monitor.receiveEvent();
ASSERT_TRUE(consumeSeq);
@@ -8979,7 +9984,7 @@
MONITOR_PID);
monitor.finishEvent(*consumeSeq);
- monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionCancel(ui::LogicalDisplayId::DEFAULT);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyWindowResponsiveWasCalled(monitor.getToken(), MONITOR_PID);
@@ -9018,8 +10023,8 @@
// it.
TEST_F(InputDispatcherSingleWindowAnr, Policy_DoesNotGetDuplicateAnr) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, WINDOW_LOCATION));
const std::chrono::duration windowTimeout = mWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT);
mFakePolicy->assertNotifyWindowUnresponsiveWasCalled(windowTimeout, mWindow);
@@ -9029,7 +10034,7 @@
// When the ANR happened, dispatcher should abort the current event stream via ACTION_CANCEL
mWindow->consumeMotionDown();
mWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
mWindow->assertNoEvents();
mDispatcher->waitForIdle();
mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), mWindow->getPid());
@@ -9066,7 +10071,7 @@
std::this_thread::sleep_for(400ms);
// if we wait long enough though, dispatcher will give up, and still send the key
// to the focused window, even though we have not yet finished the motion event
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
mWindow->finishEvent(*downSequenceNum);
mWindow->finishEvent(*upSequenceNum);
}
@@ -9170,8 +10175,8 @@
// So InputDispatcher will enqueue ACTION_CANCEL event as well.
TEST_F(InputDispatcherSingleWindowAnr, AnrAfterWindowRemoval) {
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {WINDOW_LOCATION}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {WINDOW_LOCATION}));
const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
@@ -9188,7 +10193,7 @@
mWindow->finishEvent(*sequenceNum);
// The cancellation was generated when the window was removed, along with the focus event.
mWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
mWindow->consumeFocusEvent(false);
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyWindowResponsiveWasCalled(mWindow->getToken(), /*pid=*/std::nullopt);
@@ -9198,8 +10203,8 @@
// notified of the unresponsive window, then remove the app window.
TEST_F(InputDispatcherSingleWindowAnr, AnrFollowedByWindowRemoval) {
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {WINDOW_LOCATION}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {WINDOW_LOCATION}));
const auto [sequenceNum, _] = mWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(sequenceNum);
@@ -9212,7 +10217,7 @@
mWindow->finishEvent(*sequenceNum);
// The cancellation was generated during the ANR, and the window lost focus when it was removed.
mWindow->consumeMotionEvent(
- AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ADISPLAY_ID_DEFAULT)));
+ AllOf(WithMotionAction(ACTION_CANCEL), WithDisplayId(ui::LogicalDisplayId::DEFAULT)));
mWindow->consumeFocusEvent(false);
ASSERT_TRUE(mDispatcher->waitForIdle());
// Since the window was removed, Dispatcher does not know the PID associated with the window
@@ -9227,18 +10232,18 @@
mApplication = std::make_shared<FakeApplicationHandle>();
mApplication->setDispatchingTimeout(100ms);
mUnfocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Unfocused",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mUnfocusedWindow->setFrame(Rect(0, 0, 30, 30));
// Adding FLAG_WATCH_OUTSIDE_TOUCH to receive ACTION_OUTSIDE when another window is tapped
mUnfocusedWindow->setWatchOutsideTouch(true);
mFocusedWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Focused",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mFocusedWindow->setDispatchingTimeout(100ms);
mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
// Set focused application.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApplication);
mFocusedWindow->setFocusable(true);
// Expect one focus window exist in display.
@@ -9270,11 +10275,11 @@
private:
void tap(const PointF& location) {
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- location));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, location));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- location));
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, location));
}
};
@@ -9299,7 +10304,7 @@
.build()));
mFocusedWindow->consumeMotionDown();
mFocusedWindow->consumeMotionUp();
- mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ui::LogicalDisplayId::DEFAULT, /*flags=*/0);
// We consumed all events, so no ANR
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -9375,7 +10380,7 @@
// At the same time, FLAG_WATCH_OUTSIDE_TOUCH targets should not receive any events.
TEST_F(InputDispatcherMultiWindowAnr, DuringAnr_SecondTapIsIgnored) {
tapOnFocusedWindow();
- mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mUnfocusedWindow->consumeMotionOutside(ui::LogicalDisplayId::DEFAULT, /*flags=*/0);
// Receive the events, but don't respond
const auto [downEventSequenceNum, downEvent] = mFocusedWindow->receiveEvent(); // ACTION_DOWN
ASSERT_TRUE(downEventSequenceNum);
@@ -9388,10 +10393,10 @@
// Tap once again
// We cannot use "tapOnFocusedWindow" because it asserts the injection result to be success
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, FOCUSED_WINDOW_LOCATION));
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
FOCUSED_WINDOW_LOCATION));
// Unfocused window does not receive ACTION_OUTSIDE because the tapped window is not a
// valid touch target
@@ -9412,8 +10417,8 @@
// If you tap outside of all windows, there will not be ANR
TEST_F(InputDispatcherMultiWindowAnr, TapOutsideAllWindows_DoesNotAnr) {
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- LOCATION_OUTSIDE_ALL_WINDOWS));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, LOCATION_OUTSIDE_ALL_WINDOWS));
ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertNotifyAnrWasNotCalled();
}
@@ -9425,8 +10430,8 @@
{{*mUnfocusedWindow->getInfo(), *mFocusedWindow->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::FAILED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- FOCUSED_WINDOW_LOCATION));
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, FOCUSED_WINDOW_LOCATION));
std::this_thread::sleep_for(mFocusedWindow->getDispatchingTimeout(DISPATCHING_TIMEOUT));
ASSERT_TRUE(mDispatcher->waitForIdle());
@@ -9466,8 +10471,9 @@
// window even if motions are still being processed.
InputEventInjectionResult result =
- injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0, ADISPLAY_ID_DEFAULT,
- InputEventInjectionSync::NONE, /*injectionTimeout=*/100ms);
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, 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.
@@ -9493,7 +10499,7 @@
// Now that all queues are cleared and no backlog in the connections, the key event
// can finally go to the newly focused "mUnfocusedWindow".
- mUnfocusedWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mUnfocusedWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
mFocusedWindow->assertNoEvents();
mUnfocusedWindow->assertNoEvents();
mFakePolicy->assertNotifyAnrWasNotCalled();
@@ -9504,14 +10510,15 @@
// The other window should not be affected by that.
TEST_F(InputDispatcherMultiWindowAnr, SplitTouch_SingleWindowAnr) {
// Touch Window 1
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {FOCUSED_WINDOW_LOCATION}));
- mUnfocusedWindow->consumeMotionOutside(ADISPLAY_ID_DEFAULT, /*flags=*/0);
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {FOCUSED_WINDOW_LOCATION}));
+ mUnfocusedWindow->consumeMotionOutside(ui::LogicalDisplayId::DEFAULT, /*flags=*/0);
// Touch Window 2
mDispatcher->notifyMotion(
- generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ generateMotionArgs(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT,
{FOCUSED_WINDOW_LOCATION, UNFOCUSED_WINDOW_LOCATION}));
const std::chrono::duration timeout =
@@ -9557,7 +10564,7 @@
std::shared_ptr<FakeApplicationHandle> focusedApplication =
std::make_shared<FakeApplicationHandle>();
focusedApplication->setDispatchingTimeout(300ms);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, focusedApplication);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, focusedApplication);
// The application that owns 'mFocusedWindow' and 'mUnfocusedWindow' is not focused.
mFocusedWindow->setFocusable(false);
@@ -9569,8 +10576,9 @@
// 'focusedApplication' will get blamed if this timer completes.
// 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,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -9584,9 +10592,9 @@
std::this_thread::sleep_for(100ms);
// Touch unfocused window. This should force the pending key to get dropped.
- mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {UNFOCUSED_WINDOW_LOCATION}));
+ mDispatcher->notifyMotion(
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {UNFOCUSED_WINDOW_LOCATION}));
// We do not consume the motion right away, because that would require dispatcher to first
// process (== drop) the key event, and by that time, ANR will be raised.
@@ -9639,20 +10647,20 @@
mFakePolicy->setStaleEventTimeout(3000ms);
sp<FakeWindowHandle> navigationBar =
sp<FakeWindowHandle>::make(systemUiApplication, mDispatcher, "NavigationBar",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
navigationBar->setFocusable(false);
navigationBar->setWatchOutsideTouch(true);
navigationBar->setFrame(Rect(0, 0, 100, 100));
mApplication->setDispatchingTimeout(3000ms);
// 'mApplication' is already focused, but we call it again here to make it explicit.
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApplication);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApplication);
std::shared_ptr<FakeApplicationHandle> anotherApplication =
std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> appWindow =
sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Another window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
appWindow->setFocusable(false);
appWindow->setFrame(Rect(100, 100, 200, 200));
@@ -9671,8 +10679,9 @@
// Key will not be sent anywhere because we have no focused window. It will remain pending.
// 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,
+ injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::NONE,
+ /*injectionTimeout=*/100ms,
/*allowKeyRepeat=*/false);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, result);
@@ -9680,8 +10689,9 @@
mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN)
.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,
+ result = injectKey(*mDispatcher, AKEY_EVENT_ACTION_UP, /*repeatCount=*/0,
+ ui::LogicalDisplayId::DEFAULT, 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
@@ -9716,16 +10726,16 @@
InputDispatcherTest::SetUp();
mApplication = std::make_shared<FakeApplicationHandle>();
- mNoInputWindow =
- sp<FakeWindowHandle>::make(mApplication, mDispatcher,
- "Window without input channel", ADISPLAY_ID_DEFAULT,
- /*createInputChannel=*/false);
+ mNoInputWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher,
+ "Window without input channel",
+ ui::LogicalDisplayId::DEFAULT,
+ /*createInputChannel=*/false);
mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
// It's perfectly valid for this window to not have an associated input channel
mBottomWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Bottom window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mBottomWindow->setFrame(Rect(0, 0, 100, 100));
mDispatcher->onWindowInfosChanged(
@@ -9742,8 +10752,8 @@
PointF touchedPoint = {10, 10};
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {touchedPoint}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {touchedPoint}));
mNoInputWindow->assertNoEvents();
// Even though the window 'mNoInputWindow' positioned above 'mBottomWindow' does not have
@@ -9760,7 +10770,7 @@
NoInputChannelFeature_DropsTouchesWithValidChannel) {
mNoInputWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher,
"Window with input channel and NO_INPUT_CHANNEL",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
mNoInputWindow->setNoInputChannel(true);
mNoInputWindow->setFrame(Rect(0, 0, 100, 100));
@@ -9770,8 +10780,8 @@
PointF touchedPoint = {10, 10};
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {touchedPoint}));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {touchedPoint}));
mNoInputWindow->assertNoEvents();
mBottomWindow->assertNoEvents();
@@ -9786,9 +10796,10 @@
virtual void SetUp() override {
InputDispatcherTest::SetUp();
mApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
- mMirror = mWindow->clone(ADISPLAY_ID_DEFAULT);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
+ mMirror = mWindow->clone(ui::LogicalDisplayId::DEFAULT);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mWindow->setFocusable(true);
mMirror->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9803,7 +10814,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::LogicalDisplayId::INVALID);
}
// A focused & mirrored window remains focused only if the window and its mirror are both
@@ -9815,10 +10826,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::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::INVALID);
mMirror->setFocusable(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9840,20 +10851,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::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::INVALID);
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::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::INVALID);
mWindow->setVisible(false);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9874,20 +10885,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::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyUp(ADISPLAY_ID_NONE);
+ mWindow->consumeKeyUp(ui::LogicalDisplayId::INVALID);
// 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::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mMirror->consumeKeyUp(ADISPLAY_ID_NONE);
+ mMirror->consumeKeyUp(ui::LogicalDisplayId::INVALID);
// Both windows are removed
mDispatcher->onWindowInfosChanged({{}, {}, 0, 0});
@@ -9909,7 +10920,7 @@
// Injected key goes to pending queue.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectKey(*mDispatcher, AKEY_EVENT_ACTION_DOWN, /*repeatCount=*/0,
- ADISPLAY_ID_DEFAULT, InputEventInjectionSync::NONE));
+ ui::LogicalDisplayId::DEFAULT, InputEventInjectionSync::NONE));
mMirror->setVisible(true);
mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mMirror->getInfo()}, {}, 0, 0});
@@ -9917,7 +10928,7 @@
// window gets focused
mWindow->consumeFocusEvent(true);
// window gets the pending key event
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
}
class InputDispatcherPointerCaptureTests : public InputDispatcherTest {
@@ -9929,13 +10940,14 @@
void SetUp() override {
InputDispatcherTest::SetUp();
mApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFocusable(true);
- mSecondWindow =
- sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2",
+ ui::LogicalDisplayId::DEFAULT);
mSecondWindow->setFocusable(true);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mDispatcher->onWindowInfosChanged(
{{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
@@ -9950,7 +10962,7 @@
PointerCaptureRequest requestAndVerifyPointerCapture(const sp<FakeWindowHandle>& window,
bool enabled) {
mDispatcher->requestPointerCapture(window->getToken(), enabled);
- auto request = mFakePolicy->assertSetPointerCaptureCalled(enabled);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(window, enabled);
notifyPointerCaptureChanged(request);
window->consumeCaptureEvent(enabled);
return request;
@@ -9983,7 +10995,7 @@
mWindow->consumeCaptureEvent(false);
mWindow->consumeFocusEvent(false);
mSecondWindow->consumeFocusEvent(true);
- mFakePolicy->assertSetPointerCaptureCalled(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
// Ensure that additional state changes from InputReader are not sent to the window.
notifyPointerCaptureChanged({});
@@ -10002,7 +11014,7 @@
notifyPointerCaptureChanged(request);
// Ensure that Pointer Capture is disabled.
- mFakePolicy->assertSetPointerCaptureCalled(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
mWindow->consumeCaptureEvent(false);
mWindow->assertNoEvents();
}
@@ -10012,13 +11024,13 @@
// The first window loses focus.
setFocusedWindow(mSecondWindow);
- mFakePolicy->assertSetPointerCaptureCalled(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
mWindow->consumeCaptureEvent(false);
// Request Pointer Capture from the second window before the notification from InputReader
// arrives.
mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
- auto request = mFakePolicy->assertSetPointerCaptureCalled(true);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(mSecondWindow, true);
// InputReader notifies Pointer Capture was disabled (because of the focus change).
notifyPointerCaptureChanged({});
@@ -10033,11 +11045,11 @@
TEST_F(InputDispatcherPointerCaptureTests, EnableRequestFollowsSequenceNumbers) {
// App repeatedly enables and disables capture.
mDispatcher->requestPointerCapture(mWindow->getToken(), true);
- auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+ auto firstRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
mDispatcher->requestPointerCapture(mWindow->getToken(), false);
- mFakePolicy->assertSetPointerCaptureCalled(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
mDispatcher->requestPointerCapture(mWindow->getToken(), true);
- auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+ auto secondRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
// InputReader notifies that PointerCapture has been enabled for the first request. Since the
// first request is now stale, this should do nothing.
@@ -10054,10 +11066,10 @@
// App toggles pointer capture off and on.
mDispatcher->requestPointerCapture(mWindow->getToken(), false);
- mFakePolicy->assertSetPointerCaptureCalled(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
mDispatcher->requestPointerCapture(mWindow->getToken(), true);
- auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(true);
+ auto enableRequest = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
// InputReader notifies that the latest "enable" request was processed, while skipping over the
// preceding "disable" request.
@@ -10109,6 +11121,58 @@
mWindow->assertNoEvents();
}
+TEST_F(InputDispatcherPointerCaptureTests, MultiDisplayPointerCapture) {
+ // The default display is the focused display to begin with.
+ requestAndVerifyPointerCapture(mWindow, true);
+
+ // Move the second window to a second display, make it the focused window on that display.
+ mSecondWindow->editInfo()->displayId = SECOND_DISPLAY_ID;
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+ setFocusedWindow(mSecondWindow);
+ mSecondWindow->consumeFocusEvent(true);
+
+ mWindow->assertNoEvents();
+
+ // The second window cannot gain capture because it is not on the focused display.
+ mDispatcher->requestPointerCapture(mSecondWindow->getToken(), true);
+ mFakePolicy->assertSetPointerCaptureNotCalled();
+ mSecondWindow->assertNoEvents();
+
+ // Make the second display the focused display.
+ mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
+
+ // This causes the first window to lose pointer capture, and it's unable to request capture.
+ mWindow->consumeCaptureEvent(false);
+ mFakePolicy->assertSetPointerCaptureCalled(mWindow, false);
+
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ mFakePolicy->assertSetPointerCaptureNotCalled();
+
+ // The second window is now able to gain pointer capture successfully.
+ requestAndVerifyPointerCapture(mSecondWindow, true);
+}
+
+using InputDispatcherPointerCaptureDeathTest = InputDispatcherPointerCaptureTests;
+
+TEST_F(InputDispatcherPointerCaptureDeathTest,
+ NotifyPointerCaptureChangedWithWrongTokenAbortsDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ ScopedSilentDeath _silentDeath;
+
+ mDispatcher->requestPointerCapture(mWindow->getToken(), true);
+ auto request = mFakePolicy->assertSetPointerCaptureCalled(mWindow, true);
+
+ // Dispatch a pointer changed event with a wrong token.
+ request.window = mSecondWindow->getToken();
+ ASSERT_DEATH(
+ {
+ notifyPointerCaptureChanged(request);
+ mSecondWindow->consumeCaptureEvent(true);
+ },
+ "Unexpected requested window for Pointer Capture.");
+}
+
class InputDispatcherUntrustedTouchesTest : public InputDispatcherTest {
protected:
constexpr static const float MAXIMUM_OBSCURING_OPACITY = 0.8;
@@ -10154,7 +11218,7 @@
sp<FakeWindowHandle> getWindow(gui::Uid uid, std::string name) {
std::shared_ptr<FakeApplicationHandle> app = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
- sp<FakeWindowHandle>::make(app, mDispatcher, name, ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle>::make(app, mDispatcher, name, ui::LogicalDisplayId::DEFAULT);
// Generate an arbitrary PID based on the UID
window->setOwnerInfo(gui::Pid{static_cast<pid_t>(1777 + (uid.val() % 10000))}, uid);
return window;
@@ -10162,8 +11226,8 @@
void touch(const std::vector<PointF>& points = {PointF{100, 200}}) {
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- points));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, points));
}
};
@@ -10528,20 +11592,21 @@
void SetUp() override {
InputDispatcherTest::SetUp();
mApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFrame(Rect(0, 0, 100, 100));
- mSecondWindow =
- sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2",
+ ui::LogicalDisplayId::DEFAULT);
mSecondWindow->setFrame(Rect(100, 0, 200, 100));
- mSpyWindow =
- sp<FakeWindowHandle>::make(mApp, mDispatcher, "SpyWindow", ADISPLAY_ID_DEFAULT);
+ mSpyWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "SpyWindow",
+ ui::LogicalDisplayId::DEFAULT);
mSpyWindow->setSpy(true);
mSpyWindow->setTrustedOverlay(true);
mSpyWindow->setFrame(Rect(0, 0, 200, 100));
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mDispatcher->onWindowInfosChanged(
{{*mSpyWindow->getInfo(), *mWindow->getInfo(), *mSecondWindow->getInfo()},
{},
@@ -10554,7 +11619,7 @@
case AINPUT_SOURCE_TOUCHSCREEN:
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
break;
case AINPUT_SOURCE_STYLUS:
@@ -10586,9 +11651,9 @@
}
// Window should receive motion event.
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// Spy window should also receive motion event
- mSpyWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
// Start performing drag, we will create a drag window and transfer touch to it.
@@ -10600,8 +11665,8 @@
}
// The drag window covers the entire display
- mDragWindow =
- sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow",
+ ui::LogicalDisplayId::DEFAULT);
mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
mDispatcher->onWindowInfosChanged({{*mDragWindow->getInfo(), *mSpyWindow->getInfo(),
*mWindow->getInfo(), *mSecondWindow->getInfo()},
@@ -10615,7 +11680,8 @@
/*isDragDrop=*/true);
if (transferred) {
mWindow->consumeMotionCancel();
- mDragWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
}
return transferred;
}
@@ -10627,35 +11693,38 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {150, 50}))
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(true, 150, 50);
mSecondWindow->consumeDragEvent(false, 50, 50);
// Move back to original window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->consumeDragEvent(true, -50, 50);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -10690,27 +11759,29 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {150, 50}))
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(true, 150, 50);
mSecondWindow->consumeDragEvent(false, 50, 50);
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
@@ -10772,7 +11843,8 @@
.pointer(PointerBuilder(0, ToolType::STYLUS).x(50).y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
@@ -10784,7 +11856,8 @@
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
@@ -10797,7 +11870,7 @@
.pointer(PointerBuilder(0, ToolType::STYLUS).x(150).y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -10813,27 +11886,29 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {150, 50}))
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(true, 150, 50);
mSecondWindow->assertNoEvents();
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
@@ -10844,14 +11919,14 @@
mWindow->setPreventSplitting(true);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(75).y(50))
@@ -10869,16 +11944,16 @@
TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
// First down on second window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {150, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mSecondWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mSecondWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
// Second down on first window.
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
@@ -10887,8 +11962,8 @@
injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- mSecondWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ mSecondWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT);
// Perform drag and drop from first window.
ASSERT_TRUE(startDrag(false));
@@ -10903,7 +11978,8 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->consumeMotionMove();
@@ -10917,7 +11993,7 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT,
InputEventInjectionSync::WAIT_FOR_RESULT));
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->consumeMotionMove();
@@ -10956,27 +12032,29 @@
// Move on window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {50, 50}))
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
// Move to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {150, 50}))
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(true, 150, 50);
mSecondWindow->consumeDragEvent(false, 50, 50);
// drop to another window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
@@ -10994,7 +12072,8 @@
.y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(false, 50, 50);
mSecondWindow->assertNoEvents();
@@ -11008,7 +12087,8 @@
.y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionMove(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mWindow->consumeDragEvent(true, 150, 50);
mSecondWindow->consumeDragEvent(false, 50, 50);
@@ -11022,7 +12102,7 @@
.y(50))
.build()))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
+ mDragWindow->consumeMotionUp(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE);
mFakePolicy->assertDropTargetEquals(*mDispatcher, mSecondWindow->getToken());
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
@@ -11035,8 +12115,8 @@
TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) {
// Down on second window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {150, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {150, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionDown());
@@ -11045,7 +12125,7 @@
// Down on first window
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
.build();
@@ -11063,7 +12143,7 @@
// Trigger cancel
mDispatcher->cancelCurrentTouch();
ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel());
- ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT,
+ ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel(ui::LogicalDisplayId::DEFAULT,
AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE));
ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel());
@@ -11076,14 +12156,14 @@
// Inject a simple gesture, ensure dispatcher not crashed
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- PointF{50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, PointF{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
const MotionEvent moveEvent =
MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
.build();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
@@ -11093,7 +12173,7 @@
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionMove());
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT,
{50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
@@ -11103,7 +12183,7 @@
// Start hovering over the window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE,
- ADISPLAY_ID_DEFAULT, {50, 50}));
+ ui::LogicalDisplayId::DEFAULT, {50, 50}));
ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)));
ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)));
@@ -11116,23 +12196,25 @@
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
window->setDropInput(true);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
// With the flag set, window should not get any input
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
window->assertNoEvents();
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
mDispatcher->waitForIdle();
window->assertNoEvents();
@@ -11140,12 +12222,13 @@
window->setDropInput(false);
mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents();
}
@@ -11154,16 +12237,17 @@
std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> obscuringWindow =
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
window->setDropInputIfObscured(true);
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged(
{{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -11171,13 +12255,14 @@
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
// With the flag set, window should not get any input
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
window->assertNoEvents();
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
window->assertNoEvents();
// With the flag cleared, the window should get input
@@ -11185,12 +12270,14 @@
mDispatcher->onWindowInfosChanged(
{{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT,
+ AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED);
window->assertNoEvents();
}
@@ -11199,16 +12286,17 @@
std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> obscuringWindow =
sp<FakeWindowHandle>::make(obscuringApplication, mDispatcher, "obscuringWindow",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
obscuringWindow->setFrame(Rect(0, 0, 50, 50));
obscuringWindow->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
obscuringWindow->setTouchable(false);
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Test window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Test window",
+ ui::LogicalDisplayId::DEFAULT);
window->setDropInputIfObscured(true);
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
window->setFocusable(true);
mDispatcher->onWindowInfosChanged(
{{*obscuringWindow->getInfo(), *window->getInfo()}, {}, 0, 0});
@@ -11216,25 +12304,27 @@
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
// With the flag set, window should not get any input
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT));
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT));
window->assertNoEvents();
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
window->assertNoEvents();
// When the window is no longer obscured because it went on top, it should get input
mDispatcher->onWindowInfosChanged(
{{*window->getInfo(), *obscuringWindow->getInfo()}, {}, 0, 0});
- mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT));
- window->consumeKeyUp(ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_UP, ui::LogicalDisplayId::DEFAULT));
+ window->consumeKeyUp(ui::LogicalDisplayId::DEFAULT);
mDispatcher->notifyMotion(generateMotionArgs(AMOTION_EVENT_ACTION_DOWN,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT));
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT));
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents();
}
@@ -11251,18 +12341,19 @@
mApp = std::make_shared<FakeApplicationHandle>();
mSecondaryApp = std::make_shared<FakeApplicationHandle>();
- mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow",
+ ui::LogicalDisplayId::DEFAULT);
mWindow->setFocusable(true);
setFocusedWindow(mWindow);
- mSecondWindow =
- sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow2",
+ ui::LogicalDisplayId::DEFAULT);
mSecondWindow->setFocusable(true);
mThirdWindow =
sp<FakeWindowHandle>::make(mSecondaryApp, mDispatcher,
"TestWindow3_SecondaryDisplay", SECOND_DISPLAY_ID);
mThirdWindow->setFocusable(true);
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, mApp);
mDispatcher->onWindowInfosChanged(
{{*mWindow->getInfo(), *mSecondWindow->getInfo(), *mThirdWindow->getInfo()},
{},
@@ -11273,7 +12364,8 @@
// Set main display initial touch mode to InputDispatcher::kDefaultInTouchMode.
if (mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, WINDOW_PID,
- WINDOW_UID, /*hasPermission=*/true, ADISPLAY_ID_DEFAULT)) {
+ WINDOW_UID, /*hasPermission=*/true,
+ ui::LogicalDisplayId::DEFAULT)) {
mWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
mSecondWindow->consumeTouchModeEvent(InputDispatcher::kDefaultInTouchMode);
mThirdWindow->assertNoEvents();
@@ -11292,7 +12384,7 @@
void changeAndVerifyTouchModeInMainDisplayOnly(bool inTouchMode, gui::Pid pid, gui::Uid uid,
bool hasPermission) {
ASSERT_TRUE(mDispatcher->setInTouchMode(inTouchMode, pid, uid, hasPermission,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
mWindow->consumeTouchModeEvent(inTouchMode);
mSecondWindow->consumeTouchModeEvent(inTouchMode);
mThirdWindow->assertNoEvents();
@@ -11313,7 +12405,7 @@
mWindow->setOwnerInfo(gui::Pid::INVALID, gui::Uid::INVALID);
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode, ownerPid,
ownerUid, /*hasPermission=*/false,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -11331,7 +12423,8 @@
const WindowInfo& windowInfo = *mWindow->getInfo();
ASSERT_FALSE(mDispatcher->setInTouchMode(InputDispatcher::kDefaultInTouchMode,
windowInfo.ownerPid, windowInfo.ownerUid,
- /*hasPermission=*/true, ADISPLAY_ID_DEFAULT));
+ /*hasPermission=*/true,
+ ui::LogicalDisplayId::DEFAULT));
mWindow->assertNoEvents();
mSecondWindow->assertNoEvents();
}
@@ -11349,9 +12442,9 @@
TEST_F(InputDispatcherTouchModeChangedTests, CanChangeTouchModeWhenOwningLastInteractedWindow) {
// Interact with the window first.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectKeyDown(*mDispatcher, ADISPLAY_ID_DEFAULT))
+ injectKeyDown(*mDispatcher, ui::LogicalDisplayId::DEFAULT))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- mWindow->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeKeyDown(ui::LogicalDisplayId::DEFAULT);
// Then remove focus.
mWindow->setFocusable(false);
@@ -11361,7 +12454,8 @@
const WindowInfo& windowInfo = *mWindow->getInfo();
ASSERT_TRUE(mDispatcher->setInTouchMode(!InputDispatcher::kDefaultInTouchMode,
windowInfo.ownerPid, windowInfo.ownerUid,
- /*hasPermission=*/false, ADISPLAY_ID_DEFAULT));
+ /*hasPermission=*/false,
+ ui::LogicalDisplayId::DEFAULT));
}
class InputDispatcherSpyWindowTest : public InputDispatcherTest {
@@ -11371,8 +12465,9 @@
std::make_shared<FakeApplicationHandle>();
std::string name = "Fake Spy ";
name += std::to_string(mSpyCount++);
- sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(application, mDispatcher,
- name.c_str(), ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spy =
+ sp<FakeWindowHandle>::make(application, mDispatcher, name.c_str(),
+ ui::LogicalDisplayId::DEFAULT);
spy->setSpy(true);
spy->setTrustedOverlay(true);
return spy;
@@ -11383,7 +12478,7 @@
std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
return window;
}
@@ -11414,9 +12509,10 @@
mDispatcher->onWindowInfosChanged({{*spy->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
}
/**
@@ -11454,7 +12550,8 @@
}
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
std::vector<size_t> eventOrder;
@@ -11492,9 +12589,10 @@
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
spy->assertNoEvents();
}
@@ -11511,20 +12609,22 @@
// Inject an event outside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->assertNoEvents();
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionUp();
spy->assertNoEvents();
// Inject an event inside the spy window's touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {5, 10}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {5, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->consumeMotionDown();
@@ -11545,8 +12645,8 @@
// Inject an event outside the spy window's frame and touchable region.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->consumeMotionOutsideWithZeroedCoords();
@@ -11567,8 +12667,8 @@
{{*spy->getInfo(), *windowLeft->getInfo(), *windowRight->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
windowLeft->consumeMotionDown();
spy->consumeMotionDown();
@@ -11599,8 +12699,8 @@
mDispatcher->onWindowInfosChanged({{*spyRight->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {50, 50}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {50, 50}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spyRight->assertNoEvents();
@@ -11636,16 +12736,16 @@
// First finger down, no window touched.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
window->assertNoEvents();
// Second finger down on window, the window should receive touch down.
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
@@ -11655,7 +12755,7 @@
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
spy->consumeMotionPointerDown(/*pointerIndex=*/1);
}
@@ -11674,11 +12774,11 @@
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeKeyDown(ADISPLAY_ID_NONE);
+ window->consumeKeyDown(ui::LogicalDisplayId::INVALID);
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyUp(*mDispatcher))
<< "Inject key event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeKeyUp(ADISPLAY_ID_NONE);
+ window->consumeKeyUp(ui::LogicalDisplayId::INVALID);
spy->assertNoEvents();
}
@@ -11697,7 +12797,8 @@
{{*spy1->getInfo(), *spy2->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy1->consumeMotionDown();
@@ -11712,7 +12813,7 @@
// The rest of the gesture should only be sent to the second spy window.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT))
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy2->consumeMotionMove();
spy1->assertNoEvents();
@@ -11729,19 +12830,21 @@
mDispatcher->onWindowInfosChanged({{*spy->getInfo(), *window->getInfo()}, {}, 0, 0});
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
+ spy->consumeMotionDown(ui::LogicalDisplayId::DEFAULT);
window->releaseChannel();
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
- spy->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ spy->consumeMotionUp(ui::LogicalDisplayId::DEFAULT);
}
/**
@@ -11756,8 +12859,8 @@
// First finger down on the window and the spy.
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {100, 200}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {100, 200}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
spy->consumeMotionDown();
window->consumeMotionDown();
@@ -11769,7 +12872,7 @@
// Second finger down on the window and spy, but the window should not receive the pointer down.
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
@@ -11784,7 +12887,7 @@
// Third finger goes down outside all windows, so injection should fail.
const MotionEvent thirdFingerDownEvent =
MotionEventBuilder(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
@@ -11812,15 +12915,15 @@
// First finger down on the window only
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {150, 150}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {150, 150}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
// Second finger down on the spy and window
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
@@ -11835,7 +12938,7 @@
// Third finger down on the spy and window
const MotionEvent thirdFingerDownEvent =
MotionEventBuilder(POINTER_2_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(150))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(10).y(10))
@@ -11850,8 +12953,14 @@
// Spy window pilfers the pointers.
EXPECT_EQ(OK, mDispatcher->pilferPointers(spy->getToken()));
- window->consumeMotionPointerUp(/*idx=*/2, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
- window->consumeMotionPointerUp(/*idx=*/1, ADISPLAY_ID_DEFAULT, AMOTION_EVENT_FLAG_CANCELED);
+ window->consumeMotionPointerUp(/*pointerIdx=*/2,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED),
+ WithPointerCount(3)));
+ window->consumeMotionPointerUp(/*pointerIdx=*/1,
+ AllOf(WithDisplayId(ui::LogicalDisplayId::DEFAULT),
+ WithFlags(AMOTION_EVENT_FLAG_CANCELED),
+ WithPointerCount(2)));
spy->assertNoEvents();
window->assertNoEvents();
@@ -11872,8 +12981,8 @@
// First finger down on both spy and window
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {10, 10}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->consumeMotionDown();
@@ -11881,7 +12990,7 @@
// Second finger down on the spy and window
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(10).y(10))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
@@ -11915,8 +13024,8 @@
// First finger down on both window and spy
ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
- injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
- {10, 10}))
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN,
+ ui::LogicalDisplayId::DEFAULT, {10, 10}))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
window->consumeMotionDown();
spy->consumeMotionDown();
@@ -11928,7 +13037,7 @@
// Second finger down on the window only
const MotionEvent secondFingerDownEvent =
MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
- .displayId(ADISPLAY_ID_DEFAULT)
+ .displayId(ui::LogicalDisplayId::DEFAULT)
.eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
.pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(10).y(10))
.pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(150).y(150))
@@ -11953,7 +13062,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();
@@ -12011,6 +13121,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();
@@ -12035,9 +13222,9 @@
std::pair<sp<FakeWindowHandle>, sp<FakeWindowHandle>> setupStylusOverlayScenario() {
std::shared_ptr<FakeApplicationHandle> overlayApplication =
std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> overlay =
- sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
- "Stylus interceptor window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> overlay = sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
+ "Stylus interceptor window",
+ ui::LogicalDisplayId::DEFAULT);
overlay->setFocusable(false);
overlay->setOwnerInfo(gui::Pid{111}, gui::Uid{111});
overlay->setTouchable(false);
@@ -12048,11 +13235,11 @@
std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
sp<FakeWindowHandle>::make(application, mDispatcher, "Application window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
window->setFocusable(true);
window->setOwnerInfo(gui::Pid{222}, gui::Uid{222});
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ mDispatcher->setFocusedApplication(ui::LogicalDisplayId::DEFAULT, application);
mDispatcher->onWindowInfosChanged({{*overlay->getInfo(), *window->getInfo()}, {}, 0, 0});
setFocusedWindow(window);
window->consumeFocusEvent(/*hasFocus=*/true, /*inTouchMode=*/true);
@@ -12062,13 +13249,13 @@
void sendFingerEvent(int32_t action) {
mDispatcher->notifyMotion(
generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{20, 20}}));
+ ui::LogicalDisplayId::DEFAULT, {PointF{20, 20}}));
}
void sendStylusEvent(int32_t action) {
NotifyMotionArgs motionArgs =
generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
- ADISPLAY_ID_DEFAULT, {PointF{30, 40}});
+ ui::LogicalDisplayId::DEFAULT, {PointF{30, 40}});
motionArgs.pointerProperties[0].toolType = ToolType::STYLUS;
mDispatcher->notifyMotion(motionArgs);
}
@@ -12172,7 +13359,7 @@
InputEventInjectionResult injectTargetedMotion(int32_t action) const {
return injectMotionEvent(*mDispatcher, action, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT, {100, 200},
+ ui::LogicalDisplayId::DEFAULT, {100, 200},
{AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
INJECT_EVENT_TIMEOUT, InputEventInjectionSync::WAIT_FOR_RESULT,
@@ -12180,7 +13367,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::LogicalDisplayId::INVALID,
InputEventInjectionSync::WAIT_FOR_RESULT,
INJECT_EVENT_TIMEOUT, /*allowKeyRepeat=*/false, {mUid},
mPolicyFlags);
@@ -12189,8 +13377,9 @@
sp<FakeWindowHandle> createWindow(const char* name) const {
std::shared_ptr<FakeApplicationHandle> overlayApplication =
std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(overlayApplication, mDispatcher,
- name, ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> window =
+ sp<FakeWindowHandle>::make(overlayApplication, mDispatcher, name,
+ ui::LogicalDisplayId::DEFAULT);
window->setOwnerInfo(mPid, mUid);
return window;
}
@@ -12212,7 +13401,7 @@
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
owner.injectTargetedKey(AKEY_EVENT_ACTION_DOWN));
- window->consumeKeyDown(ADISPLAY_ID_NONE);
+ window->consumeKeyDown(ui::LogicalDisplayId::INVALID);
}
TEST_F(InputDispatcherTargetedInjectionTest, CannotInjectIntoUnownedWindow) {
@@ -12277,7 +13466,7 @@
// A user that has injection permission can inject into any window.
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED,
injectMotionEvent(*mDispatcher, AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
- ADISPLAY_ID_DEFAULT));
+ ui::LogicalDisplayId::DEFAULT));
randosSpy->consumeMotionDown();
window->consumeMotionDown();
@@ -12285,7 +13474,7 @@
randosSpy->consumeFocusEvent(true);
EXPECT_EQ(InputEventInjectionResult::SUCCEEDED, injectKeyDown(*mDispatcher));
- randosSpy->consumeKeyDown(ADISPLAY_ID_NONE);
+ randosSpy->consumeKeyDown(ui::LogicalDisplayId::INVALID);
window->assertNoEvents();
}
@@ -12312,13 +13501,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
left->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Right Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> right =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
+ ui::LogicalDisplayId::DEFAULT);
right->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> spy =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window",
+ ui::LogicalDisplayId::DEFAULT);
spy->setFrame(Rect(0, 0, 200, 100));
spy->setTrustedOverlay(true);
spy->setSpy(true);
@@ -12335,11 +13525,14 @@
left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
// Hover move to the right window.
@@ -12352,11 +13545,14 @@
right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
// Stop hovering.
@@ -12368,11 +13564,14 @@
right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
spy->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
}
@@ -12380,13 +13579,14 @@
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> left = sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
left->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Right Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> right =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
+ ui::LogicalDisplayId::DEFAULT);
right->setFrame(Rect(100, 0, 200, 100));
- sp<FakeWindowHandle> spy =
- sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> spy = sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window",
+ ui::LogicalDisplayId::DEFAULT);
spy->setFrame(Rect(0, 0, 200, 100));
spy->setTrustedOverlay(true);
spy->setSpy(true);
@@ -12403,11 +13603,14 @@
left->consumeMotionDown();
spy->consumeMotionDown();
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
// Second pointer down on right window.
@@ -12421,17 +13624,23 @@
right->consumeMotionDown();
spy->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
// Second pointer up.
@@ -12445,17 +13654,23 @@
right->consumeMotionUp();
spy->consumeMotionEvent(WithMotionAction(POINTER_1_UP));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/1));
// First pointer up.
@@ -12467,29 +13682,36 @@
left->consumeMotionUp();
spy->consumeMotionUp();
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(spy->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*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",
- ADISPLAY_ID_DEFAULT);
+ ui::LogicalDisplayId::DEFAULT);
left->setFrame(Rect(0, 0, 100, 100));
- sp<FakeWindowHandle> right = sp<FakeWindowHandle>::make(application, mDispatcher,
- "Right Window", ADISPLAY_ID_DEFAULT);
+ sp<FakeWindowHandle> right =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
+ ui::LogicalDisplayId::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,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
// Hover move into the window.
@@ -12503,7 +13725,8 @@
left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
// Move the mouse with another device. This cancels the hovering pointer from the first device.
@@ -12520,9 +13743,10 @@
// 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,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
SECOND_DEVICE_ID,
/*pointerId=*/0));
@@ -12538,11 +13762,95 @@
left->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT));
right->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER));
- ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT, DEVICE_ID,
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
/*pointerId=*/0));
- ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ADISPLAY_ID_DEFAULT,
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
SECOND_DEVICE_ID,
/*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",
+ ui::LogicalDisplayId::DEFAULT);
+ left->setFrame(Rect(0, 0, 100, 100));
+ sp<FakeWindowHandle> right =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right Window",
+ ui::LogicalDisplayId::DEFAULT);
+ right->setFrame(Rect(100, 0, 200, 100));
+
+ mDispatcher->onWindowInfosChanged({{*left->getInfo(), *right->getInfo()}, {}, 0, 0});
+
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(right->getToken(), ui::LogicalDisplayId::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(), ui::LogicalDisplayId::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(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_TRUE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::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(), ui::LogicalDisplayId::DEFAULT,
+ DEVICE_ID,
+ /*pointerId=*/0));
+ ASSERT_FALSE(mDispatcher->isPointerInWindow(left->getToken(), ui::LogicalDisplayId::DEFAULT,
+ SECOND_DEVICE_ID,
+ /*pointerId=*/0));
+}
+
+TEST_F(InputDispatcherTest, FocusedDisplayChangeIsNotified) {
+ mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
+ mFakePolicy->assertFocusedDisplayNotified(SECOND_DISPLAY_ID);
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp
index 2aecab9..ff0de83 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -26,17 +26,13 @@
namespace android {
using testing::_;
+using testing::NiceMock;
using testing::Return;
+using testing::ReturnRef;
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));
@@ -49,16 +45,11 @@
EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) {
return mPropertyMap;
});
-}
-void InputMapperUnitTest::createDevice() {
- mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
- /*generation=*/2, mIdentifier);
- mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
+ mDevice = std::make_unique<NiceMock<MockInputDevice>>(&mMockInputReaderContext, DEVICE_ID,
+ /*generation=*/2, mIdentifier);
+ ON_CALL((*mDevice), getConfiguration).WillByDefault(ReturnRef(mPropertyMap));
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
- std::list<NotifyArgs> args =
- mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{});
- ASSERT_THAT(args, testing::ElementsAre(testing::VariantWith<NotifyDeviceResetArgs>(_)));
}
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
@@ -110,7 +101,7 @@
event.type = type;
event.code = code;
event.value = value;
- return mMapper->process(&event);
+ return mMapper->process(event);
}
const char* InputMapperTest::DEVICE_NAME = "device";
@@ -178,8 +169,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) {
@@ -201,7 +192,7 @@
event.type = type;
event.code = code;
event.value = value;
- std::list<NotifyArgs> processArgList = mapper.process(&event);
+ std::list<NotifyArgs> processArgList = mapper.process(event);
for (const NotifyArgs& args : processArgList) {
mFakeListener->notify(args);
}
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index e176a65..4271a70 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -40,18 +40,9 @@
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);
- /**
- * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of
- * mPropertyMap, so tests that need to set configuration properties should do so before calling
- * this. Others will most likely want to call it in their SetUp method.
- */
- void createDevice();
-
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
void expectScanCodes(bool present, std::set<int> scanCodes);
@@ -66,9 +57,8 @@
InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
- std::shared_ptr<FakePointerController> mFakePointerController;
MockInputReaderContext mMockInputReaderContext;
- std::unique_ptr<InputDevice> mDevice;
+ std::unique_ptr<MockInputDevice> mDevice;
std::unique_ptr<InputDeviceContext> mDeviceContext;
InputReaderConfiguration mReaderConfiguration;
@@ -124,14 +114,15 @@
T& constructAndAddMapper(Args... args) {
// ensure a device entry exists for this eventHubId
mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
- // configure the empty device
- configureDevice(/*changes=*/{});
- return mDevice->constructAndAddMapper<T>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
- args...);
+ auto& mapper =
+ mDevice->constructAndAddMapper<T>(EVENTHUB_ID,
+ mFakePolicy->getReaderConfiguration(), args...);
+ configureDevice(/*changes=*/{});
+ return mapper;
}
- 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..bdf156c 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::LogicalDisplayId::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..f7e5e67 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::LogicalDisplayId::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::LogicalDisplayId::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 835f8b8..e6367b7 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -27,6 +27,7 @@
#include <JoystickInputMapper.h>
#include <KeyboardInputMapper.h>
#include <MultiTouchInputMapper.h>
+#include <NotifyArgsBuilders.h>
#include <PeripheralController.h>
#include <SensorInputMapper.h>
#include <SingleTouchInputMapper.h>
@@ -40,13 +41,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 +60,13 @@
using std::chrono_literals::operator""s;
// Arbitrary display properties.
-static constexpr int32_t DISPLAY_ID = 0;
+static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT;
static const std::string DISPLAY_UNIQUE_ID = "local:1";
-static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
-static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2";
+static constexpr ui::LogicalDisplayId SECONDARY_DISPLAY_ID =
+ ui::LogicalDisplayId{DISPLAY_ID.val() + 1};
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";
@@ -310,9 +309,9 @@
return {};
}
- std::list<NotifyArgs> process(const RawEvent* rawEvent) override {
+ std::list<NotifyArgs> process(const RawEvent& rawEvent) override {
std::scoped_lock<std::mutex> lock(mLock);
- mLastEvent = *rawEvent;
+ mLastEvent = rawEvent;
mProcessWasCalled = true;
mStateChangedCondition.notify_all();
return mProcessResult;
@@ -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::LogicalDisplayId::DEFAULT)
+ << "Test display ID should not be ui::LogicalDisplayId::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::LogicalDisplayId::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::LogicalDisplayId::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::LogicalDisplayId::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::LogicalDisplayId::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;
@@ -1165,18 +1164,18 @@
TEST_F(InputReaderTest, ChangingPointerCaptureNotifiesInputListener) {
NotifyPointerCaptureChangedArgs args;
- auto request = mFakePolicy->setPointerCapture(true);
+ auto request = mFakePolicy->setPointerCapture(/*window=*/sp<BBinder>::make());
mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_TRUE(args.request.enable) << "Pointer Capture should be enabled.";
+ ASSERT_TRUE(args.request.isEnable()) << "Pointer Capture should be enabled.";
ASSERT_EQ(args.request, request) << "Pointer Capture sequence number should match.";
- mFakePolicy->setPointerCapture(false);
+ mFakePolicy->setPointerCapture(/*window=*/nullptr);
mReader->requestRefreshConfiguration(InputReaderConfiguration::Change::POINTER_CAPTURE);
mReader->loopOnce();
mFakeListener->assertNotifyCaptureWasCalled(&args);
- ASSERT_FALSE(args.request.enable) << "Pointer Capture should be disabled.";
+ ASSERT_FALSE(args.request.isEnable()) << "Pointer Capture should be disabled.";
// Verify that the Pointer Capture state is not updated when the configuration value
// does not change.
@@ -1185,6 +1184,82 @@
mFakeListener->assertNotifyCaptureWasNotCalled();
}
+TEST_F(InputReaderTest, GetLastUsedInputDeviceId) {
+ constexpr int32_t FIRST_DEVICE_ID = END_RESERVED_ID + 1000;
+ constexpr int32_t SECOND_DEVICE_ID = FIRST_DEVICE_ID + 1;
+ FakeInputMapper& firstMapper =
+ addDeviceWithFakeInputMapper(FIRST_DEVICE_ID, FIRST_DEVICE_ID, "first",
+ InputDeviceClass::KEYBOARD, AINPUT_SOURCE_KEYBOARD,
+ /*configuration=*/nullptr);
+ FakeInputMapper& secondMapper =
+ addDeviceWithFakeInputMapper(SECOND_DEVICE_ID, SECOND_DEVICE_ID, "second",
+ InputDeviceClass::TOUCH_MT, AINPUT_SOURCE_STYLUS,
+ /*configuration=*/nullptr);
+
+ ASSERT_EQ(ReservedInputDeviceId::INVALID_INPUT_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+
+ // Start a new key gesture from the first device
+ firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .deviceId(FIRST_DEVICE_ID)
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(firstMapper.getDeviceId(), mReader->getLastUsedInputDeviceId());
+
+ // Start a new touch gesture from the second device
+ secondMapper.setProcessResult(
+ {MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_STYLUS)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER))
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+
+ // Releasing the key is not a new gesture, so it does not update the last used device
+ firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .deviceId(FIRST_DEVICE_ID)
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+
+ // But pressing a new key does start a new gesture
+ firstMapper.setProcessResult({KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .deviceId(FIRST_DEVICE_ID)
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, FIRST_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+
+ // Moving or ending a touch gesture does not update the last used device
+ secondMapper.setProcessResult(
+ {MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_STYLUS)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+ secondMapper.setProcessResult({MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_STYLUS)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(FIRST_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+
+ // Starting a new hover gesture updates the last used device
+ secondMapper.setProcessResult(
+ {MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS)
+ .deviceId(SECOND_DEVICE_ID)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::STYLUS))
+ .build()});
+ mFakeEventHub->enqueueEvent(ARBITRARY_TIME, ARBITRARY_TIME, SECOND_DEVICE_ID, 0, 0, 0);
+ mReader->loopOnce();
+ ASSERT_EQ(SECOND_DEVICE_ID, mReader->getLastUsedInputDeviceId());
+}
+
class FakeVibratorInputMapper : public FakeInputMapper {
public:
FakeVibratorInputMapper(InputDeviceContext& deviceContext,
@@ -1349,8 +1424,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 +1432,6 @@
GTEST_SKIP();
#endif
mFakePolicy = sp<FakeInputReaderPolicy>::make();
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
setupInputReader();
}
@@ -1603,7 +1674,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 +1725,6 @@
} else {
mFakePolicy->addInputUniqueIdAssociation(INPUT_PORT, UNIQUE_ID);
}
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
InputReaderIntegrationTest::setupInputReader();
@@ -2907,7 +2976,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);
}
@@ -3245,13 +3314,17 @@
class KeyboardInputMapperTest : public InputMapperTest {
protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::ALPHAKEY);
+ }
const std::string UNIQUE_ID = "local:0";
const KeyboardLayoutInfo DEVICE_KEYBOARD_LAYOUT_INFO = KeyboardLayoutInfo("en-US", "qwerty");
void prepareDisplay(ui::Rotation orientation);
void testDPadKeyRotation(KeyboardInputMapper& mapper, int32_t originalScanCode,
int32_t originalKeyCode, int32_t rotatedKeyCode,
- int32_t displayId = ADISPLAY_ID_NONE);
+ ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID);
};
/* Similar to setDisplayInfoAndReconfigure, but pre-populates all parameters except for the
@@ -3264,7 +3337,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);
@@ -3284,8 +3358,7 @@
TEST_F(KeyboardInputMapperTest, GetSources) {
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
ASSERT_EQ(AINPUT_SOURCE_KEYBOARD, mapper.getSources());
}
@@ -3300,8 +3373,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3401,8 +3473,7 @@
mFakeEventHub->addKeyRemapping(EVENTHUB_ID, AKEYCODE_A, AKEYCODE_B);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Key down by scan code.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_A, 1);
@@ -3423,8 +3494,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
NotifyKeyArgs args;
// Key down
@@ -3446,8 +3516,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, 0, KEY_SCROLLLOCK, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3487,8 +3556,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
prepareDisplay(ui::ROTATION_90);
ASSERT_NO_FATAL_FAILURE(testDPadKeyRotation(mapper,
@@ -3509,8 +3577,7 @@
addConfigurationProperty("keyboard.orientationAware", "1");
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
prepareDisplay(ui::ROTATION_0);
ASSERT_NO_FATAL_FAILURE(
@@ -3581,23 +3648,22 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_UP, 0, AKEYCODE_DPAD_UP, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
NotifyKeyArgs args;
- // Display id should be ADISPLAY_ID_NONE without any display configuration.
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
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::LogicalDisplayId::INVALID, 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::LogicalDisplayId::INVALID, args.displayId);
}
TEST_F(KeyboardInputMapperTest, DisplayIdConfigurationChange_OrientationAware) {
@@ -3607,11 +3673,10 @@
addConfigurationProperty("keyboard.orientationAware", "1");
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
NotifyKeyArgs args;
- // Display id should be ADISPLAY_ID_NONE without any display configuration.
+ // Display id should be LogicalDisplayId::INVALID without any display configuration.
// ^--- already checked by the previous test
setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
@@ -3622,7 +3687,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);
@@ -3635,8 +3700,7 @@
TEST_F(KeyboardInputMapperTest, GetKeyCodeState) {
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
mFakeEventHub->setKeyCodeState(EVENTHUB_ID, AKEYCODE_A, 1);
ASSERT_EQ(1, mapper.getKeyCodeState(AINPUT_SOURCE_ANY, AKEYCODE_A));
@@ -3647,8 +3711,7 @@
TEST_F(KeyboardInputMapperTest, GetKeyCodeForKeyLocation) {
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
mFakeEventHub->addKeyCodeMapping(EVENTHUB_ID, AKEYCODE_Y, AKEYCODE_Z);
ASSERT_EQ(AKEYCODE_Z, mapper.getKeyCodeForKeyLocation(AKEYCODE_Y))
@@ -3660,8 +3723,7 @@
TEST_F(KeyboardInputMapperTest, GetScanCodeState) {
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
mFakeEventHub->setScanCodeState(EVENTHUB_ID, KEY_A, 1);
ASSERT_EQ(1, mapper.getScanCodeState(AINPUT_SOURCE_ANY, KEY_A));
@@ -3672,8 +3734,7 @@
TEST_F(KeyboardInputMapperTest, MarkSupportedKeyCodes) {
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
mFakeEventHub->addKey(EVENTHUB_ID, KEY_A, 0, AKEYCODE_A, 0);
@@ -3692,8 +3753,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3758,8 +3818,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, BTN_Y, 0, AKEYCODE_BUTTON_Y, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Meta state should be AMETA_NONE after reset
std::list<NotifyArgs> unused = mapper.reset(ARBITRARY_TIME);
@@ -3808,16 +3867,14 @@
mFakeEventHub->addKey(SECOND_EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
device2->addEmptyEventHubDevice(SECOND_EVENTHUB_ID);
KeyboardInputMapper& mapper2 =
device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
mFakePolicy
->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
/*changes=*/{});
@@ -3837,7 +3894,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,
@@ -3879,8 +3936,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3930,8 +3986,7 @@
device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
mFakePolicy
->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
/*changes=*/{});
@@ -3950,11 +4005,9 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
// Suppose we have two mappers. (DPAD + KEYBOARD)
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD,
- AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Initial metastate is AMETA_NONE.
ASSERT_EQ(AMETA_NONE, mapper.getMetaState());
@@ -3972,8 +4025,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, KEY_SCROLLLOCK, 0, AKEYCODE_SCROLL_LOCK, 0);
KeyboardInputMapper& mapper1 =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// keyboard 2.
const std::string USB2 = "USB2";
@@ -3995,8 +4047,7 @@
device2->constructAndAddMapper<KeyboardInputMapper>(SECOND_EVENTHUB_ID,
mFakePolicy
->getReaderConfiguration(),
- AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
device2->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
/*changes=*/{});
@@ -4052,8 +4103,7 @@
mFakeEventHub->addKey(EVENTHUB_ID, 0, USAGE_A, AKEYCODE_A, POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
// Key down by scan code.
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
@@ -4078,8 +4128,7 @@
}
TEST_F(KeyboardInputMapperTest, Configure_AssignKeyboardLayoutInfo) {
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
std::list<NotifyArgs> unused =
mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
/*changes=*/{});
@@ -4110,8 +4159,7 @@
RawLayoutInfo{.languageTag = "en", .layoutType = "extended"});
// Configuration
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
InputReaderConfiguration config;
std::list<NotifyArgs> unused = mDevice->configure(ARBITRARY_TIME, config, /*changes=*/{});
@@ -4122,8 +4170,7 @@
TEST_F(KeyboardInputMapperTest, Process_GesureEventToSetFlagKeepTouchMode) {
mFakeEventHub->addKey(EVENTHUB_ID, KEY_LEFT, 0, AKEYCODE_DPAD_LEFT, POLICY_FLAG_GESTURE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
NotifyKeyArgs args;
// Key down
@@ -4132,14 +4179,72 @@
ASSERT_EQ(AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_KEEP_TOUCH_MODE, args.flags);
}
-// --- KeyboardInputMapperTest_ExternalDevice ---
+/**
+ * When there is more than one KeyboardInputMapper for an InputDevice, each mapper should produce
+ * events that use the shared keyboard source across all mappers. This is to ensure that each
+ * input device generates key events in a consistent manner, regardless of which mapper produces
+ * the event.
+ */
+TEST_F(KeyboardInputMapperTest, UsesSharedKeyboardSource) {
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
-class KeyboardInputMapperTest_ExternalDevice : public InputMapperTest {
+ // Add a mapper with SOURCE_KEYBOARD
+ KeyboardInputMapper& keyboardMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
+
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+ process(keyboardMapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyKeyWasCalled(WithSource(AINPUT_SOURCE_KEYBOARD)));
+
+ // Add a mapper with SOURCE_DPAD
+ KeyboardInputMapper& dpadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_DPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD)));
+ }
+
+ // Add a mapper with SOURCE_GAMEPAD
+ KeyboardInputMapper& gamepadMapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_GAMEPAD);
+ for (auto* mapper : {&keyboardMapper, &dpadMapper, &gamepadMapper}) {
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ process(*mapper, ARBITRARY_TIME, 0, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(
+ WithSource(AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD)));
+ }
+}
+
+// --- KeyboardInputMapperTest_ExternalAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalAlphabeticDevice : public InputMapperTest {
protected:
- void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::ALPHAKEY | InputDeviceClass::EXTERNAL);
+ }
};
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) {
+// --- KeyboardInputMapperTest_ExternalNonAlphabeticDevice ---
+
+class KeyboardInputMapperTest_ExternalNonAlphabeticDevice : public InputMapperTest {
+protected:
+ void SetUp() override {
+ InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::KEYBOARD |
+ InputDeviceClass::EXTERNAL);
+ }
+};
+
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, WakeBehavior_AlphabeticKeyboard) {
// For external devices, keys will trigger wake on key down. Media keys should also trigger
// wake if triggered from external devices.
@@ -4149,8 +4254,7 @@
POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
@@ -4178,7 +4282,7 @@
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) {
+TEST_F(KeyboardInputMapperTest_ExternalNonAlphabeticDevice, WakeBehavior_NonAlphabeticKeyboard) {
// For external devices, keys will trigger wake on key down. Media keys should not trigger
// wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
@@ -4187,8 +4291,7 @@
POLICY_FLAG_WAKE);
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
NotifyKeyArgs args;
@@ -4208,7 +4311,7 @@
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
-TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
+TEST_F(KeyboardInputMapperTest_ExternalAlphabeticDevice, DoNotWakeByDefaultBehavior) {
// Tv Remote key's wake behavior is prescribed by the keylayout file.
mFakeEventHub->addKey(EVENTHUB_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
@@ -4217,8 +4320,7 @@
addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
KeyboardInputMapper& mapper =
- constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD);
process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_HOME, 1);
NotifyKeyArgs args;
@@ -5344,10 +5446,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);
@@ -5419,6 +5517,9 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, tool, tool, tool, tool, orientation, distance));
ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT));
+ ASSERT_EQ(args.flags,
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION |
+ AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION);
}
TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) {
@@ -6192,52 +6293,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");
@@ -7907,6 +7962,7 @@
ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor,
orientation, distance));
+ ASSERT_EQ(args.flags, AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION);
}
TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) {
@@ -8717,21 +8773,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 +8787,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::LogicalDisplayId::INVALID, motionArgs.displayId);
}
/**
@@ -8920,97 +8967,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 +9002,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 +9027,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 +9659,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 +9684,7 @@
processPosition(mapper, 100, 100);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(ADISPLAY_ID_DEFAULT, motionArgs.displayId);
+ ASSERT_EQ(ui::LogicalDisplayId::DEFAULT, motionArgs.displayId);
// Expect the event to be sent to the external viewport if it is present.
prepareSecondaryDisplay(ViewportType::EXTERNAL);
@@ -9790,168 +9694,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(true);
- 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(false);
- 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 +9755,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(false);
+ 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(true);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- ASSERT_EQ(AINPUT_SOURCE_TOUCHPAD, mapper.getSources());
}
// --- BluetoothMultiTouchInputMapperTest ---
@@ -10080,19 +9822,14 @@
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);
prepareAbsoluteAxisResolution(xAxisResolution, yAxisResolution);
// In order to enable swipe and freeform gesture in pointer mode, pointer capture
// needs to be disabled, and the pointer gesture needs to be enabled.
- mFakePolicy->setPointerCapture(false);
+ 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 +10335,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",
@@ -10872,24 +10631,24 @@
ASSERT_EQ(controller.getLightColor(lights[0].id).value_or(-1), LIGHT_COLOR);
}
-TEST_F(LightControllerTest, PlayerIdLight) {
+TEST_F(LightControllerTest, SonyPlayerIdLight) {
RawLightInfo info1 = {.id = 1,
- .name = "player1",
+ .name = "sony1",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info2 = {.id = 2,
- .name = "player2",
+ .name = "sony2",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info3 = {.id = 3,
- .name = "player3",
+ .name = "sony3",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
RawLightInfo info4 = {.id = 4,
- .name = "player4",
+ .name = "sony4",
.maxBrightness = 255,
.flags = InputLightClass::BRIGHTNESS,
.path = ""};
@@ -10903,6 +10662,49 @@
controller.populateDeviceInfo(&info);
std::vector<InputDeviceLightInfo> lights = info.getLights();
ASSERT_EQ(1U, lights.size());
+ ASSERT_STREQ("sony", lights[0].name.c_str());
+ ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
+ ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
+ ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
+
+ ASSERT_FALSE(controller.setLightColor(lights[0].id, LIGHT_COLOR));
+ ASSERT_TRUE(controller.setLightPlayerId(lights[0].id, LIGHT_PLAYER_ID));
+ ASSERT_EQ(controller.getLightPlayerId(lights[0].id).value_or(-1), LIGHT_PLAYER_ID);
+ ASSERT_STREQ("sony", lights[0].name.c_str());
+}
+
+TEST_F(LightControllerTest, PlayerIdLight) {
+ RawLightInfo info1 = {.id = 1,
+ .name = "player-1",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info2 = {.id = 2,
+ .name = "player-2",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info3 = {.id = 3,
+ .name = "player-3",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ RawLightInfo info4 = {.id = 4,
+ .name = "player-4",
+ .maxBrightness = 255,
+ .flags = InputLightClass::BRIGHTNESS,
+ .path = ""};
+ mFakeEventHub->addRawLightInfo(info1.id, std::move(info1));
+ mFakeEventHub->addRawLightInfo(info2.id, std::move(info2));
+ mFakeEventHub->addRawLightInfo(info3.id, std::move(info3));
+ mFakeEventHub->addRawLightInfo(info4.id, std::move(info4));
+
+ PeripheralController& controller = addControllerAndConfigure<PeripheralController>();
+ InputDeviceInfo info;
+ controller.populateDeviceInfo(&info);
+ std::vector<InputDeviceLightInfo> lights = info.getLights();
+ ASSERT_EQ(1U, lights.size());
+ ASSERT_STREQ("player", lights[0].name.c_str());
ASSERT_EQ(InputDeviceLightType::PLAYER_ID, lights[0].type);
ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS));
ASSERT_FALSE(lights[0].capabilityFlags.test(InputDeviceLightCapability::RGB));
diff --git a/services/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp
new file mode 100644
index 0000000..db4e761
--- /dev/null
+++ b/services/inputflinger/tests/InputTraceSession.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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 <perfetto/trace/android/android_input_event.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions_impl.pbzero.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;
+using perfetto::protos::pbzero::WinscopeExtensions;
+using perfetto::protos::pbzero::WinscopeExtensionsImpl;
+
+// 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()) {
+ for (auto it = trace.packet(); it; it++) {
+ TracePacket::Decoder packet{it->as_bytes()};
+ if (!packet.has_winscope_extensions()) {
+ continue;
+ }
+
+ WinscopeExtensions::Decoder extensions{packet.winscope_extensions()};
+ const auto& field =
+ extensions.Get(WinscopeExtensionsImpl::kAndroidInputEventFieldNumber);
+ if (!field.valid()) {
+ continue;
+ }
+
+ EXPECT_TRUE(packet.has_timestamp());
+ EXPECT_TRUE(packet.has_timestamp_clock_id());
+ EXPECT_EQ(packet.timestamp_clock_id(),
+ static_cast<uint32_t>(perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC));
+
+ AndroidInputEvent::Decoder event{field.as_bytes()};
+ 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);
+ }
+ }
+ }
+ 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..bda5521
--- /dev/null
+++ b/services/inputflinger/tests/InputTraceSession.h
@@ -0,0 +1,84 @@
+/*
+ * 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/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..2ccd93e
--- /dev/null
+++ b/services/inputflinger/tests/InputTracingTest.cpp
@@ -0,0 +1,751 @@
+/*
+ * 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/android/winscope_extensions.pbzero.h>
+#include <perfetto/trace/android/winscope_extensions_impl.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::LogicalDisplayId::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..8a15d07 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -26,9 +26,11 @@
#include <vector>
#include <EventHub.h>
+#include <InputDevice.h>
#include <InputReaderBase.h>
+#include <InputReaderContext.h>
#include <NotifyArgs.h>
-#include <PointerControllerInterface.h>
+#include <PointerChoreographerPolicyInterface.h>
#include <StylusState.h>
#include <VibrationElement.h>
#include <android-base/logging.h>
@@ -37,6 +39,7 @@
#include <input/InputDevice.h>
#include <input/KeyCharacterMap.h>
#include <input/KeyLayoutMap.h>
+#include <input/KeyboardClassifier.h>
#include <input/PropertyMap.h>
#include <input/TouchVideoFrame.h>
#include <input/VirtualKeyMap.h>
@@ -54,14 +57,10 @@
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; }
- MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo> & outDevices),
+ MOCK_METHOD(void, getExternalStylusDevices, (std::vector<InputDeviceInfo>& outDevices),
(override));
MOCK_METHOD(std::list<NotifyArgs>, dispatchExternalStylusState, (const StylusState& outState),
(override));
@@ -80,8 +79,11 @@
MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when));
MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ());
+ KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; };
+
private:
int32_t mGeneration = 0;
+ std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>();
};
class MockEventHubInterface : public EventHubInterface {
@@ -171,7 +173,7 @@
MOCK_METHOD(void, requestReopenDevices, (), (override));
MOCK_METHOD(void, wake, (), (override));
- MOCK_METHOD(void, dump, (std::string & dump), (const, override));
+ MOCK_METHOD(void, dump, (std::string& dump), (const, override));
MOCK_METHOD(void, monitor, (), (const, override));
MOCK_METHOD(bool, isDeviceEnabled, (int32_t deviceId), (const, override));
MOCK_METHOD(status_t, enableDevice, (int32_t deviceId), (override));
@@ -179,4 +181,84 @@
MOCK_METHOD(void, sysfsNodeChanged, (const std::string& sysfsNodePath), (override));
};
+class MockPointerChoreographerPolicyInterface : public PointerChoreographerPolicyInterface {
+public:
+ MOCK_METHOD(std::shared_ptr<PointerControllerInterface>, createPointerController,
+ (PointerControllerInterface::ControllerType), (override));
+ MOCK_METHOD(void, notifyPointerDisplayIdChanged,
+ (ui::LogicalDisplayId displayId, const FloatPoint& position), (override));
+ MOCK_METHOD(bool, isInputMethodConnectionActive, (), (override));
+};
+
+class MockInputDevice : public InputDevice {
+public:
+ MockInputDevice(InputReaderContext* context, int32_t id, int32_t generation,
+ const InputDeviceIdentifier& identifier)
+ : InputDevice(context, id, generation, identifier) {}
+
+ MOCK_METHOD(uint32_t, getSources, (), (const, override));
+ MOCK_METHOD(bool, isEnabled, (), ());
+
+ MOCK_METHOD(void, dump, (std::string& dump, const std::string& eventHubDevStr), ());
+ MOCK_METHOD(void, addEmptyEventHubDevice, (int32_t eventHubId), ());
+ MOCK_METHOD(std::list<NotifyArgs>, addEventHubDevice,
+ (nsecs_t when, int32_t eventHubId, const InputReaderConfiguration& readerConfig),
+ ());
+ MOCK_METHOD(void, removeEventHubDevice, (int32_t eventHubId), ());
+ MOCK_METHOD(std::list<NotifyArgs>, configure,
+ (nsecs_t when, const InputReaderConfiguration& readerConfig,
+ ConfigurationChanges changes),
+ ());
+ MOCK_METHOD(std::list<NotifyArgs>, reset, (nsecs_t when), ());
+ MOCK_METHOD(std::list<NotifyArgs>, process, (const RawEvent* rawEvents, size_t count), ());
+ MOCK_METHOD(std::list<NotifyArgs>, timeoutExpired, (nsecs_t when), ());
+ MOCK_METHOD(std::list<NotifyArgs>, updateExternalStylusState, (const StylusState& state), ());
+
+ MOCK_METHOD(InputDeviceInfo, getDeviceInfo, (), ());
+ MOCK_METHOD(int32_t, getKeyCodeState, (uint32_t sourceMask, int32_t keyCode), ());
+ MOCK_METHOD(int32_t, getScanCodeState, (uint32_t sourceMask, int32_t scanCode), ());
+ MOCK_METHOD(int32_t, getSwitchState, (uint32_t sourceMask, int32_t switchCode), ());
+ MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t locationKeyCode), (const));
+ MOCK_METHOD(bool, markSupportedKeyCodes,
+ (uint32_t sourceMask, const std::vector<int32_t>& keyCodes, uint8_t* outFlags), ());
+ MOCK_METHOD(std::list<NotifyArgs>, vibrate,
+ (const VibrationSequence& sequence, ssize_t repeat, int32_t token), ());
+ MOCK_METHOD(std::list<NotifyArgs>, cancelVibrate, (int32_t token), ());
+ MOCK_METHOD(bool, isVibrating, (), ());
+ MOCK_METHOD(std::vector<int32_t>, getVibratorIds, (), ());
+ MOCK_METHOD(std::list<NotifyArgs>, cancelTouch, (nsecs_t when, nsecs_t readTime), ());
+ MOCK_METHOD(bool, enableSensor,
+ (InputDeviceSensorType sensorType, std::chrono::microseconds samplingPeriod,
+ std::chrono::microseconds maxBatchReportLatency),
+ ());
+
+ MOCK_METHOD(void, disableSensor, (InputDeviceSensorType sensorType), ());
+ MOCK_METHOD(void, flushSensor, (InputDeviceSensorType sensorType), ());
+
+ MOCK_METHOD(std::optional<int32_t>, getBatteryEventHubId, (), (const));
+
+ MOCK_METHOD(bool, setLightColor, (int32_t lightId, int32_t color), ());
+ MOCK_METHOD(bool, setLightPlayerId, (int32_t lightId, int32_t playerId), ());
+ MOCK_METHOD(std::optional<int32_t>, getLightColor, (int32_t lightId), ());
+ MOCK_METHOD(std::optional<int32_t>, getLightPlayerId, (int32_t lightId), ());
+
+ MOCK_METHOD(int32_t, getMetaState, (), ());
+ MOCK_METHOD(void, updateMetaState, (int32_t keyCode), ());
+
+ MOCK_METHOD(void, addKeyRemapping, (int32_t fromKeyCode, int32_t toKeyCode), ());
+
+ MOCK_METHOD(void, setKeyboardType, (KeyboardType keyboardType), ());
+
+ MOCK_METHOD(void, bumpGeneration, (), ());
+
+ MOCK_METHOD(const PropertyMap&, getConfiguration, (), (const, override));
+
+ MOCK_METHOD(NotifyDeviceResetArgs, notifyReset, (nsecs_t when), ());
+
+ MOCK_METHOD(std::optional<ui::LogicalDisplayId>, getAssociatedDisplayId, (), ());
+
+ MOCK_METHOD(void, updateLedState, (bool reset), ());
+
+ MOCK_METHOD(size_t, getMapperCount, (), ());
+};
} // namespace android
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index b44529b..4d322e9 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -55,7 +55,6 @@
void SetUp() override {
InputMapperUnitTest::SetUp();
- createDevice();
// set key-codes expected in tests
for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
@@ -66,25 +65,14 @@
mFakePolicy = sp<FakeInputReaderPolicy>::make();
EXPECT_CALL(mMockInputReaderContext, getPolicy).WillRepeatedly(Return(mFakePolicy.get()));
- mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
- AINPUT_SOURCE_KEYBOARD,
- AINPUT_KEYBOARD_TYPE_ALPHABETIC);
- }
+ ON_CALL((*mDevice), getSources).WillByDefault(Return(AINPUT_SOURCE_KEYBOARD));
- 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);
- process(EV_KEY, keyCode, 0);
- process(EV_SYN, SYN_REPORT, 0);
- }
+ mMapper = createInputMapper<KeyboardInputMapper>(*mDeviceContext, mReaderConfiguration,
+ AINPUT_SOURCE_KEYBOARD);
}
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());
@@ -99,42 +87,6 @@
};
/**
- * Pointer visibility should remain unaffected if there is no active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDoesNotHidePointer) {
- testPointerVisibilityForKeys({KEY_0, KEY_A, KEY_LEFTCTRL}, /* expectVisible= */ true);
-}
-
-/**
- * Pointer should hide if there is a active Input Method Connection
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithIMeConnectionHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
-}
-
-/**
- * Pointer should still hide if touchpad taps are already disabled
- */
-TEST_F(KeyboardInputMapperUnitTest, AlphanumericKeystrokesWithTouchpadTapDisabledHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- EXPECT_CALL(mMockInputReaderContext, isPreventingTouchpadTaps).WillRepeatedly(Return(true));
- testPointerVisibilityForKeys({KEY_0, KEY_A}, /* expectVisible= */ false);
-}
-
-/**
- * Pointer visibility should remain unaffected by meta keys even if Input Method Connection is
- * active
- */
-TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDoesNotHidePointer) {
- mFakePolicy->setIsInputMethodConnectionActive(true);
- std::vector<int32_t> metaKeys{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTSHIFT, KEY_RIGHTSHIFT,
- KEY_FN, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA,
- KEY_RIGHTMETA, KEY_CAPSLOCK, KEY_NUMLOCK, KEY_SCROLLLOCK};
- testPointerVisibilityForKeys(metaKeys, /* expectVisible= */ true);
-}
-
-/**
* Touchpad tap should not be disabled if there is no active Input Method Connection
*/
TEST_F(KeyboardInputMapperUnitTest, KeystrokesWithoutIMeConnectionDontDisableTouchpadTap) {
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index 6606de8..4fcffdd 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::LogicalDisplayId::INVALID);
return info;
}
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
index d726385..c57c251 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::LogicalDisplayId::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
@@ -112,7 +112,6 @@
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
/*isActive=*/true, "local:0", NO_PORT,
ViewportType::INTERNAL);
- createDevice();
mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
mFakePolicy->getReaderConfiguration());
}
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
index 5e67506..9ddb8c1 100644
--- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -23,10 +23,7 @@
protected:
static constexpr size_t SLOT_COUNT = 8;
- void SetUp() override {
- InputMapperUnitTest::SetUp();
- createDevice();
- }
+ void SetUp() override { InputMapperUnitTest::SetUp(); }
MultiTouchMotionAccumulator mMotionAccumulator;
@@ -38,7 +35,7 @@
event.type = type;
event.code = code;
event.value = value;
- mMotionAccumulator.process(&event);
+ mMotionAccumulator.process(event);
}
};
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 b9c685e..9a5b6a7 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -15,18 +15,22 @@
*/
#include "../PointerChoreographer.h"
-
+#include <com_android_input_flags.h>
+#include <flag_macros.h>
#include <gtest/gtest.h>
#include <deque>
#include <vector>
#include "FakePointerController.h"
+#include "InterfaceMocks.h"
#include "NotifyArgsBuilders.h"
#include "TestEventMatchers.h"
#include "TestInputListener.h"
namespace android {
+namespace input_flags = com::android::input::flags;
+
using ControllerType = PointerControllerInterface::ControllerType;
using testing::AllOf;
@@ -42,8 +46,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 +64,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 +74,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;
@@ -85,10 +90,55 @@
// --- PointerChoreographerTest ---
-class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface {
+class TestPointerChoreographer : public PointerChoreographer {
+public:
+ TestPointerChoreographer(InputListenerInterface& inputListener,
+ PointerChoreographerPolicyInterface& policy,
+ sp<gui::WindowInfosListener>& windowInfoListener,
+ const std::vector<gui::WindowInfo>& mInitialWindowInfos);
+};
+
+TestPointerChoreographer::TestPointerChoreographer(
+ InputListenerInterface& inputListener, PointerChoreographerPolicyInterface& policy,
+ sp<gui::WindowInfosListener>& windowInfoListener,
+ const std::vector<gui::WindowInfo>& mInitialWindowInfos)
+ : PointerChoreographer(
+ inputListener, policy,
+ [&windowInfoListener,
+ &mInitialWindowInfos](const sp<android::gui::WindowInfosListener>& listener) {
+ windowInfoListener = listener;
+ return mInitialWindowInfos;
+ },
+ [&windowInfoListener](const sp<android::gui::WindowInfosListener>& listener) {
+ windowInfoListener = nullptr;
+ }) {}
+
+class PointerChoreographerTest : public testing::Test {
protected:
TestInputListener mTestListener;
- PointerChoreographer mChoreographer{mTestListener, *this};
+ sp<gui::WindowInfosListener> mRegisteredWindowInfoListener;
+ std::vector<gui::WindowInfo> mInjectedInitialWindowInfos;
+ testing::NiceMock<MockPointerChoreographerPolicyInterface> mMockPolicy;
+ TestPointerChoreographer mChoreographer{mTestListener, mMockPolicy,
+ mRegisteredWindowInfoListener,
+ mInjectedInitialWindowInfos};
+
+ void SetUp() override {
+ // flag overrides
+ input_flags::hide_pointer_indicators_for_secure_windows(true);
+
+ ON_CALL(mMockPolicy, createPointerController).WillByDefault([this](ControllerType type) {
+ std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
+ EXPECT_FALSE(pc->isPointerShown());
+ mCreatedControllers.emplace_back(type, pc);
+ return pc;
+ });
+
+ ON_CALL(mMockPolicy, notifyPointerDisplayIdChanged)
+ .WillByDefault([this](ui::LogicalDisplayId displayId, const FloatPoint& position) {
+ mPointerDisplayIdNotified = displayId;
+ });
+ }
std::shared_ptr<FakePointerController> assertPointerControllerCreated(
ControllerType expectedType) {
@@ -120,29 +170,27 @@
"reference to this PointerController";
}
- void assertPointerDisplayIdNotified(int32_t displayId) {
+ void assertPointerDisplayIdNotified(ui::LogicalDisplayId displayId) {
ASSERT_EQ(displayId, mPointerDisplayIdNotified);
mPointerDisplayIdNotified.reset();
}
void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); }
+ void assertWindowInfosListenerRegistered() {
+ ASSERT_NE(nullptr, mRegisteredWindowInfoListener)
+ << "WindowInfosListener was not registered";
+ }
+
+ void assertWindowInfosListenerNotRegistered() {
+ ASSERT_EQ(nullptr, mRegisteredWindowInfoListener)
+ << "WindowInfosListener was not unregistered";
+ }
+
private:
std::deque<std::pair<ControllerType, std::shared_ptr<FakePointerController>>>
mCreatedControllers;
- std::optional<int32_t> mPointerDisplayIdNotified;
-
- std::shared_ptr<PointerControllerInterface> createPointerController(
- ControllerType type) override {
- std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>();
- EXPECT_FALSE(pc->isPointerShown());
- mCreatedControllers.emplace_back(type, pc);
- return pc;
- }
-
- void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override {
- mPointerDisplayIdNotified = displayId;
- }
+ std::optional<ui::LogicalDisplayId> mPointerDisplayIdNotified;
};
TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) {
@@ -197,13 +245,17 @@
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::LogicalDisplayId::INVALID)}});
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the mouse.
@@ -214,7 +266,8 @@
TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) {
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
- {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}});
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD,
+ ui::LogicalDisplayId::INVALID)}});
assertPointerControllerNotCreated();
}
@@ -248,7 +301,9 @@
// 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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
ASSERT_TRUE(pc->isPointerShown());
@@ -260,7 +315,9 @@
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::LogicalDisplayId::INVALID)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
ASSERT_TRUE(firstDisplayPc->isPointerShown());
@@ -279,7 +336,9 @@
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::LogicalDisplayId::INVALID)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -288,7 +347,9 @@
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::LogicalDisplayId::INVALID)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -300,12 +361,14 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerDisplayIdNotified(ui::LogicalDisplayId::INVALID);
assertPointerControllerRemoved(pc);
}
@@ -316,7 +379,9 @@
// 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::LogicalDisplayId::INVALID)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -332,7 +397,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -344,7 +411,7 @@
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(MOUSE_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::LogicalDisplayId::INVALID)
.build());
// Check that the PointerController updated the position and the pointer is shown.
@@ -356,6 +423,40 @@
AllOf(WithCoords(110, 220), WithDisplayId(DISPLAY_ID), WithCursorPosition(110, 220)));
}
+TEST_F(PointerChoreographerTest, AbsoluteMouseMovesPointerAndReturnsNewArgs) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+ mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID);
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
+
+ // Set initial position of the PointerController.
+ pc->setPosition(100, 200);
+ const auto absoluteMousePointer = PointerBuilder(/*id=*/0, ToolType::MOUSE)
+ .axis(AMOTION_EVENT_AXIS_X, 110)
+ .axis(AMOTION_EVENT_AXIS_Y, 220);
+
+ // Make NotifyMotionArgs and notify Choreographer.
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(absoluteMousePointer)
+ .deviceId(DEVICE_ID)
+ .displayId(ui::LogicalDisplayId::INVALID)
+ .build());
+
+ // Check that the PointerController updated the position and the pointer is shown.
+ pc->assertPosition(110, 220);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ // Check that x-y coordinates, displayId and cursor position are correctly updated.
+ mTestListener.assertNotifyMotionWasCalled(
+ AllOf(WithCoords(110, 220), WithRelativeMotion(10, 20), WithDisplayId(DISPLAY_ID),
+ WithCursorPosition(110, 220)));
+}
+
TEST_F(PointerChoreographerTest,
AssociatedMouseMovesPointerOnAssociatedDisplayAndDoesNotMovePointerOnDefaultDisplay) {
// Add two displays and set one to default.
@@ -365,7 +466,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::LogicalDisplayId::INVALID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, unassociatedMousePc->getDisplayId());
@@ -401,7 +502,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -411,10 +514,12 @@
// 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::LogicalDisplayId::INVALID)}});
mChoreographer.notifyPointerCaptureChanged(
NotifyPointerCaptureChangedArgs(/*id=*/2, systemTime(SYSTEM_TIME_MONOTONIC),
- PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+ /*seq=*/0)));
// Notify motion as if pointer capture is enabled.
mChoreographer.notifyMotion(
@@ -425,7 +530,7 @@
.axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10)
.axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20))
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::LogicalDisplayId::INVALID)
.build());
// Check that there's no update on the PointerController.
@@ -434,7 +539,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::LogicalDisplayId::INVALID),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION)));
}
@@ -443,7 +549,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -451,7 +559,8 @@
// Enable pointer capture and check if the PointerController hid the pointer.
mChoreographer.notifyPointerCaptureChanged(
NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
- PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+ /*seq=*/0)));
ASSERT_FALSE(pc->isPointerShown());
}
@@ -461,7 +570,9 @@
// 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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
@@ -471,15 +582,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::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
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::LogicalDisplayId::INVALID)}});
assertPointerControllerNotRemoved(pc);
ASSERT_TRUE(pc->isPointerShown());
@@ -492,7 +605,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_TRUE(pc->isPointerShown());
@@ -502,8 +617,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::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID,
+ AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS,
DISPLAY_ID)}});
ASSERT_FALSE(pc->isPointerShown());
@@ -514,6 +630,75 @@
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::LogicalDisplayId::INVALID);
+ // 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::LogicalDisplayId::INVALID);
+
+ 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::LogicalDisplayId::INVALID);
+ disabledMouseDeviceInfo.setEnabled(false);
+
+ InputDeviceInfo enabledMouseDeviceInfo =
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID);
+
+ InputDeviceInfo anotherEnabledMouseDeviceInfo =
+ generateTestDeviceInfo(THIRD_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID);
+
+ 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);
@@ -1052,7 +1237,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
assertPointerControllerCreated(ControllerType::MOUSE);
}
@@ -1060,7 +1245,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
// Remove the touchpad.
@@ -1101,7 +1286,9 @@
// 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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertViewportSet(DISPLAY_ID);
}
@@ -1114,7 +1301,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
firstDisplayPc->assertViewportSet(DISPLAY_ID);
@@ -1132,7 +1319,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
@@ -1143,7 +1330,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotNotified();
@@ -1157,12 +1344,12 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}});
- assertPointerDisplayIdNotified(ADISPLAY_ID_NONE);
+ assertPointerDisplayIdNotified(ui::LogicalDisplayId::INVALID);
assertPointerControllerRemoved(pc);
}
@@ -1176,12 +1363,12 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE);
assertPointerDisplayIdNotified(DISPLAY_ID);
- // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified
- // before a touchpad event.
+ // Set another viewport as a default mouse display ID. ui::LogicalDisplayId::INVALID will be
+ // notified before a touchpad event.
mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID);
assertPointerControllerRemoved(firstDisplayPc);
@@ -1195,7 +1382,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1207,7 +1394,7 @@
MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE)
.pointer(TOUCHPAD_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::LogicalDisplayId::INVALID)
.build());
// Check that the PointerController updated the position and the pointer is shown.
@@ -1225,7 +1412,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1239,7 +1426,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::LogicalDisplayId::INVALID)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
@@ -1253,7 +1440,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::LogicalDisplayId::INVALID)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -1270,7 +1457,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::LogicalDisplayId::INVALID)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_DOWN |
@@ -1285,7 +1472,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::LogicalDisplayId::INVALID)
.build());
mTestListener.assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
@@ -1304,7 +1491,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE),
+ ui::LogicalDisplayId::INVALID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
ANOTHER_DISPLAY_ID)}});
auto unassociatedMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
@@ -1343,7 +1530,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
@@ -1353,13 +1540,14 @@
// Assume that pointer capture is enabled.
mChoreographer.notifyPointerCaptureChanged(
NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
- PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+ /*seq=*/0)));
// Notify motion as if pointer capture is enabled.
mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHPAD)
.pointer(FIRST_TOUCH_POINTER)
.deviceId(DEVICE_ID)
- .displayId(ADISPLAY_ID_NONE)
+ .displayId(ui::LogicalDisplayId::INVALID)
.build());
// Check that there's no update on the PointerController.
@@ -1368,7 +1556,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::LogicalDisplayId::INVALID),
WithCursorPosition(AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION)));
}
@@ -1379,7 +1567,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, pc->getDisplayId());
ASSERT_TRUE(pc->isPointerShown());
@@ -1387,7 +1575,8 @@
// Enable pointer capture and check if the PointerController hid the pointer.
mChoreographer.notifyPointerCaptureChanged(
NotifyPointerCaptureChangedArgs(/*id=*/1, systemTime(SYSTEM_TIME_MONOTONIC),
- PointerCaptureRequest(/*enable=*/true, /*seq=*/0)));
+ PointerCaptureRequest(/*window=*/sp<BBinder>::make(),
+ /*seq=*/0)));
ASSERT_FALSE(pc->isPointerShown());
}
@@ -1396,7 +1585,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1410,7 +1601,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1425,7 +1618,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertPointerIconNotSet();
@@ -1440,7 +1635,9 @@
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::LogicalDisplayId::INVALID)}});
auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
pc->assertCustomPointerIconNotSet();
@@ -1463,7 +1660,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::LogicalDisplayId::INVALID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
@@ -1482,6 +1679,254 @@
firstMousePc->assertPointerIconNotSet();
}
+using SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam =
+ std::tuple<std::string_view /*name*/, uint32_t /*source*/, ControllerType, PointerBuilder,
+ std::function<void(PointerChoreographer&)>, int32_t /*action*/>;
+
+class SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture
+ : public PointerChoreographerTest,
+ public ::testing::WithParamInterface<
+ SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam> {
+protected:
+ void initializePointerDevice(const PointerBuilder& pointerBuilder, const uint32_t source,
+ const std::function<void(PointerChoreographer&)> onControllerInit,
+ const int32_t action) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Add appropriate pointer device
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, source, DISPLAY_ID)}});
+ onControllerInit(mChoreographer);
+
+ // Emit input events to create PointerController
+ mChoreographer.notifyMotion(MotionArgsBuilder(action, source)
+ .pointer(pointerBuilder)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ }
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ PointerChoreographerTest, SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ ::testing::Values(
+ std::make_tuple(
+ "TouchSpots", AINPUT_SOURCE_TOUCHSCREEN, ControllerType::TOUCH,
+ FIRST_TOUCH_POINTER,
+ [](PointerChoreographer& pc) { pc.setShowTouchesEnabled(true); },
+ AMOTION_EVENT_ACTION_DOWN),
+ std::make_tuple(
+ "Mouse", AINPUT_SOURCE_MOUSE, ControllerType::MOUSE, MOUSE_POINTER,
+ [](PointerChoreographer& pc) {}, AMOTION_EVENT_ACTION_DOWN),
+ std::make_tuple(
+ "Stylus", AINPUT_SOURCE_STYLUS, ControllerType::STYLUS, STYLUS_POINTER,
+ [](PointerChoreographer& pc) { pc.setStylusPointerIconEnabled(true); },
+ AMOTION_EVENT_ACTION_HOVER_ENTER),
+ std::make_tuple(
+ "DrawingTablet", AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_STYLUS,
+ ControllerType::MOUSE, STYLUS_POINTER, [](PointerChoreographer& pc) {},
+ AMOTION_EVENT_ACTION_HOVER_ENTER)),
+ [](const testing::TestParamInfo<
+ SkipPointerScreenshotForPrivacySensitiveDisplaysFixtureParam>& p) {
+ return std::string{std::get<0>(p.param)};
+ });
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ WindowInfosListenerIsOnlyRegisteredWhenRequired) {
+ const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+ GetParam();
+ assertWindowInfosListenerNotRegistered();
+
+ // Listener should registered when a pointer device is added
+ initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+ assertWindowInfosListenerRegistered();
+
+ mChoreographer.notifyInputDevicesChanged({});
+ assertWindowInfosListenerNotRegistered();
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ InitialDisplayInfoIsPopulatedForListener) {
+ const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+ GetParam();
+ // listener should not be registered if there is no pointer device
+ assertWindowInfosListenerNotRegistered();
+
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ mInjectedInitialWindowInfos = {windowInfo};
+
+ initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+ assertWindowInfosListenerRegistered();
+
+ // Pointer indicators should be hidden based on the initial display info
+ auto pc = assertPointerControllerCreated(controllerType);
+ pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ // un-marking the privacy sensitive display should reset the state
+ windowInfo.inputConfig.clear();
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ SkipsPointerScreenshotForPrivacySensitiveWindows) {
+ const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+ GetParam();
+ initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+
+ // By default pointer indicators should not be hidden
+ auto pc = assertPointerControllerCreated(controllerType);
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ // marking a display privacy sensitive should set flag to hide pointer indicators on the
+ // display screenshot
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ assertWindowInfosListenerRegistered();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ // un-marking the privacy sensitive display should reset the state
+ windowInfo.inputConfig.clear();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ DoesNotSkipPointerScreenshotForHiddenPrivacySensitiveWindows) {
+ const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+ GetParam();
+ initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+
+ // By default pointer indicators should not be hidden
+ auto pc = assertPointerControllerCreated(controllerType);
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::NOT_VISIBLE;
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ assertWindowInfosListenerRegistered();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
+TEST_P(SkipPointerScreenshotForPrivacySensitiveDisplaysTestFixture,
+ DoesNotUpdateControllerForUnchangedPrivacySensitiveWindows) {
+ const auto& [name, source, controllerType, pointerBuilder, onControllerInit, action] =
+ GetParam();
+ initializePointerDevice(pointerBuilder, source, onControllerInit, action);
+
+ auto pc = assertPointerControllerCreated(controllerType);
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ assertWindowInfosListenerRegistered();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ gui::WindowInfo windowInfo2 = windowInfo;
+ windowInfo2.inputConfig.clear();
+ pc->assertSkipScreenshotFlagChanged();
+
+ // controller should not be updated if there are no changes in privacy sensitive windows
+ mRegisteredWindowInfoListener->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo, windowInfo2},
+ {displayInfo},
+ /*vsyncId=*/0,
+ /*timestamp=*/0});
+ pc->assertSkipScreenshotFlagNotChanged();
+}
+
+TEST_F_WITH_FLAGS(
+ PointerChoreographerTest, HidesPointerScreenshotForExistingPrivacySensitiveWindows,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ hide_pointer_indicators_for_secure_windows))) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Add a first mouse device
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+
+ mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ assertWindowInfosListenerRegistered();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ // Add a second touch device and controller
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID)}});
+ mChoreographer.setShowTouchesEnabled(true);
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(FIRST_TOUCH_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+
+ // Pointer indicators should be hidden for this controller by default
+ auto pc2 = assertPointerControllerCreated(ControllerType::TOUCH);
+ pc->assertIsSkipScreenshotFlagSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+
+ // un-marking the privacy sensitive display should reset the state
+ windowInfo.inputConfig.clear();
+ mRegisteredWindowInfoListener
+ ->onWindowInfosChanged(/*windowInfosUpdate=*/
+ {{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+
+ pc->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+ pc2->assertIsSkipScreenshotFlagNotSet(DISPLAY_ID);
+ pc2->assertIsSkipScreenshotFlagNotSet(ANOTHER_DISPLAY_ID);
+}
+
TEST_P(StylusTestFixture, SetsPointerIconForStylus) {
const auto& [name, source, controllerType] = GetParam();
@@ -1588,14 +2033,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::LogicalDisplayId::INVALID),
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::LogicalDisplayId::INVALID)
.build());
auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
mChoreographer.notifyMotion(MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, source)
@@ -1623,7 +2068,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::LogicalDisplayId::INVALID),
generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
auto firstMousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, firstMousePc->getDisplayId());
@@ -1679,7 +2124,9 @@
// 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::LogicalDisplayId::INVALID)}});
auto mousePc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, mousePc->getDisplayId());
@@ -1697,7 +2144,7 @@
mChoreographer.notifyInputDevicesChanged(
{/*id=*/0,
{generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE | AINPUT_SOURCE_TOUCHPAD,
- ADISPLAY_ID_NONE)}});
+ ui::LogicalDisplayId::INVALID)}});
auto touchpadPc = assertPointerControllerCreated(ControllerType::MOUSE);
ASSERT_EQ(DISPLAY_ID, touchpadPc->getDisplayId());
@@ -1736,4 +2183,331 @@
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::LogicalDisplayId::INVALID)}});
+ // 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::LogicalDisplayId::INVALID)}});
+ 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::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, DRAWING_TABLET_SOURCE,
+ ui::LogicalDisplayId::INVALID)}});
+ 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::LogicalDisplayId::INVALID)}});
+ 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::LogicalDisplayId::INVALID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE,
+ ui::LogicalDisplayId::INVALID)}});
+ 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::LogicalDisplayId::INVALID)}});
+
+ // 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);
+}
+
+class PointerVisibilityOnKeyPressTest : public PointerChoreographerTest {
+protected:
+ const std::unordered_map<int32_t, int32_t>
+ mMetaKeyStates{{AKEYCODE_ALT_LEFT, AMETA_ALT_LEFT_ON},
+ {AKEYCODE_ALT_RIGHT, AMETA_ALT_RIGHT_ON},
+ {AKEYCODE_SHIFT_LEFT, AMETA_SHIFT_LEFT_ON},
+ {AKEYCODE_SHIFT_RIGHT, AMETA_SHIFT_RIGHT_ON},
+ {AKEYCODE_SYM, AMETA_SYM_ON},
+ {AKEYCODE_FUNCTION, AMETA_FUNCTION_ON},
+ {AKEYCODE_CTRL_LEFT, AMETA_CTRL_LEFT_ON},
+ {AKEYCODE_CTRL_RIGHT, AMETA_CTRL_RIGHT_ON},
+ {AKEYCODE_META_LEFT, AMETA_META_LEFT_ON},
+ {AKEYCODE_META_RIGHT, AMETA_META_RIGHT_ON},
+ {AKEYCODE_CAPS_LOCK, AMETA_CAPS_LOCK_ON},
+ {AKEYCODE_NUM_LOCK, AMETA_NUM_LOCK_ON},
+ {AKEYCODE_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON}};
+
+ void notifyKey(ui::LogicalDisplayId targetDisplay, int32_t keyCode,
+ int32_t metaState = AMETA_NONE) {
+ if (metaState == AMETA_NONE && mMetaKeyStates.contains(keyCode)) {
+ // For simplicity, we always set the corresponding meta state when sending a meta
+ // keycode. This does not take into consideration when the meta state is updated in
+ // reality.
+ metaState = mMetaKeyStates.at(keyCode);
+ }
+ mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD)
+ .displayId(targetDisplay)
+ .keyCode(keyCode)
+ .metaState(metaState)
+ .build());
+ mChoreographer.notifyKey(KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD)
+ .displayId(targetDisplay)
+ .keyCode(keyCode)
+ .metaState(metaState)
+ .build());
+ }
+
+ void metaKeyCombinationHidesPointer(FakePointerController& pc, int32_t keyCode,
+ int32_t metaKeyCode) {
+ ASSERT_TRUE(pc.isPointerShown());
+ notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode));
+ ASSERT_FALSE(pc.isPointerShown());
+
+ unfadePointer();
+ }
+
+ void metaKeyCombinationDoesNotHidePointer(FakePointerController& pc, int32_t keyCode,
+ int32_t metaKeyCode) {
+ ASSERT_TRUE(pc.isPointerShown());
+ notifyKey(DISPLAY_ID, keyCode, mMetaKeyStates.at(metaKeyCode));
+ ASSERT_TRUE(pc.isPointerShown());
+ }
+
+ void unfadePointer() {
+ // unfade pointer by injecting mose hover event
+ mChoreographer.notifyMotion(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER, AINPUT_SOURCE_MOUSE)
+ .pointer(MOUSE_POINTER)
+ .deviceId(DEVICE_ID)
+ .displayId(DISPLAY_ID)
+ .build());
+ }
+};
+
+TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutImeConnectionDoesNotHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_CTRL_LEFT);
+
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, AlphanumericKeystrokesWithImeConnectionHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ notifyKey(DISPLAY_ID, AKEYCODE_0);
+ ASSERT_FALSE(pc->isPointerShown());
+
+ unfadePointer();
+
+ notifyKey(DISPLAY_ID, AKEYCODE_A);
+ ASSERT_FALSE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, MetaKeystrokesDoNotHidePointer) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ const std::vector<int32_t> metaKeyCodes{AKEYCODE_ALT_LEFT, AKEYCODE_ALT_RIGHT,
+ AKEYCODE_SHIFT_LEFT, AKEYCODE_SHIFT_RIGHT,
+ AKEYCODE_SYM, AKEYCODE_FUNCTION,
+ AKEYCODE_CTRL_LEFT, AKEYCODE_CTRL_RIGHT,
+ AKEYCODE_META_LEFT, AKEYCODE_META_RIGHT,
+ AKEYCODE_CAPS_LOCK, AKEYCODE_NUM_LOCK,
+ AKEYCODE_SCROLL_LOCK};
+ for (int32_t keyCode : metaKeyCodes) {
+ notifyKey(ui::LogicalDisplayId::INVALID, keyCode);
+ }
+
+ ASSERT_TRUE(pc->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, KeystrokesWithoutTargetHidePointerOnlyOnFocusedDisplay) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID}));
+ mChoreographer.setFocusedDisplay(DISPLAY_ID);
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0,
+ {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID),
+ generateTestDeviceInfo(SECOND_DEVICE_ID, AINPUT_SOURCE_MOUSE, ANOTHER_DISPLAY_ID)}});
+ auto pc1 = assertPointerControllerCreated(ControllerType::MOUSE);
+ auto pc2 = assertPointerControllerCreated(ControllerType::MOUSE);
+ ASSERT_TRUE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_0);
+ ASSERT_FALSE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+ unfadePointer();
+
+ notifyKey(ui::LogicalDisplayId::INVALID, AKEYCODE_A);
+ ASSERT_FALSE(pc1->isPointerShown());
+ ASSERT_TRUE(pc2->isPointerShown());
+}
+
+TEST_F(PointerVisibilityOnKeyPressTest, TestMetaKeyCombinations) {
+ mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Mouse connected
+ mChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+ auto pc = assertPointerControllerCreated(ControllerType::MOUSE);
+ EXPECT_CALL(mMockPolicy, isInputMethodConnectionActive).WillRepeatedly(testing::Return(true));
+
+ // meta key combinations that should hide pointer
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_LEFT);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SHIFT_RIGHT);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_CAPS_LOCK);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_0, AKEYCODE_NUM_LOCK);
+ metaKeyCombinationHidesPointer(*pc, AKEYCODE_A, AKEYCODE_SCROLL_LOCK);
+
+ // meta key combinations that should not hide pointer
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_ALT_RIGHT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_CTRL_RIGHT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_SYM);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_FUNCTION);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_LEFT);
+ metaKeyCombinationDoesNotHidePointer(*pc, AKEYCODE_A, AKEYCODE_META_RIGHT);
+}
+
+class PointerChoreographerWindowInfoListenerTest : public testing::Test {};
+
+TEST_F_WITH_FLAGS(
+ PointerChoreographerWindowInfoListenerTest,
+ doesNotCrashIfListenerCalledAfterPointerChoreographerDestroyed,
+ REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::input::flags,
+ hide_pointer_indicators_for_secure_windows))) {
+ sp<android::gui::WindowInfosListener> registeredListener;
+ sp<android::gui::WindowInfosListener> localListenerCopy;
+ {
+ testing::NiceMock<MockPointerChoreographerPolicyInterface> mockPolicy;
+ EXPECT_CALL(mockPolicy, createPointerController(ControllerType::MOUSE))
+ .WillOnce(testing::Return(std::make_shared<FakePointerController>()));
+ TestInputListener testListener;
+ std::vector<gui::WindowInfo> injectedInitialWindowInfos;
+ TestPointerChoreographer testChoreographer{testListener, mockPolicy, registeredListener,
+ injectedInitialWindowInfos};
+ testChoreographer.setDisplayViewports(createViewports({DISPLAY_ID}));
+
+ // Add mouse to create controller and listener
+ testChoreographer.notifyInputDevicesChanged(
+ {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}});
+
+ ASSERT_NE(nullptr, registeredListener) << "WindowInfosListener was not registered";
+ localListenerCopy = registeredListener;
+ }
+ ASSERT_EQ(nullptr, registeredListener) << "WindowInfosListener was not unregistered";
+
+ gui::WindowInfo windowInfo;
+ windowInfo.displayId = DISPLAY_ID;
+ windowInfo.inputConfig |= gui::WindowInfo::InputConfig::SENSITIVE_FOR_PRIVACY;
+ gui::DisplayInfo displayInfo;
+ displayInfo.displayId = DISPLAY_ID;
+ localListenerCopy->onWindowInfosChanged(
+ /*windowInfosUpdate=*/{{windowInfo}, {displayInfo}, /*vsyncId=*/0, /*timestamp=*/0});
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp
index 9818176..a36d526 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,
+ ui::LogicalDisplayId::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::LogicalDisplayId::INVALID);
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 76170eb..a6d9d5b 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..12fa835 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::LogicalDisplayId::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();
@@ -116,19 +112,7 @@
.WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
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 +123,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..853f628 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,
+ ui::LogicalDisplayId::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::LogicalDisplayId::INVALID);
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::LogicalDisplayId::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/CursorInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
index af20a27..8361517 100644
--- a/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/CursorInputFuzzer.cpp
@@ -81,7 +81,7 @@
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
InputReaderConfiguration::Change(0));
RawEvent rawEvent = getFuzzedRawEvent(*fdp);
- unused += mapper.process(&rawEvent);
+ unused += mapper.process(rawEvent);
},
[&]() -> void {
std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
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..0b4ac1f 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::LogicalDisplayId::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..7d26a43 100644
--- a/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/InputReaderFuzzer.cpp
@@ -55,8 +55,6 @@
void monitor() { reader->monitor(); }
- bool isInputDeviceEnabled(int32_t deviceId) { return reader->isInputDeviceEnabled(deviceId); }
-
status_t start() { return reader->start(); }
status_t stop() { return reader->stop(); }
@@ -119,7 +117,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);
}
@@ -169,6 +167,8 @@
reader->sysfsNodeChanged(sysfsNodePath);
}
+ DeviceId getLastUsedInputDeviceId() override { return reader->getLastUsedInputDeviceId(); }
+
private:
std::unique_ptr<InputReaderInterface> reader;
};
@@ -204,7 +204,6 @@
},
[&]() -> void { reader->monitor(); },
[&]() -> void { reader->getInputDevices(); },
- [&]() -> void { reader->isInputDeviceEnabled(fdp->ConsumeIntegral<int32_t>()); },
[&]() -> void {
reader->getScanCodeState(fdp->ConsumeIntegral<int32_t>(),
fdp->ConsumeIntegral<uint32_t>(),
@@ -241,7 +240,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/KeyboardInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
index 922cbdf..9e02502 100644
--- a/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/KeyboardInputFuzzer.cpp
@@ -50,11 +50,10 @@
FuzzInputReaderContext context(eventHub, fdp);
InputDevice device = getFuzzedInputDevice(*fdp, &context);
- KeyboardInputMapper& mapper = getMapperForDevice<
- ThreadSafeFuzzedDataProvider,
- KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{},
- /*source=*/fdp->ConsumeIntegral<uint32_t>(),
- /*keyboardType=*/fdp->ConsumeIntegral<int32_t>());
+ KeyboardInputMapper& mapper =
+ getMapperForDevice<ThreadSafeFuzzedDataProvider,
+ KeyboardInputMapper>(*fdp.get(), device, InputReaderConfiguration{},
+ /*source=*/fdp->ConsumeIntegral<uint32_t>());
// Loop through mapper operations until randomness is exhausted.
while (fdp->remaining_bytes() > 0) {
@@ -80,7 +79,7 @@
},
[&]() -> void {
RawEvent rawEvent = getFuzzedRawEvent(*fdp);
- std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(rawEvent);
},
[&]() -> void {
mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 7898126..ff425dd 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 {}
@@ -382,9 +338,11 @@
void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; };
nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; };
+ KeyboardClassifier& getKeyboardClassifier() override { return *mClassifier; }
private:
nsecs_t mLastKeyDownTimestamp;
+ std::unique_ptr<KeyboardClassifier> mClassifier = std::make_unique<KeyboardClassifier>();
};
template <class Fdp>
diff --git a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
index d3f6690..f29577d 100644
--- a/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/MultiTouchInputFuzzer.cpp
@@ -100,7 +100,7 @@
},
[&]() -> void {
RawEvent rawEvent = getFuzzedRawEvent(*fdp);
- std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(rawEvent);
},
[&]() -> void {
mapper.getKeyCodeState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
index ac2030a..a42d447 100644
--- a/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/SwitchInputFuzzer.cpp
@@ -44,7 +44,7 @@
[&]() -> void { mapper.getSources(); },
[&]() -> void {
RawEvent rawEvent = getFuzzedRawEvent(*fdp);
- std::list<NotifyArgs> unused = mapper.process(&rawEvent);
+ std::list<NotifyArgs> unused = mapper.process(rawEvent);
},
[&]() -> void {
mapper.getSwitchState(fdp->ConsumeIntegral<uint32_t>(),
diff --git a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
index c2bf275..c620032 100644
--- a/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
+++ b/services/inputflinger/tests/fuzzers/TouchpadInputFuzzer.cpp
@@ -125,6 +125,9 @@
config.touchpadTapToClickEnabled = fdp.ConsumeBool();
config.touchpadTapDraggingEnabled = fdp.ConsumeBool();
config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
+
+ config.pointerCaptureRequest.window = fdp.ConsumeBool() ? sp<BBinder>::make() : nullptr;
+ config.pointerCaptureRequest.seq = fdp.ConsumeIntegral<uint32_t>();
}
} // namespace
@@ -145,7 +148,6 @@
// Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
// TouchpadInputMapper constructor.
setTouchpadSettings(*fdp, policyConfig);
- policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
TouchpadInputMapper& mapper =
getMapperForDevice<ThreadSafeFuzzedDataProvider, TouchpadInputMapper>(*fdp, device,
policyConfig);
@@ -164,7 +166,6 @@
[&]() -> void { mapper.getSources(); },
[&]() -> void {
setTouchpadSettings(*fdp, policyConfig);
- policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
std::list<NotifyArgs> unused =
mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
InputReaderConfiguration::Change(
@@ -175,7 +176,7 @@
},
[&]() -> void {
RawEvent event = getFuzzedRawEvent(*fdp);
- std::list<NotifyArgs> unused = mapper.process(&event);
+ std::list<NotifyArgs> unused = mapper.process(event);
},
})();
}
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 1f72e8b..4f65e77 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -17,9 +17,9 @@
"PowerHalController.cpp",
"PowerHalLoader.cpp",
"PowerHalWrapper.cpp",
+ "PowerHintSessionWrapper.cpp",
"PowerSaveState.cpp",
"Temperature.cpp",
- "WorkDuration.cpp",
"WorkSource.cpp",
":libpowermanager_aidl",
],
@@ -51,6 +51,10 @@
"android.hardware.power@1.3",
],
+ whole_static_libs: [
+ "android.os.hintmanager_aidl-ndk",
+ ],
+
cflags: [
"-Wall",
"-Werror",
diff --git a/services/powermanager/PowerHalController.cpp b/services/powermanager/PowerHalController.cpp
index bc178bc..40fd097 100644
--- a/services/powermanager/PowerHalController.cpp
+++ b/services/powermanager/PowerHalController.cpp
@@ -57,6 +57,10 @@
PowerHalLoader::unloadAll();
}
+int32_t HalConnector::getAidlVersion() {
+ return PowerHalLoader::getAidlVersion();
+}
+
// -------------------------------------------------------------------------------------------------
void PowerHalController::init() {
@@ -77,6 +81,22 @@
return mConnectedHal;
}
+// Using statement expression macro instead of a method lets the static be
+// scoped to the outer method while dodging the need for a support lookup table
+// This only works for AIDL methods that do not vary supported/unsupported depending
+// on their arguments (not setBoost, setMode) which do their own support checks
+#define CACHE_SUPPORT(version, method) \
+ ({ \
+ static bool support = mHalConnector->getAidlVersion() >= version; \
+ !support ? decltype(method)::unsupported() : ({ \
+ auto result = method; \
+ if (result.isUnsupported()) { \
+ support = false; \
+ } \
+ std::move(result); \
+ }); \
+ })
+
// Check if a call to Power HAL function failed; if so, log the failure and
// invalidate the current Power HAL handle.
template <typename T>
@@ -103,40 +123,49 @@
return processHalResult(handle->setMode(mode, enabled), "setMode");
}
-HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
-PowerHalController::createHintSession(int32_t tgid, int32_t uid,
- const std::vector<int32_t>& threadIds,
- int64_t durationNanos) {
+// Aidl-only methods
+
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> PowerHalController::createHintSession(
+ int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->createHintSession(tgid, uid, threadIds, durationNanos),
- "createHintSession");
+ return CACHE_SUPPORT(2,
+ processHalResult(handle->createHintSession(tgid, uid, threadIds,
+ durationNanos),
+ "createHintSession"));
}
-HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>
-PowerHalController::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> PowerHalController::createHintSessionWithConfig(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
aidl::android::hardware::power::SessionTag tag,
aidl::android::hardware::power::SessionConfig* config) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
- tag, config),
- "createHintSessionWithConfig");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->createHintSessionWithConfig(tgid, uid, threadIds,
+ durationNanos, tag,
+ config),
+ "createHintSessionWithConfig"));
}
HalResult<int64_t> PowerHalController::getHintSessionPreferredRate() {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->getHintSessionPreferredRate(), "getHintSessionPreferredRate");
+ return CACHE_SUPPORT(2,
+ processHalResult(handle->getHintSessionPreferredRate(),
+ "getHintSessionPreferredRate"));
}
HalResult<aidl::android::hardware::power::ChannelConfig> PowerHalController::getSessionChannel(
int tgid, int uid) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->getSessionChannel(tgid, uid), "getSessionChannel");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->getSessionChannel(tgid, uid),
+ "getSessionChannel"));
}
HalResult<void> PowerHalController::closeSessionChannel(int tgid, int uid) {
std::shared_ptr<HalWrapper> handle = initHal();
- return processHalResult(handle->closeSessionChannel(tgid, uid), "closeSessionChannel");
+ return CACHE_SUPPORT(5,
+ processHalResult(handle->closeSessionChannel(tgid, uid),
+ "closeSessionChannel"));
}
} // namespace power
diff --git a/services/powermanager/PowerHalLoader.cpp b/services/powermanager/PowerHalLoader.cpp
index 2214461..ea284c3 100644
--- a/services/powermanager/PowerHalLoader.cpp
+++ b/services/powermanager/PowerHalLoader.cpp
@@ -60,6 +60,7 @@
sp<V1_1::IPower> PowerHalLoader::gHalHidlV1_1 = nullptr;
sp<V1_2::IPower> PowerHalLoader::gHalHidlV1_2 = nullptr;
sp<V1_3::IPower> PowerHalLoader::gHalHidlV1_3 = nullptr;
+int32_t PowerHalLoader::gAidlInterfaceVersion = 0;
void PowerHalLoader::unloadAll() {
std::lock_guard<std::mutex> lock(gHalMutex);
@@ -89,6 +90,8 @@
ndk::SpAIBinder(AServiceManager_waitForService(aidlServiceName.c_str())));
if (gHalAidl) {
ALOGI("Successfully connected to Power HAL AIDL service.");
+ gHalAidl->getInterfaceVersion(&gAidlInterfaceVersion);
+
} else {
ALOGI("Power HAL AIDL service not available.");
gHalExists = false;
@@ -128,6 +131,10 @@
return loadHal<V1_0::IPower>(gHalExists, gHalHidlV1_0, loadFn, "HIDL v1.0");
}
+int32_t PowerHalLoader::getAidlVersion() {
+ return gAidlInterfaceVersion;
+}
+
// -------------------------------------------------------------------------------------------------
} // namespace power
diff --git a/services/powermanager/PowerHalWrapper.cpp b/services/powermanager/PowerHalWrapper.cpp
index 1009100..bd6685c 100644
--- a/services/powermanager/PowerHalWrapper.cpp
+++ b/services/powermanager/PowerHalWrapper.cpp
@@ -18,11 +18,10 @@
#include <aidl/android/hardware/power/Boost.h>
#include <aidl/android/hardware/power/IPowerHintSession.h>
#include <aidl/android/hardware/power/Mode.h>
+#include <powermanager/HalResult.h>
#include <powermanager/PowerHalWrapper.h>
#include <utils/Log.h>
-#include <cinttypes>
-
using namespace android::hardware::power;
namespace Aidl = aidl::android::hardware::power;
@@ -31,15 +30,6 @@
namespace power {
// -------------------------------------------------------------------------------------------------
-inline HalResult<void> toHalResult(const ndk::ScopedAStatus& result) {
- if (result.isOk()) {
- return HalResult<void>::ok();
- }
- ALOGE("Power HAL request failed: %s", result.getDescription().c_str());
- return HalResult<void>::failed(result.getDescription());
-}
-
-// -------------------------------------------------------------------------------------------------
HalResult<void> EmptyHalWrapper::setBoost(Aidl::Boost boost, int32_t durationMs) {
ALOGV("Skipped setBoost %s with duration %dms because %s", toString(boost).c_str(), durationMs,
@@ -53,19 +43,19 @@
return HalResult<void>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSession(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> EmptyHalWrapper::createHintSession(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t) {
ALOGV("Skipped createHintSession(task num=%zu) because %s", threadIds.size(),
getUnsupportedMessage());
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::unsupported();
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> EmptyHalWrapper::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> EmptyHalWrapper::createHintSessionWithConfig(
int32_t, int32_t, const std::vector<int32_t>& threadIds, int64_t, Aidl::SessionTag,
Aidl::SessionConfig*) {
ALOGV("Skipped createHintSessionWithConfig(task num=%zu) because %s", threadIds.size(),
getUnsupportedMessage());
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::unsupported();
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::unsupported();
}
HalResult<int64_t> EmptyHalWrapper::getHintSessionPreferredRate() {
@@ -225,7 +215,7 @@
}
lock.unlock();
- return toHalResult(mHandle->setBoost(boost, durationMs));
+ return HalResult<void>::fromStatus(mHandle->setBoost(boost, durationMs));
}
HalResult<void> AidlHalWrapper::setMode(Aidl::Mode mode, bool enabled) {
@@ -253,25 +243,25 @@
}
lock.unlock();
- return toHalResult(mHandle->setMode(mode, enabled));
+ return HalResult<void>::fromStatus(mHandle->setMode(mode, enabled));
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSession(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> AidlHalWrapper::createHintSession(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos) {
std::shared_ptr<Aidl::IPowerHintSession> appSession;
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(mHandle->createHintSession(tgid, uid, threadIds, durationNanos, &appSession),
- std::move(appSession));
+ std::make_shared<PowerHintSessionWrapper>(std::move(appSession)));
}
-HalResult<std::shared_ptr<Aidl::IPowerHintSession>> AidlHalWrapper::createHintSessionWithConfig(
+HalResult<std::shared_ptr<PowerHintSessionWrapper>> AidlHalWrapper::createHintSessionWithConfig(
int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds, int64_t durationNanos,
Aidl::SessionTag tag, Aidl::SessionConfig* config) {
std::shared_ptr<Aidl::IPowerHintSession> appSession;
- return HalResult<std::shared_ptr<Aidl::IPowerHintSession>>::
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
fromStatus(mHandle->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos,
tag, config, &appSession),
- std::move(appSession));
+ std::make_shared<PowerHintSessionWrapper>(std::move(appSession)));
}
HalResult<int64_t> AidlHalWrapper::getHintSessionPreferredRate() {
@@ -287,7 +277,7 @@
}
HalResult<void> AidlHalWrapper::closeSessionChannel(int tgid, int uid) {
- return toHalResult(mHandle->closeSessionChannel(tgid, uid));
+ return HalResult<void>::fromStatus(mHandle->closeSessionChannel(tgid, uid));
}
const char* AidlHalWrapper::getUnsupportedMessage() {
diff --git a/services/powermanager/PowerHintSessionWrapper.cpp b/services/powermanager/PowerHintSessionWrapper.cpp
new file mode 100644
index 0000000..930c7fa
--- /dev/null
+++ b/services/powermanager/PowerHintSessionWrapper.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 <powermanager/PowerHintSessionWrapper.h>
+
+using namespace aidl::android::hardware::power;
+
+namespace android::power {
+
+// Caches support for a given call in a static variable, checking both
+// the return value and interface version.
+#define CACHE_SUPPORT(version, method) \
+ ({ \
+ static bool support = mInterfaceVersion >= version; \
+ !support ? decltype(method)::unsupported() : ({ \
+ auto result = method; \
+ if (result.isUnsupported()) { \
+ support = false; \
+ } \
+ std::move(result); \
+ }); \
+ })
+
+#define CHECK_SESSION(resultType) \
+ if (mSession == nullptr) { \
+ return HalResult<resultType>::failed("Session not running"); \
+ }
+
+// FWD_CALL just forwards calls from the wrapper to the session object.
+// It only works if the call has no return object, as is the case with all calls
+// except getSessionConfig.
+#define FWD_CALL(version, name, args, untypedArgs) \
+ HalResult<void> PowerHintSessionWrapper::name args { \
+ CHECK_SESSION(void) \
+ return CACHE_SUPPORT(version, HalResult<void>::fromStatus(mSession->name untypedArgs)); \
+ }
+
+PowerHintSessionWrapper::PowerHintSessionWrapper(std::shared_ptr<IPowerHintSession>&& session)
+ : mSession(session) {
+ if (mSession != nullptr) {
+ mSession->getInterfaceVersion(&mInterfaceVersion);
+ }
+}
+
+// Support for individual hints/modes is not really handled here since there
+// is no way to check for it, so in the future if a way to check that is added,
+// this will need to be updated.
+
+FWD_CALL(2, updateTargetWorkDuration, (int64_t in_targetDurationNanos), (in_targetDurationNanos));
+FWD_CALL(2, reportActualWorkDuration, (const std::vector<WorkDuration>& in_durations),
+ (in_durations));
+FWD_CALL(2, pause, (), ());
+FWD_CALL(2, resume, (), ());
+FWD_CALL(2, close, (), ());
+FWD_CALL(4, sendHint, (SessionHint in_hint), (in_hint));
+FWD_CALL(4, setThreads, (const std::vector<int32_t>& in_threadIds), (in_threadIds));
+FWD_CALL(5, setMode, (SessionMode in_type, bool in_enabled), (in_type, in_enabled));
+
+HalResult<SessionConfig> PowerHintSessionWrapper::getSessionConfig() {
+ CHECK_SESSION(SessionConfig);
+ SessionConfig config;
+ return CACHE_SUPPORT(5,
+ HalResult<SessionConfig>::fromStatus(mSession->getSessionConfig(&config),
+ std::move(config)));
+}
+
+} // namespace android::power
diff --git a/services/powermanager/WorkDuration.cpp b/services/powermanager/WorkDuration.cpp
deleted file mode 100644
index bd2b10a..0000000
--- a/services/powermanager/WorkDuration.cpp
+++ /dev/null
@@ -1,52 +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.
- */
-
-#define LOG_TAG "WorkDuration"
-
-#include <android/WorkDuration.h>
-#include <android/performance_hint.h>
-#include <binder/Parcel.h>
-#include <utils/Log.h>
-
-namespace android::os {
-
-WorkDuration::WorkDuration(int64_t startTimestampNanos, int64_t totalDurationNanos,
- int64_t cpuDurationNanos, int64_t gpuDurationNanos)
- : timestampNanos(0),
- actualTotalDurationNanos(totalDurationNanos),
- workPeriodStartTimestampNanos(startTimestampNanos),
- actualCpuDurationNanos(cpuDurationNanos),
- actualGpuDurationNanos(gpuDurationNanos) {}
-
-status_t WorkDuration::writeToParcel(Parcel* parcel) const {
- if (parcel == nullptr) {
- ALOGE("%s: Null parcel", __func__);
- return BAD_VALUE;
- }
-
- parcel->writeInt64(workPeriodStartTimestampNanos);
- parcel->writeInt64(actualTotalDurationNanos);
- parcel->writeInt64(actualCpuDurationNanos);
- parcel->writeInt64(actualGpuDurationNanos);
- parcel->writeInt64(timestampNanos);
- return OK;
-}
-
-status_t WorkDuration::readFromParcel(const Parcel*) {
- return INVALID_OPERATION;
-}
-
-} // namespace android::os
diff --git a/services/powermanager/include/android/WorkDuration.h b/services/powermanager/include/android/WorkDuration.h
deleted file mode 100644
index 26a575f..0000000
--- a/services/powermanager/include/android/WorkDuration.h
+++ /dev/null
@@ -1,71 +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.
- */
-
-#pragma once
-
-#include <binder/Parcelable.h>
-#include <math.h>
-
-struct AWorkDuration {};
-
-namespace android::os {
-
-/**
- * C++ Parcelable version of {@link PerformanceHintManager.WorkDuration} that can be used in
- * binder calls.
- * This file needs to be kept in sync with the WorkDuration in
- * frameworks/base/core/java/android/os/WorkDuration.java
- */
-struct WorkDuration : AWorkDuration, android::Parcelable {
- WorkDuration() = default;
- ~WorkDuration() = default;
-
- WorkDuration(int64_t workPeriodStartTimestampNanos, int64_t actualTotalDurationNanos,
- int64_t actualCpuDurationNanos, int64_t actualGpuDurationNanos);
- status_t writeToParcel(Parcel* parcel) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- inline bool equalsWithoutTimestamp(const WorkDuration& other) const {
- return workPeriodStartTimestampNanos == other.workPeriodStartTimestampNanos &&
- actualTotalDurationNanos == other.actualTotalDurationNanos &&
- actualCpuDurationNanos == other.actualCpuDurationNanos &&
- actualGpuDurationNanos == other.actualGpuDurationNanos;
- }
-
- bool operator==(const WorkDuration& other) const {
- return timestampNanos == other.timestampNanos && equalsWithoutTimestamp(other);
- }
-
- bool operator!=(const WorkDuration& other) const { return !(*this == other); }
-
- friend std::ostream& operator<<(std::ostream& os, const WorkDuration& workDuration) {
- os << "{"
- << "workPeriodStartTimestampNanos: " << workDuration.workPeriodStartTimestampNanos
- << ", actualTotalDurationNanos: " << workDuration.actualTotalDurationNanos
- << ", actualCpuDurationNanos: " << workDuration.actualCpuDurationNanos
- << ", actualGpuDurationNanos: " << workDuration.actualGpuDurationNanos
- << ", timestampNanos: " << workDuration.timestampNanos << "}";
- return os;
- }
-
- int64_t timestampNanos;
- int64_t actualTotalDurationNanos;
- int64_t workPeriodStartTimestampNanos;
- int64_t actualCpuDurationNanos;
- int64_t actualGpuDurationNanos;
-};
-
-} // namespace android::os
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 6fc96c0..a05ce2b 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -37,6 +37,7 @@
"PowerHalWrapperHidlV1_1Test.cpp",
"PowerHalWrapperHidlV1_2Test.cpp",
"PowerHalWrapperHidlV1_3Test.cpp",
+ "PowerHintSessionWrapperTest.cpp",
"WorkSourceTest.cpp",
],
cflags: [
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index a720296..1589c99 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -86,6 +86,10 @@
void PowerHalWrapperAidlTest::SetUp() {
mMockHal = ndk::SharedRefBase::make<StrictMock<MockIPower>>();
+ EXPECT_CALL(*mMockHal, getInterfaceVersion(_)).WillRepeatedly(([](int32_t* ret) {
+ *ret = 5;
+ return ndk::ScopedAStatus::ok();
+ }));
mWrapper = std::make_unique<AidlHalWrapper>(mMockHal);
ASSERT_NE(nullptr, mWrapper);
}
@@ -130,10 +134,12 @@
}
TEST_F(PowerHalWrapperAidlTest, TestSetBoostUnsupported) {
- EXPECT_CALL(*mMockHal.get(), isBoostSupported(Eq(Boost::INTERACTION), _))
- .Times(Exactly(1))
- .WillOnce(DoAll(SetArgPointee<1>(false),
- Return(testing::ByMove(ndk::ScopedAStatus::ok()))));
+ EXPECT_CALL(*mMockHal.get(), isBoostSupported(_, _))
+ .Times(Exactly(2))
+ .WillRepeatedly([](Boost, bool* ret) {
+ *ret = false;
+ return ndk::ScopedAStatus::ok();
+ });
auto result = mWrapper->setBoost(Boost::INTERACTION, 1000);
ASSERT_TRUE(result.isUnsupported());
@@ -311,3 +317,29 @@
auto closeResult = mWrapper->closeSessionChannel(tgid, uid);
ASSERT_TRUE(closeResult.isOk());
}
+
+TEST_F(PowerHalWrapperAidlTest, TestCreateHintSessionWithConfigUnsupported) {
+ std::vector<int> threadIds{gettid()};
+ int32_t tgid = 999;
+ int32_t uid = 1001;
+ int64_t durationNanos = 16666666L;
+ SessionTag tag = SessionTag::OTHER;
+ SessionConfig out;
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos),
+ Eq(tag), _, _))
+ .Times(1)
+ .WillOnce(Return(testing::ByMove(
+ ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION))));
+ auto result =
+ mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
+ ASSERT_TRUE(result.isUnsupported());
+ Mock::VerifyAndClearExpectations(mMockHal.get());
+ EXPECT_CALL(*mMockHal.get(),
+ createHintSessionWithConfig(Eq(tgid), Eq(uid), Eq(threadIds), Eq(durationNanos),
+ Eq(tag), _, _))
+ .WillOnce(Return(
+ testing::ByMove(ndk::ScopedAStatus::fromStatus(STATUS_UNKNOWN_TRANSACTION))));
+ result = mWrapper->createHintSessionWithConfig(tgid, uid, threadIds, durationNanos, tag, &out);
+ ASSERT_TRUE(result.isUnsupported());
+}
diff --git a/services/powermanager/tests/PowerHintSessionWrapperTest.cpp b/services/powermanager/tests/PowerHintSessionWrapperTest.cpp
new file mode 100644
index 0000000..7743fa4
--- /dev/null
+++ b/services/powermanager/tests/PowerHintSessionWrapperTest.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 <aidl/android/hardware/power/IPowerHintSession.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using aidl::android::hardware::power::IPowerHintSession;
+using android::power::PowerHintSessionWrapper;
+
+using namespace android;
+using namespace std::chrono_literals;
+using namespace testing;
+
+class MockIPowerHintSession : public IPowerHintSession {
+public:
+ MockIPowerHintSession() = default;
+ MOCK_METHOD(::ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t in_targetDurationNanos),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, reportActualWorkDuration,
+ (const std::vector<::aidl::android::hardware::power::WorkDuration>& in_durations),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, pause, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, resume, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, close, (), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, sendHint,
+ (::aidl::android::hardware::power::SessionHint in_hint), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setThreads, (const std::vector<int32_t>& in_threadIds),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, setMode,
+ (::aidl::android::hardware::power::SessionMode in_type, bool in_enabled),
+ (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getSessionConfig,
+ (::aidl::android::hardware::power::SessionConfig * _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
+ MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
+ MOCK_METHOD(::ndk::SpAIBinder, asBinder, (), (override));
+ MOCK_METHOD(bool, isRemote, (), (override));
+};
+
+class PowerHintSessionWrapperTest : public Test {
+public:
+ void SetUp() override;
+
+protected:
+ std::shared_ptr<NiceMock<MockIPowerHintSession>> mMockSession = nullptr;
+ std::unique_ptr<PowerHintSessionWrapper> mSession = nullptr;
+};
+
+void PowerHintSessionWrapperTest::SetUp() {
+ mMockSession = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
+ EXPECT_CALL(*mMockSession, getInterfaceVersion(_)).WillRepeatedly(([](int32_t* ret) {
+ *ret = 5;
+ return ndk::ScopedAStatus::ok();
+ }));
+ mSession = std::make_unique<PowerHintSessionWrapper>(mMockSession);
+ ASSERT_NE(nullptr, mSession);
+}
+
+TEST_F(PowerHintSessionWrapperTest, updateTargetWorkDuration) {
+ EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(1000000000))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->updateTargetWorkDuration(1000000000);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, reportActualWorkDuration) {
+ EXPECT_CALL(*mMockSession.get(),
+ reportActualWorkDuration(
+ std::vector<::aidl::android::hardware::power::WorkDuration>()))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->reportActualWorkDuration(
+ std::vector<::aidl::android::hardware::power::WorkDuration>());
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, pause) {
+ EXPECT_CALL(*mMockSession.get(), pause()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->pause();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, resume) {
+ EXPECT_CALL(*mMockSession.get(), resume()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->resume();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, close) {
+ EXPECT_CALL(*mMockSession.get(), close()).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->close();
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, sendHint) {
+ EXPECT_CALL(*mMockSession.get(),
+ sendHint(::aidl::android::hardware::power::SessionHint::CPU_LOAD_UP))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->sendHint(::aidl::android::hardware::power::SessionHint::CPU_LOAD_UP);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, setThreads) {
+ EXPECT_CALL(*mMockSession.get(), setThreads(_)).WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->setThreads(std::vector<int32_t>{gettid()});
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, setMode) {
+ EXPECT_CALL(*mMockSession.get(),
+ setMode(::aidl::android::hardware::power::SessionMode::POWER_EFFICIENCY, true))
+ .WillOnce(Return(ndk::ScopedAStatus::ok()));
+ auto status = mSession->setMode(::aidl::android::hardware::power::SessionMode::POWER_EFFICIENCY,
+ true);
+ ASSERT_TRUE(status.isOk());
+}
+
+TEST_F(PowerHintSessionWrapperTest, getSessionConfig) {
+ EXPECT_CALL(*mMockSession.get(), getSessionConfig(_))
+ .WillOnce(DoAll(SetArgPointee<0>(
+ aidl::android::hardware::power::SessionConfig{.id = 12L}),
+ Return(ndk::ScopedAStatus::ok())));
+ auto status = mSession->getSessionConfig();
+ ASSERT_TRUE(status.isOk());
+}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index afaf0ae..f4b0265 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -84,6 +84,7 @@
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index f62562c..9c4d1ac 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -429,14 +429,18 @@
}
void SensorDevice::onDynamicSensorsDisconnected(
- const std::vector<int32_t>& dynamicSensorHandlesRemoved) {
- if (sensorservice_flags::sensor_device_on_dynamic_sensor_disconnected()) {
- for (auto handle : dynamicSensorHandlesRemoved) {
- auto it = mConnectedDynamicSensors.find(handle);
- if (it != mConnectedDynamicSensors.end()) {
- mConnectedDynamicSensors.erase(it);
- }
- }
+ const std::vector<int32_t>& /*dynamicSensorHandlesRemoved*/) {
+ // This function is currently a no-op has removing data in mConnectedDynamicSensors here will
+ // cause a race condition between when this callback is invoked and when the dynamic sensor meta
+ // event is processed by polling. The clean up should only happen after processing the meta
+ // event. See the call stack of cleanupDisconnectedDynamicSensor.
+}
+
+void SensorDevice::cleanupDisconnectedDynamicSensor(int handle) {
+ std::lock_guard<std::mutex> lock(mDynamicSensorsMutex);
+ auto it = mConnectedDynamicSensors.find(handle);
+ if (it != mConnectedDynamicSensors.end()) {
+ mConnectedDynamicSensors.erase(it);
}
}
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index 52f7cf2..b7b04b5 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -63,6 +63,14 @@
std::vector<int32_t> getDynamicSensorHandles();
void handleDynamicSensorConnection(int handle, bool connected);
+ /**
+ * Removes handle from connected dynamic sensor list. Note that this method must be called after
+ * SensorService has done using sensor data.
+ *
+ * @param handle of the disconnected dynamic sensor.
+ */
+ void cleanupDisconnectedDynamicSensor(int handle);
+
status_t initCheck() const;
int getHalDeviceVersion() const;
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..31b7f88 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1273,6 +1273,10 @@
} else {
int handle = mSensorEventBuffer[i].dynamic_sensor_meta.handle;
disconnectDynamicSensor(handle, activeConnections);
+ if (sensorservice_flags::
+ sensor_service_clear_dynamic_sensor_data_at_the_end()) {
+ device.cleanupDisconnectedDynamicSensor(handle);
+ }
}
}
}
@@ -2302,11 +2306,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/SensorService.h b/services/sensorservice/SensorService.h
index 118d928..bd54d24 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -340,8 +340,8 @@
binder::Status onSensorPrivacyChanged(int toggleType, int sensor,
bool enabled);
- // This callback is used for additional automotive-specific states for sensor privacy
- // such as AUTO_DRIVER_ASSISTANCE_APPS. The newly defined states will only be valid
+ // This callback is used for additional automotive-specific state for sensor privacy
+ // such as ENABLED_EXCEPT_ALLOWLISTED_APPS. The newly defined states will only be valid
// for camera privacy on automotive devices. onSensorPrivacyChanged() will still be
// invoked whenever the enabled status of a toggle changes.
binder::Status onSensorPrivacyStateChanged(int, int, int) {return binder::Status::ok();}
diff --git a/services/sensorservice/senserservice_flags.aconfig b/services/sensorservice/senserservice_flags.aconfig
index 8d43f79..7abfbaa 100644
--- a/services/sensorservice/senserservice_flags.aconfig
+++ b/services/sensorservice/senserservice_flags.aconfig
@@ -13,4 +13,18 @@
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"
+}
+
+flag {
+ name: "sensor_service_clear_dynamic_sensor_data_at_the_end"
+ namespace: "sensors"
+ description: "When this flag is enabled, sensor service will only erase dynamic sensor data at the end of the threadLoop to prevent race condition."
+ bug: "329020894"
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 46252e1..1b6c598 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -45,6 +45,7 @@
"android.hardware.power-ndk_shared",
"librenderengine_deps",
"libtimestats_deps",
+ "libsurfaceflinger_common_deps",
"surfaceflinger_defaults",
],
cflags: [
@@ -82,12 +83,12 @@
"libprotobuf-cpp-lite",
"libsync",
"libui",
- "libinput",
"libutils",
"libSurfaceFlingerProp",
- "server_configurable_flags",
+ "libaconfig_storage_read_api_cc"
],
static_libs: [
+ "iinputflinger_aidl_lib_static",
"libaidlcommonsupport",
"libcompositionengine",
"libframetimeline",
@@ -98,10 +99,9 @@
"libscheduler",
"libserviceutils",
"libshaders",
- "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
"libtimestats",
"libtonemap",
- "libsurfaceflingerflags",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -160,6 +160,7 @@
"BackgroundExecutor.cpp",
"Client.cpp",
"ClientCache.cpp",
+ "Display/DisplayModeController.cpp",
"Display/DisplaySnapshot.cpp",
"DisplayDevice.cpp",
"DisplayHardware/AidlComposerHal.cpp",
@@ -212,7 +213,6 @@
"Scheduler/VsyncModulator.cpp",
"Scheduler/VsyncSchedule.cpp",
"ScreenCaptureOutput.cpp",
- "StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
"Tracing/LayerDataSource.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 05af4ed..ad5a760 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -39,7 +39,6 @@
"libSurfaceFlingerProp",
"libui",
"libutils",
- "server_configurable_flags",
],
static_libs: [
"liblayers_proto",
@@ -92,24 +91,23 @@
cc_library {
name: "libcompositionengine",
- defaults: ["libcompositionengine_defaults"],
- static_libs: [
- "libsurfaceflinger_common",
- "libsurfaceflingerflags",
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_deps",
],
srcs: [
":libcompositionengine_sources",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
- shared_libs: [
- "server_configurable_flags",
- ],
}
cc_library {
name: "libcompositionengine_mocks",
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"mock/CompositionEngine.cpp",
"mock/Display.cpp",
@@ -125,11 +123,6 @@
"libgtest",
"libgmock",
"libcompositionengine",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
@@ -142,7 +135,10 @@
"frameworks/native/services/surfaceflinger/common/include",
"frameworks/native/services/surfaceflinger/tests/unittests",
],
- defaults: ["libcompositionengine_defaults"],
+ defaults: [
+ "libcompositionengine_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
":libcompositionengine_sources",
"tests/planner/CachedSetTest.cpp",
@@ -168,14 +164,11 @@
"librenderengine_mocks",
"libgmock",
"libgtest",
- "libsurfaceflinger_common_test",
- "libsurfaceflingerflags_test",
],
shared_libs: [
// For some reason, libvulkan isn't picked up from librenderengine
// Probably ASAN related?
"libvulkan",
- "server_configurable_flags",
],
sanitize: {
hwaddress: true,
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 7c10fa5..e32cc02 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -73,6 +73,9 @@
// TODO(b/121291683): These will become private/internal
virtual void preComposition(CompositionRefreshArgs&) = 0;
+ // Resolves any unfulfilled promises for release fences
+ virtual void postComposition(CompositionRefreshArgs&) = 0;
+
virtual FeatureFlags getFeatureFlags() const = 0;
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 18a96f4..dd0f985 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -19,6 +19,7 @@
#include <chrono>
#include <optional>
#include <vector>
+#include "utils/Timers.h"
#include <compositionengine/Display.h>
#include <compositionengine/LayerFE.h>
@@ -33,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;
@@ -100,11 +95,12 @@
// 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;
+
+ // System time for when frame refresh starts. Used for stats.
+ nsecs_t refreshStartTime = 0;
};
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index a1d6132..4e080b3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -58,8 +58,7 @@
// Called before composition starts. Should return true if this layer has
// pending updates which would require an extra display refresh cycle to
// process.
- virtual bool onPreComposition(nsecs_t refreshStartTime,
- bool updatingOutputGeometryThisFrame) = 0;
+ virtual bool onPreComposition(bool updatingOutputGeometryThisFrame) = 0;
struct ClientCompositionTargetSettings {
enum class BlurSetting {
@@ -134,6 +133,15 @@
uint64_t frameNumber = 0;
};
+ // Describes the states of the release fence. Checking the states allows checks
+ // to ensure that set_value() is not called on the same promise multiple times,
+ // and can indicate if the promise has been fulfilled.
+ enum class ReleaseFencePromiseStatus {
+ UNINITIALIZED, // Promise not created
+ INITIALIZED, // Promise created, fence has not been set
+ FULFILLED // Promise fulfilled, fence is set
+ };
+
// Returns the LayerSettings to pass to RenderEngine::drawLayers. The state may contain shadows
// casted by the layer or the content of the layer itself. If the layer does not render then an
// empty optional will be returned.
@@ -143,6 +151,19 @@
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack) = 0;
+ // Initializes a promise for a buffer release fence and provides the future for that
+ // fence. This should only be called when a promise has not yet been created, or
+ // after the previous promise has already been fulfilled. Attempting to call this
+ // when an existing promise is INITIALIZED will fail because the promise has not
+ // yet been fulfilled.
+ virtual ftl::Future<FenceResult> createReleaseFenceFuture() = 0;
+
+ // Sets promise with its buffer's release fence
+ virtual void setReleaseFence(const FenceResult& releaseFence) = 0;
+
+ // Checks if the buffer's release fence has been set
+ virtual LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() = 0;
+
// Gets some kind of identifier for the layer for debug purposes.
virtual const char* getDebugName() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index f1d6f52..191d475 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -306,7 +306,7 @@
virtual void finishFrame(GpuCompositionResult&&) = 0;
virtual std::optional<base::unique_fd> composeSurfaces(
const Region&, std::shared_ptr<renderengine::ExternalTexture>, base::unique_fd&) = 0;
- virtual void presentFrameAndReleaseLayers() = 0;
+ virtual void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) = 0;
virtual void renderCachedSets(const CompositionRefreshArgs&) = 0;
virtual bool chooseCompositionStrategy(
std::optional<android::HWComposer::DeviceRequestedChanges>*) = 0;
@@ -314,6 +314,7 @@
const std::optional<android::HWComposer::DeviceRequestedChanges>& changes) = 0;
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentFrame() = 0;
+ virtual void executeCommands() = 0;
virtual std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
bool supportsProtectedContent, ui::Dataspace outputDataspace,
std::vector<LayerFE*> &outLayerRef) = 0;
@@ -321,8 +322,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/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index c699557..45208dd 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -48,6 +48,8 @@
void preComposition(CompositionRefreshArgs&) override;
+ void postComposition(CompositionRefreshArgs&) override;
+
FeatureFlags getFeatureFlags() const override;
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index 2dc9a1a..d1eff24 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -60,6 +60,7 @@
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override;
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentFrame() override;
+ void executeCommands() override;
void setExpensiveRenderingExpected(bool) override;
void finishFrame(GpuCompositionResult&&) override;
bool supportsOffloadPresent() const override;
@@ -93,7 +94,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..9990a74 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -104,7 +104,7 @@
std::optional<base::unique_fd> composeSurfaces(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&) override;
- void presentFrameAndReleaseLayers() override;
+ void presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) override;
void renderCachedSets(const CompositionRefreshArgs&) override;
void cacheClientCompositionRequests(uint32_t) override;
bool canPredictCompositionStrategy(const CompositionRefreshArgs&) override;
@@ -123,7 +123,8 @@
virtual std::future<bool> chooseCompositionStrategyAsync(
std::optional<android::HWComposer::DeviceRequestedChanges>*);
virtual void resetCompositionStrategy();
- virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync();
+ virtual ftl::Future<std::monostate> presentFrameAndReleaseLayersAsync(
+ bool flushEvenWhenDisabled);
protected:
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
@@ -137,6 +138,7 @@
void applyCompositionStrategy(const std::optional<DeviceRequestedChanges>&) override{};
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentFrame() override;
+ void executeCommands() override {}
virtual renderengine::DisplaySettings generateClientCompositionDisplaySettings(
const std::shared_ptr<renderengine::ExternalTexture>& buffer) const;
std::vector<LayerFE::LayerSettings> generateClientCompositionRequests(
@@ -144,8 +146,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 +167,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/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 9b2387b..a1b7282 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -52,6 +52,7 @@
MOCK_METHOD1(updateCursorAsync, void(CompositionRefreshArgs&));
MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+ MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&));
MOCK_CONST_METHOD0(getFeatureFlags, FeatureFlags());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 15e4577..05a5d38 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -20,6 +20,7 @@
#include <compositionengine/LayerFECompositionState.h>
#include <gmock/gmock.h>
#include <ui/Fence.h>
+#include "ui/FenceResult.h"
namespace android::compositionengine::mock {
@@ -43,7 +44,7 @@
MOCK_CONST_METHOD0(getCompositionState, const LayerFECompositionState*());
- MOCK_METHOD2(onPreComposition, bool(nsecs_t, bool));
+ MOCK_METHOD1(onPreComposition, bool(bool));
MOCK_CONST_METHOD1(prepareClientComposition,
std::optional<compositionengine::LayerFE::LayerSettings>(
@@ -52,6 +53,9 @@
MOCK_METHOD(void, onLayerDisplayed, (ftl::SharedFuture<FenceResult>, ui::LayerStack),
(override));
+ MOCK_METHOD0(createReleaseFenceFuture, ftl::Future<FenceResult>());
+ MOCK_METHOD1(setReleaseFence, void(const FenceResult&));
+ MOCK_METHOD0(getReleaseFencePromiseStatus, LayerFE::ReleaseFencePromiseStatus());
MOCK_CONST_METHOD0(getDebugName, const char*());
MOCK_CONST_METHOD0(getSequence, int32_t());
MOCK_CONST_METHOD0(hasRoundedCorners, bool());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 95ea3a4..d5bf2b5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -121,9 +121,10 @@
base::unique_fd&));
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
MOCK_METHOD1(renderCachedSets, void(const CompositionRefreshArgs&));
MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+ MOCK_METHOD(void, executeCommands, ());
MOCK_METHOD3(generateClientCompositionRequests,
std::vector<LayerFE::LayerSettings>(bool, ui::Dataspace, std::vector<compositionengine::LayerFE*>&));
@@ -134,8 +135,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/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index d87eae3..4c77687 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -162,6 +162,7 @@
future.get();
}
}
+ postComposition(args);
}
void CompositionEngine::updateCursorAsync(CompositionRefreshArgs& args) {
@@ -181,10 +182,10 @@
bool needsAnotherUpdate = false;
- mRefreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mRefreshStartTime = args.refreshStartTime;
for (auto& layer : args.layers) {
- if (layer->onPreComposition(mRefreshStartTime, args.updatingOutputGeometryThisFrame)) {
+ if (layer->onPreComposition(args.updatingOutputGeometryThisFrame)) {
needsAnotherUpdate = true;
}
}
@@ -192,6 +193,34 @@
mNeedsAnotherUpdate = needsAnotherUpdate;
}
+// If a buffer is latched but the layer is not presented, such as when
+// obscured by another layer, the previous buffer needs to be released. We find
+// these buffers and fire a NO_FENCE to release it. This ensures that all
+// promises for buffer releases are fulfilled at the end of composition.
+void CompositionEngine::postComposition(CompositionRefreshArgs& args) {
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ ATRACE_CALL();
+ ALOGV(__FUNCTION__);
+
+ for (auto& layerFE : args.layers) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
+ }
+ }
+
+ // List of layersWithQueuedFrames does not necessarily overlap with
+ // list of layers, so those layersWithQueuedFrames also need any
+ // unfulfilled promises to be resolved for completeness.
+ for (auto& layerFE : args.layersWithQueuedFrames) {
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::INITIALIZED) {
+ layerFE->setReleaseFence(Fence::NO_FENCE);
+ }
+ }
+ }
+}
+
FeatureFlags CompositionEngine::getFeatureFlags() const {
return {};
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 6428d08..c1617d7 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -79,7 +79,7 @@
}
bool Display::isVirtual() const {
- return VirtualDisplayId::tryCast(mId).has_value();
+ return mId.isVirtual();
}
std::optional<DisplayId> Display::getDisplayId() const {
@@ -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,
@@ -365,6 +361,15 @@
static_cast<ui::PixelFormat>(clientTargetProperty.clientTargetProperty.pixelFormat));
}
+void Display::executeCommands() {
+ const auto halDisplayIdOpt = HalDisplayId::tryCast(mId);
+ if (mIsDisconnected || !halDisplayIdOpt) {
+ return;
+ }
+
+ getCompositionEngine().getHwComposer().executeCommands(*halDisplayIdOpt);
+}
+
compositionengine::Output::FrameFences Display::presentFrame() {
auto fences = impl::Output::presentFrame();
@@ -416,10 +421,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 921e05d..b40aea4 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -16,6 +16,7 @@
#include <SurfaceFlingerProperties.sysprop.h>
#include <android-base/stringprintf.h>
+#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -463,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) {
@@ -474,8 +479,9 @@
devOptRepaintFlash(refreshArgs);
finishFrame(std::move(result));
ftl::Future<std::monostate> future;
+ const bool flushEvenWhenDisabled = !refreshArgs.bufferIdsToUncache.empty();
if (mOffloadPresent) {
- future = presentFrameAndReleaseLayersAsync();
+ future = presentFrameAndReleaseLayersAsync(flushEvenWhenDisabled);
// Only offload for this frame. The next frame will determine whether it
// needs to be offloaded. Leave the HwcAsyncWorker in place. For one thing,
@@ -483,7 +489,7 @@
// we don't want to churn.
mOffloadPresent = false;
} else {
- presentFrameAndReleaseLayers();
+ presentFrameAndReleaseLayers(flushEvenWhenDisabled);
future = ftl::yield<std::monostate>({});
}
renderCachedSets(refreshArgs);
@@ -818,44 +824,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() {
@@ -1133,9 +1101,9 @@
finishPrepareFrame();
}
-ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync() {
- return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([&]() {
- presentFrameAndReleaseLayers();
+ftl::Future<std::monostate> Output::presentFrameAndReleaseLayersAsync(bool flushEvenWhenDisabled) {
+ return ftl::Future<bool>(std::move(mHwComposerAsyncWorker->send([this, flushEvenWhenDisabled]() {
+ presentFrameAndReleaseLayers(flushEvenWhenDisabled);
return true;
})))
.then([](bool) { return std::monostate{}; });
@@ -1210,7 +1178,8 @@
}
}
- presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
std::this_thread::sleep_for(*refreshArgs.devOptFlashDirtyRegionsDelay);
@@ -1247,8 +1216,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()))));
@@ -1392,8 +1360,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()) {
@@ -1443,13 +1423,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;
@@ -1576,19 +1549,36 @@
// 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;
}
-void Output::presentFrameAndReleaseLayers() {
+bool Output::isPowerHintSessionGpuReportingEnabled() {
+ return false;
+}
+
+void Output::presentFrameAndReleaseLayers(bool flushEvenWhenDisabled) {
ATRACE_FORMAT("%s for %s", __func__, mNamePlusId.c_str());
ALOGV(__FUNCTION__);
if (!getState().isEnabled) {
+ if (flushEvenWhenDisabled && FlagManager::getInstance().flush_buffer_slots_to_uncache()) {
+ // Some commands, like clearing buffer slots, should still be executed
+ // even if the display is not enabled.
+ executeCommands();
+ }
return;
}
@@ -1621,9 +1611,13 @@
releaseFence =
Fence::merge("LayerRelease", releaseFence, frame.clientTargetAcquireFence);
}
- layer->getLayerFE()
- .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
- outputState.layerFilter.layerStack);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ layer->getLayerFE().setReleaseFence(releaseFence);
+ } else {
+ layer->getLayerFE()
+ .onLayerDisplayed(ftl::yield<FenceResult>(std::move(releaseFence)).share(),
+ outputState.layerFilter.layerStack);
+ }
}
// We've got a list of layers needing fences, that are disjoint with
@@ -1631,8 +1625,12 @@
// supply them with the present fence.
for (auto& weakLayer : mReleasedLayers) {
if (const auto layer = weakLayer.promote()) {
- layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
- outputState.layerFilter.layerStack);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ layer->setReleaseFence(frame.presentFence);
+ } else {
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(frame.presentFence).share(),
+ outputState.layerFilter.layerStack);
+ }
}
}
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/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index da578e2..639164d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -28,6 +28,7 @@
#include "MockHWComposer.h"
#include "TimeStats/TimeStats.h"
+#include "gmock/gmock.h"
#include <variant>
@@ -90,14 +91,16 @@
// These are the overridable functions CompositionEngine::present() may
// call, and have separate test coverage.
MOCK_METHOD1(preComposition, void(CompositionRefreshArgs&));
+ MOCK_METHOD1(postComposition, void(CompositionRefreshArgs&));
};
StrictMock<CompositionEnginePartialMock> mEngine;
};
TEST_F(CompositionEnginePresentTest, worksWithEmptyRequest) {
- // present() always calls preComposition()
+ // present() always calls preComposition() and postComposition()
EXPECT_CALL(mEngine, preComposition(Ref(mRefreshArgs)));
+ EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
mEngine.present(mRefreshArgs);
}
@@ -126,6 +129,9 @@
EXPECT_CALL(*mOutput3, present(Ref(mRefreshArgs)))
.WillOnce(Return(ftl::yield<std::monostate>({})));
+ // present() always calls postComposition()
+ EXPECT_CALL(mEngine, postComposition(Ref(mRefreshArgs)));
+
mRefreshArgs.outputs = {mOutput1, mOutput2, mOutput3};
mEngine.present(mRefreshArgs);
}
@@ -214,6 +220,7 @@
TEST_F(CompositionTestPreComposition, preCompositionSetsFrameTimestamp) {
const nsecs_t before = systemTime(SYSTEM_TIME_MONOTONIC);
+ mRefreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
mEngine.preComposition(mRefreshArgs);
const nsecs_t after = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -226,12 +233,9 @@
nsecs_t ts1 = 0;
nsecs_t ts2 = 0;
nsecs_t ts3 = 0;
- EXPECT_CALL(*mLayer1FE, onPreComposition(_, _))
- .WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
- EXPECT_CALL(*mLayer2FE, onPreComposition(_, _))
- .WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
- EXPECT_CALL(*mLayer3FE, onPreComposition(_, _))
- .WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
+ EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts1), Return(false)));
+ EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts2), Return(false)));
+ EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(DoAll(SaveArg<0>(&ts3), Return(false)));
mRefreshArgs.outputs = {mOutput1};
mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -245,9 +249,9 @@
}
TEST_F(CompositionTestPreComposition, preCompositionDefaultsToNoUpdateNeeded) {
- EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(false));
- EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
- EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
mEngine.setNeedsAnotherUpdateForTest(true);
@@ -262,9 +266,9 @@
TEST_F(CompositionTestPreComposition,
preCompositionSetsNeedsAnotherUpdateIfAtLeastOneLayerRequestsIt) {
- EXPECT_CALL(*mLayer1FE, onPreComposition(_, _)).WillOnce(Return(true));
- EXPECT_CALL(*mLayer2FE, onPreComposition(_, _)).WillOnce(Return(false));
- EXPECT_CALL(*mLayer3FE, onPreComposition(_, _)).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer1FE, onPreComposition(_)).WillOnce(Return(true));
+ EXPECT_CALL(*mLayer2FE, onPreComposition(_)).WillOnce(Return(false));
+ EXPECT_CALL(*mLayer3FE, onPreComposition(_)).WillOnce(Return(false));
mRefreshArgs.outputs = {mOutput1};
mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
@@ -483,5 +487,29 @@
mEngine.present(mRefreshArgs);
}
+struct CompositionEnginePostCompositionTest : public CompositionEngineTest {
+ sp<StrictMock<mock::LayerFE>> mLayer1FE = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> mLayer2FE = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> mLayer3FE = sp<StrictMock<mock::LayerFE>>::make();
+};
+
+TEST_F(CompositionEnginePostCompositionTest, postCompositionReleasesAllFences) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ EXPECT_CALL(*mLayer1FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
+ EXPECT_CALL(*mLayer2FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::FULFILLED));
+ EXPECT_CALL(*mLayer3FE, getReleaseFencePromiseStatus)
+ .WillOnce(Return(LayerFE::ReleaseFencePromiseStatus::INITIALIZED));
+ mRefreshArgs.layers = {mLayer1FE, mLayer2FE, mLayer3FE};
+
+ EXPECT_CALL(*mLayer1FE, setReleaseFence(_)).Times(0);
+ EXPECT_CALL(*mLayer2FE, setReleaseFence(_)).Times(0);
+ EXPECT_CALL(*mLayer3FE, setReleaseFence(_)).Times(1);
+
+ mEngine.postComposition(mRefreshArgs);
+}
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index a95a5c6..39163ea 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -1067,8 +1067,8 @@
EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_, _));
EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
-
- mDisplay->presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mDisplay->presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 575a30e..629d9f2 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -63,6 +63,7 @@
(override));
MOCK_METHOD2(presentAndGetReleaseFences,
status_t(HalDisplayId, std::optional<std::chrono::steady_clock::time_point>));
+ MOCK_METHOD(status_t, executeCommands, (HalDisplayId));
MOCK_METHOD2(setPowerMode, status_t(PhysicalDisplayId, hal::PowerMode));
MOCK_METHOD2(setActiveConfig, status_t(HalDisplayId, size_t));
MOCK_METHOD2(setColorTransform, status_t(HalDisplayId, const mat4&));
@@ -126,6 +127,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 799c7ed..c34168d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -2014,11 +2014,20 @@
MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
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,11 +2041,12 @@
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)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2052,11 +2062,12 @@
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)));
EXPECT_CALL(mOutput, finishFrame(_));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(false));
EXPECT_CALL(mOutput, renderCachedSets(Ref(args)));
mOutput.present(args);
@@ -2902,7 +2913,7 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
MOCK_METHOD0(prepareFrame, void());
MOCK_METHOD0(updateProtectedContentState, void());
MOCK_METHOD2(dequeueRenderBuffer,
@@ -2939,7 +2950,8 @@
mOutput.mState.isEnabled = false;
InSequence seq;
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2951,7 +2963,8 @@
InSequence seq;
EXPECT_CALL(mOutput, getDirtyRegion()).WillOnce(Return(kEmptyRegion));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2967,7 +2980,8 @@
EXPECT_CALL(mOutput, dequeueRenderBuffer(_, _));
EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion), _, _));
EXPECT_CALL(*mRenderSurface, queueBuffer(_, 1.f));
- EXPECT_CALL(mOutput, presentFrameAndReleaseLayers());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
EXPECT_CALL(mOutput, prepareFrame());
mOutput.devOptRepaintFlash(mRefreshArgs);
@@ -2985,10 +2999,14 @@
std::optional<base::unique_fd>(const Region&,
std::shared_ptr<renderengine::ExternalTexture>,
base::unique_fd&));
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
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 +3015,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 +3043,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 +3067,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 +3095,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 +3106,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 +3129,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));
}
@@ -3102,7 +3142,8 @@
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentFrame, compositionengine::Output::FrameFences());
+ MOCK_METHOD(compositionengine::Output::FrameFences, presentFrame, ());
+ MOCK_METHOD(void, executeCommands, ());
};
struct Layer {
@@ -3140,9 +3181,67 @@
};
TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
mOutput.mState.isEnabled = false;
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+ EXPECT_CALL(mOutput, presentFrame()).Times(0);
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledExecutesCommandsIfFlush) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = false;
+ EXPECT_CALL(mOutput, executeCommands());
+ EXPECT_CALL(mOutput, presentFrame()).Times(0);
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // This should only be called for disabled outputs. This test's goal is to verify this line;
+ // the other expectations help satisfy the StrictMocks.
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledDoNotExecuteCommands2) {
+ // Same test as ifEnabledDoNotExecuteCommands, but with this variable set to false.
+ constexpr bool kFlushEvenWhenDisabled = false;
+
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::flush_buffer_slots_to_uncache,
+ true);
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // This should only be called for disabled outputs. This test's goal is to verify this line;
+ // the other expectations help satisfy the StrictMocks.
+ EXPECT_CALL(mOutput, executeCommands()).Times(0);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
@@ -3160,10 +3259,13 @@
EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = true;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
// Simulate getting release fences from each layer, and ensure they are passed to the
// front-end layer interface for each layer correctly.
@@ -3202,10 +3304,56 @@
EXPECT_EQ(FenceResult(layer3Fence), futureFenceResult.get());
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSetInLayerFE) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+ // Simulate getting release fences from each layer, and ensure they are passed to the
+ // front-end layer interface for each layer correctly.
+
+ mOutput.mState.isEnabled = true;
+
+ // Create three unique fence instances
+ sp<Fence> layer1Fence = sp<Fence>::make();
+ sp<Fence> layer2Fence = sp<Fence>::make();
+ sp<Fence> layer3Fence = sp<Fence>::make();
+
+ Output::FrameFences frameFences;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Compare the pointers values of each fence to make sure the correct ones
+ // are passed. This happens to work with the current implementation, but
+ // would not survive certain calls like Fence::merge() which would return a
+ // new instance.
+ EXPECT_CALL(*mLayer1.layerFE, setReleaseFence(_))
+ .WillOnce([&layer1Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer1Fence), releaseFence);
+ });
+ EXPECT_CALL(*mLayer2.layerFE, setReleaseFence(_))
+ .WillOnce([&layer2Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer2Fence), releaseFence);
+ });
+ EXPECT_CALL(*mLayer3.layerFE, setReleaseFence(_))
+ .WillOnce([&layer3Fence](FenceResult releaseFence) {
+ EXPECT_EQ(FenceResult(layer3Fence), releaseFence);
+ });
+
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
+
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
@@ -3225,10 +3373,40 @@
EXPECT_CALL(*mLayer2.layerFE, onLayerDisplayed).WillOnce(Return());
EXPECT_CALL(*mLayer3.layerFE, onLayerDisplayed).WillOnce(Return());
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+}
+
+TEST_F(OutputPostFramebufferTest, setReleaseFencesIncludeClientTargetAcquireFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ Output::FrameFences frameFences;
+ frameFences.clientTargetAcquireFence = sp<Fence>::make();
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, sp<Fence>::make());
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, sp<Fence>::make());
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, sp<Fence>::make());
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Fence::merge is called, and since none of the fences are actually valid,
+ // Fence::NO_FENCE is returned and passed to each setReleaseFence() call.
+ // This is the best we can do without creating a real kernel fence object.
+ EXPECT_CALL(*mLayer1.layerFE, setReleaseFence).WillOnce(Return());
+ EXPECT_CALL(*mLayer2.layerFE, setReleaseFence).WillOnce(Return());
+ EXPECT_CALL(*mLayer3.layerFE, setReleaseFence).WillOnce(Return());
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
}
TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, false);
+ ASSERT_FALSE(FlagManager::getInstance().ce_fence_promise());
+
mOutput.mState.isEnabled = true;
mOutput.mState.usesClientComposition = true;
@@ -3270,7 +3448,57 @@
EXPECT_EQ(FenceResult(presentFence), futureFenceResult.get());
});
- mOutput.presentFrameAndReleaseLayers();
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
+
+ // After the call the list of released layers should have been cleared.
+ EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
+
+TEST_F(OutputPostFramebufferTest, setReleasedLayersSentPresentFence) {
+ SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::ce_fence_promise, true);
+ ASSERT_TRUE(FlagManager::getInstance().ce_fence_promise());
+
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ // This should happen even if there are no (current) output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // Load up the released layers with some mock instances
+ sp<StrictMock<mock::LayerFE>> releasedLayer1 = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> releasedLayer2 = sp<StrictMock<mock::LayerFE>>::make();
+ sp<StrictMock<mock::LayerFE>> releasedLayer3 = sp<StrictMock<mock::LayerFE>>::make();
+ Output::ReleasedLayers layers;
+ layers.push_back(releasedLayer1);
+ layers.push_back(releasedLayer2);
+ layers.push_back(releasedLayer3);
+ mOutput.setReleasedLayers(std::move(layers));
+
+ // Set up a fake present fence
+ sp<Fence> presentFence = sp<Fence>::make();
+ Output::FrameFences frameFences;
+ frameFences.presentFence = presentFence;
+
+ EXPECT_CALL(mOutput, presentFrame()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Each released layer should be given the presentFence.
+ EXPECT_CALL(*releasedLayer1, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+ EXPECT_CALL(*releasedLayer2, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+ EXPECT_CALL(*releasedLayer3, setReleaseFence(_))
+ .WillOnce([&presentFence](FenceResult fenceResult) {
+ EXPECT_EQ(FenceResult(presentFence), fenceResult);
+ });
+
+ constexpr bool kFlushEvenWhenDisabled = false;
+ mOutput.presentFrameAndReleaseLayers(kFlushEvenWhenDisabled);
// After the call the list of released layers should have been cleared.
EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
@@ -3293,9 +3521,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() {
@@ -3325,6 +3556,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> {
@@ -3590,10 +3823,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;
@@ -3618,14 +3898,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);
@@ -5057,8 +5341,9 @@
struct OutputPartialMock : public OutputPrepareFrameAsyncTest::OutputPartialMock {
// Set up the helper functions called by the function under test to use
// mock implementations.
- MOCK_METHOD0(presentFrameAndReleaseLayers, void());
- MOCK_METHOD0(presentFrameAndReleaseLayersAsync, ftl::Future<std::monostate>());
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled));
+ MOCK_METHOD(ftl::Future<std::monostate>, presentFrameAndReleaseLayersAsync,
+ (bool flushEvenWhenDisabled));
};
OutputPresentFrameAndReleaseLayersAsyncTest() {
mOutput->setDisplayColorProfileForTest(
@@ -5075,16 +5360,16 @@
};
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, notCalledWhenNotRequested) {
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync()).Times(0);
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(_)).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(1);
mOutput->present(mRefreshArgs);
}
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledWhenRequested) {
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(false))
.WillOnce(Return(ftl::yield<std::monostate>({})));
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(0);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(_)).Times(0);
mOutput->offloadPresentNextFrame();
mOutput->present(mRefreshArgs);
@@ -5092,9 +5377,10 @@
TEST_F(OutputPresentFrameAndReleaseLayersAsyncTest, calledForOneFrame) {
::testing::InSequence inseq;
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync())
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayersAsync(kFlushEvenWhenDisabled))
.WillOnce(Return(ftl::yield<std::monostate>({})));
- EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers()).Times(1);
+ EXPECT_CALL(*mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled)).Times(1);
mOutput->offloadPresentNextFrame();
mOutput->present(mRefreshArgs);
@@ -5175,5 +5461,59 @@
mOutput.updateProtectedContentState();
}
+struct OutputPresentFrameAndReleaseLayersTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test (and functions we can
+ // ignore) to use mock implementations.
+ MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(updateCompositionState,
+ void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(planComposition, void());
+ MOCK_METHOD1(writeCompositionState, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(setColorTransform, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(beginFrame, void());
+ MOCK_METHOD0(prepareFrame, void());
+ MOCK_METHOD0(prepareFrameAsync, GpuCompositionResult());
+ MOCK_METHOD1(devOptRepaintFlash, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD1(finishFrame, void(GpuCompositionResult&&));
+ MOCK_METHOD(void, presentFrameAndReleaseLayers, (bool flushEvenWhenDisabled), (override));
+ 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));
+ };
+
+ OutputPresentFrameAndReleaseLayersTest() {
+ EXPECT_CALL(mOutput, isPowerHintSessionEnabled()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, isPowerHintSessionGpuReportingEnabled()).WillRepeatedly(Return(true));
+ }
+
+ NiceMock<OutputPartialMock> mOutput;
+};
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, noBuffersToUncache) {
+ CompositionRefreshArgs args;
+ ASSERT_TRUE(args.bufferIdsToUncache.empty());
+ mOutput.editState().isEnabled = false;
+
+ constexpr bool kFlushEvenWhenDisabled = false;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+ mOutput.present(args);
+}
+
+TEST_F(OutputPresentFrameAndReleaseLayersTest, buffersToUncache) {
+ CompositionRefreshArgs args;
+ args.bufferIdsToUncache.push_back(1);
+ mOutput.editState().isEnabled = false;
+
+ constexpr bool kFlushEvenWhenDisabled = true;
+ EXPECT_CALL(mOutput, presentFrameAndReleaseLayers(kFlushEvenWhenDisabled));
+
+ mOutput.present(args);
+}
+
} // namespace
} // namespace android::compositionengine
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..a6a9bec
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.cpp
@@ -0,0 +1,240 @@
+/*
+ * 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 "DisplayHardware/HWComposer.h"
+
+#include <common/FlagManager.h>
+#include <ftl/concat.h>
+#include <ftl/expected.h>
+#include <log/log.h>
+
+namespace android::display {
+
+template <size_t N>
+inline std::string DisplayModeController::Display::concatId(const char (&str)[N]) const {
+ return std::string(ftl::Concat(str, ' ', snapshot.get().displayId().value).str());
+}
+
+DisplayModeController::Display::Display(DisplaySnapshotRef snapshot,
+ RefreshRateSelectorPtr selectorPtr)
+ : snapshot(snapshot),
+ selectorPtr(std::move(selectorPtr)),
+ pendingModeFpsTrace(concatId("PendingModeFps")),
+ activeModeFpsTrace(concatId("ActiveModeFps")),
+ renderRateFpsTrace(concatId("RenderRateFps")),
+ hasDesiredModeTrace(concatId("HasDesiredMode"), false) {}
+
+void DisplayModeController::registerDisplay(PhysicalDisplayId displayId,
+ DisplaySnapshotRef snapshotRef,
+ RefreshRateSelectorPtr selectorPtr) {
+ std::lock_guard lock(mDisplayLock);
+ mDisplays.emplace_or_replace(displayId, std::make_unique<Display>(snapshotRef, selectorPtr));
+}
+
+void DisplayModeController::registerDisplay(DisplaySnapshotRef snapshotRef,
+ DisplayModeId activeModeId,
+ scheduler::RefreshRateSelector::Config config) {
+ const auto& snapshot = snapshotRef.get();
+ const auto displayId = snapshot.displayId();
+
+ std::lock_guard lock(mDisplayLock);
+ mDisplays.emplace_or_replace(displayId,
+ std::make_unique<Display>(snapshotRef, snapshot.displayModes(),
+ activeModeId, config));
+}
+
+void DisplayModeController::unregisterDisplay(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
+ const bool ok = mDisplays.erase(displayId);
+ ALOGE_IF(!ok, "%s: Unknown display %s", __func__, to_string(displayId).c_str());
+}
+
+auto DisplayModeController::selectorPtrFor(PhysicalDisplayId displayId) const
+ -> RefreshRateSelectorPtr {
+ std::lock_guard lock(mDisplayLock);
+ return mDisplays.get(displayId)
+ .transform([](const DisplayPtr& displayPtr) { return displayPtr->selectorPtr; })
+ .value_or(nullptr);
+}
+
+auto DisplayModeController::setDesiredMode(PhysicalDisplayId displayId,
+ DisplayModeRequest&& desiredMode) -> DesiredModeAction {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DesiredModeAction::None)).get();
+
+ {
+ ATRACE_NAME(displayPtr->concatId(__func__).c_str());
+ ALOGD("%s %s", displayPtr->concatId(__func__).c_str(), to_string(desiredMode).c_str());
+
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+
+ if (auto& desiredModeOpt = displayPtr->desiredModeOpt) {
+ // A mode transition was already scheduled, so just override the desired mode.
+ const bool emitEvent = desiredModeOpt->emitEvent;
+ const bool force = desiredModeOpt->force;
+ desiredModeOpt = std::move(desiredMode);
+ desiredModeOpt->emitEvent |= emitEvent;
+ if (FlagManager::getInstance().connected_display()) {
+ desiredModeOpt->force |= force;
+ }
+ return DesiredModeAction::None;
+ }
+
+ // If the desired mode is already active...
+ const auto activeMode = displayPtr->selectorPtr->getActiveMode();
+ if (const auto& desiredModePtr = desiredMode.mode.modePtr;
+ !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
+ if (activeMode == desiredMode.mode) {
+ return DesiredModeAction::None;
+ }
+
+ // ...but the render rate changed:
+ setActiveModeLocked(displayId, desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
+ desiredMode.mode.fps);
+ return DesiredModeAction::InitiateRenderRateSwitch;
+ }
+
+ // Restore peak render rate to schedule the next frame as soon as possible.
+ setActiveModeLocked(displayId, activeMode.modePtr->getId(),
+ activeMode.modePtr->getVsyncRate(), activeMode.modePtr->getPeakFps());
+
+ // Initiate a mode change.
+ displayPtr->desiredModeOpt = std::move(desiredMode);
+ displayPtr->hasDesiredModeTrace = true;
+ }
+
+ return DesiredModeAction::InitiateDisplayModeSwitch;
+}
+
+auto DisplayModeController::getDesiredMode(PhysicalDisplayId displayId) const
+ -> DisplayModeRequestOpt {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->desiredModeOpt;
+ }
+}
+
+auto DisplayModeController::getPendingMode(PhysicalDisplayId displayId) const
+ -> DisplayModeRequestOpt {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr =
+ FTL_EXPECT(mDisplays.get(displayId).ok_or(DisplayModeRequestOpt())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->pendingModeOpt;
+ }
+}
+
+bool DisplayModeController::isModeSetPending(PhysicalDisplayId displayId) const {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ return displayPtr->isModeSetPending;
+ }
+}
+
+scheduler::FrameRateMode DisplayModeController::getActiveMode(PhysicalDisplayId displayId) const {
+ return selectorPtrFor(displayId)->getActiveMode();
+}
+
+void DisplayModeController::clearDesiredMode(PhysicalDisplayId displayId) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ displayPtr->desiredModeOpt.reset();
+ displayPtr->hasDesiredModeTrace = false;
+ }
+}
+
+bool DisplayModeController::initiateModeChange(PhysicalDisplayId displayId,
+ DisplayModeRequest&& desiredMode,
+ const hal::VsyncPeriodChangeConstraints& constraints,
+ hal::VsyncPeriodChangeTimeline& outTimeline) {
+ std::lock_guard lock(mDisplayLock);
+ const auto& displayPtr = FTL_EXPECT(mDisplays.get(displayId).ok_or(false)).get();
+
+ // TODO: b/255635711 - Flow the DisplayModeRequest through the desired/pending/active states.
+ // For now, `desiredMode` and `desiredModeOpt` are one and the same, but the latter is not
+ // cleared until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been
+ // consumed at this point, so clear the `force` flag to prevent an endless loop of
+ // `initiateModeChange`.
+ if (FlagManager::getInstance().connected_display()) {
+ std::scoped_lock lock(displayPtr->desiredModeLock);
+ if (displayPtr->desiredModeOpt) {
+ displayPtr->desiredModeOpt->force = false;
+ }
+ }
+
+ displayPtr->pendingModeOpt = std::move(desiredMode);
+ displayPtr->isModeSetPending = true;
+
+ const auto& mode = *displayPtr->pendingModeOpt->mode.modePtr;
+
+ if (mComposerPtr->setActiveModeWithConstraints(displayId, mode.getHwcId(), constraints,
+ &outTimeline) != OK) {
+ return false;
+ }
+
+ ATRACE_INT(displayPtr->pendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
+ return true;
+}
+
+void DisplayModeController::finalizeModeChange(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ std::lock_guard lock(mDisplayLock);
+ setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+ displayPtr->isModeSetPending = false;
+}
+
+void DisplayModeController::setActiveMode(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ std::lock_guard lock(mDisplayLock);
+ setActiveModeLocked(displayId, modeId, vsyncRate, renderFps);
+}
+
+void DisplayModeController::setActiveModeLocked(PhysicalDisplayId displayId, DisplayModeId modeId,
+ Fps vsyncRate, Fps renderFps) {
+ const auto& displayPtr = FTL_TRY(mDisplays.get(displayId).ok_or(ftl::Unit())).get();
+
+ ATRACE_INT(displayPtr->activeModeFpsTrace.c_str(), vsyncRate.getIntValue());
+ ATRACE_INT(displayPtr->renderRateFpsTrace.c_str(), renderFps.getIntValue());
+
+ displayPtr->selectorPtr->setActiveMode(modeId, renderFps);
+
+ if (mActiveModeListener) {
+ mActiveModeListener(displayId, vsyncRate, renderFps);
+ }
+}
+
+} // namespace android::display
diff --git a/services/surfaceflinger/Display/DisplayModeController.h b/services/surfaceflinger/Display/DisplayModeController.h
new file mode 100644
index 0000000..258b04b
--- /dev/null
+++ b/services/surfaceflinger/Display/DisplayModeController.h
@@ -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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <utility>
+
+#include <android-base/thread_annotations.h>
+#include <ftl/function.h>
+#include <ftl/optional.h>
+#include <ui/DisplayId.h>
+#include <ui/DisplayMap.h>
+
+#include "Display/DisplayModeRequest.h"
+#include "Display/DisplaySnapshotRef.h"
+#include "DisplayHardware/DisplayMode.h"
+#include "Scheduler/RefreshRateSelector.h"
+#include "ThreadContext.h"
+#include "TracedOrdinal.h"
+
+namespace android {
+class HWComposer;
+} // namespace android
+
+namespace android::display {
+
+// Selects the DisplayMode of each physical display, in accordance with DisplayManager policy and
+// certain heuristic signals.
+class DisplayModeController {
+public:
+ using ActiveModeListener = ftl::Function<void(PhysicalDisplayId, Fps vsyncRate, Fps renderFps)>;
+
+ DisplayModeController() = default;
+
+ void setHwComposer(HWComposer* composerPtr) { mComposerPtr = composerPtr; }
+ void setActiveModeListener(const ActiveModeListener& listener) {
+ mActiveModeListener = listener;
+ }
+
+ // TODO: b/241285876 - Remove once ownership is no longer shared with DisplayDevice.
+ using RefreshRateSelectorPtr = std::shared_ptr<scheduler::RefreshRateSelector>;
+
+ // Used by tests to inject an existing RefreshRateSelector.
+ // TODO: b/241285876 - Remove this.
+ void registerDisplay(PhysicalDisplayId, DisplaySnapshotRef, RefreshRateSelectorPtr)
+ EXCLUDES(mDisplayLock);
+
+ // The referenced DisplaySnapshot must outlive the registration.
+ void registerDisplay(DisplaySnapshotRef, DisplayModeId, scheduler::RefreshRateSelector::Config)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ // Returns `nullptr` if the display is no longer registered (or never was).
+ RefreshRateSelectorPtr selectorPtrFor(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+ enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
+
+ DesiredModeAction setDesiredMode(PhysicalDisplayId, DisplayModeRequest&&)
+ EXCLUDES(mDisplayLock);
+
+ using DisplayModeRequestOpt = ftl::Optional<DisplayModeRequest>;
+
+ DisplayModeRequestOpt getDesiredMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+ void clearDesiredMode(PhysicalDisplayId) EXCLUDES(mDisplayLock);
+
+ DisplayModeRequestOpt getPendingMode(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+ bool isModeSetPending(PhysicalDisplayId) const REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
+
+ scheduler::FrameRateMode getActiveMode(PhysicalDisplayId) const EXCLUDES(mDisplayLock);
+
+ bool initiateModeChange(PhysicalDisplayId, DisplayModeRequest&&,
+ const hal::VsyncPeriodChangeConstraints&,
+ hal::VsyncPeriodChangeTimeline& outTimeline)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ void finalizeModeChange(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+
+ void setActiveMode(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ EXCLUDES(mDisplayLock);
+
+private:
+ struct Display {
+ template <size_t N>
+ std::string concatId(const char (&)[N]) const;
+
+ Display(DisplaySnapshotRef, RefreshRateSelectorPtr);
+ 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;
+
+ const std::string pendingModeFpsTrace;
+ const std::string activeModeFpsTrace;
+ const std::string renderRateFpsTrace;
+
+ std::mutex desiredModeLock;
+ DisplayModeRequestOpt desiredModeOpt GUARDED_BY(desiredModeLock);
+ TracedOrdinal<bool> hasDesiredModeTrace GUARDED_BY(desiredModeLock);
+
+ DisplayModeRequestOpt pendingModeOpt GUARDED_BY(kMainThreadContext);
+ bool isModeSetPending GUARDED_BY(kMainThreadContext) = false;
+ };
+
+ using DisplayPtr = std::unique_ptr<Display>;
+
+ void setActiveModeLocked(PhysicalDisplayId, DisplayModeId, Fps vsyncRate, Fps renderFps)
+ REQUIRES(mDisplayLock);
+
+ // Set once when initializing the DisplayModeController, which the HWComposer must outlive.
+ HWComposer* mComposerPtr = nullptr;
+
+ ActiveModeListener mActiveModeListener;
+
+ mutable std::mutex mDisplayLock;
+ ui::PhysicalDisplayMap<PhysicalDisplayId, DisplayPtr> mDisplays GUARDED_BY(mDisplayLock);
+};
+
+} // namespace android::display
diff --git a/libs/gui/aidl/android/gui/LayerDebugInfo.aidl b/services/surfaceflinger/Display/DisplaySnapshotRef.h
similarity index 75%
rename from libs/gui/aidl/android/gui/LayerDebugInfo.aidl
rename 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..05f4da2 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -24,7 +24,6 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
@@ -36,6 +35,7 @@
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/OutputCompositionState.h>
#include <configstore/Utils.h>
+#include <ftl/concat.h>
#include <log/log.h>
#include <system/window.h>
@@ -64,15 +64,11 @@
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mCompositionDisplay{args.compositionDisplay},
- mPendingModeFpsTrace(concatId("PendingModeFps")),
- mActiveModeFpsTrace(concatId("ActiveModeFps")),
- mRenderRateFpsTrace(concatId("RenderRateFps")),
mPhysicalOrientation(args.physicalOrientation),
mPowerMode(ftl::Concat("PowerMode ", getId().value).c_str(), args.initialPowerMode),
mIsPrimary(args.isPrimary),
mRequestedRefreshRate(args.requestedRefreshRate),
- mRefreshRateSelector(std::move(args.refreshRateSelector)),
- mHasDesiredModeTrace(concatId("HasDesiredMode"), false) {
+ mRefreshRateSelector(std::move(args.refreshRateSelector)) {
mCompositionDisplay->editState().isSecure = args.isSecure;
mCompositionDisplay->editState().isProtected = args.isProtected;
mCompositionDisplay->createRenderSurface(
@@ -137,7 +133,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
@@ -204,47 +200,6 @@
return mPowerMode != hal::PowerMode::OFF;
}
-void DisplayDevice::setActiveMode(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- ATRACE_INT(mActiveModeFpsTrace.c_str(), vsyncRate.getIntValue());
- ATRACE_INT(mRenderRateFpsTrace.c_str(), renderFps.getIntValue());
-
- mRefreshRateSelector->setActiveMode(modeId, renderFps);
- updateRefreshRateOverlayRate(vsyncRate, renderFps);
-}
-
-bool DisplayDevice::initiateModeChange(display::DisplayModeRequest&& desiredMode,
- const hal::VsyncPeriodChangeConstraints& constraints,
- hal::VsyncPeriodChangeTimeline& outTimeline) {
- // TODO(b/255635711): Flow the DisplayModeRequest through the desired/pending/active states. For
- // now, `desiredMode` and `mDesiredModeOpt` are one and the same, but the latter is not cleared
- // until the next `SF::initiateDisplayModeChanges`. However, the desired mode has been consumed
- // at this point, so clear the `force` flag to prevent an endless loop of `initiateModeChange`.
- if (FlagManager::getInstance().connected_display()) {
- std::scoped_lock lock(mDesiredModeLock);
- if (mDesiredModeOpt) {
- mDesiredModeOpt->force = false;
- }
- }
-
- mPendingModeOpt = std::move(desiredMode);
- mIsModeSetPending = true;
-
- const auto& mode = *mPendingModeOpt->mode.modePtr;
-
- if (mHwComposer.setActiveModeWithConstraints(getPhysicalId(), mode.getHwcId(), constraints,
- &outTimeline) != OK) {
- return false;
- }
-
- ATRACE_INT(mPendingModeFpsTrace.c_str(), mode.getVsyncRate().getIntValue());
- return true;
-}
-
-void DisplayDevice::finalizeModeChange(DisplayModeId modeId, Fps vsyncRate, Fps renderFps) {
- setActiveMode(modeId, vsyncRate, renderFps);
- mIsModeSetPending = false;
-}
-
nsecs_t DisplayDevice::getVsyncPeriodFromHWC() const {
const auto physicalId = getPhysicalId();
if (!mHwComposer.isConnected(physicalId)) {
@@ -450,8 +405,9 @@
}
}
-void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner,
- bool showRenderRate, bool showInMiddle) {
+void DisplayDevice::enableRefreshRateOverlay(bool enable, bool setByHwc, Fps refreshRate,
+ Fps renderFps, bool showSpinner, bool showRenderRate,
+ bool showInMiddle) {
if (!enable) {
mRefreshRateOverlay.reset();
return;
@@ -474,22 +430,23 @@
features |= RefreshRateOverlay::Features::SetByHwc;
}
- // TODO(b/296636258) Update to use the render rate range in VRR mode.
const auto fpsRange = mRefreshRateSelector->getSupportedRefreshRateRange();
mRefreshRateOverlay = RefreshRateOverlay::create(fpsRange, features);
if (mRefreshRateOverlay) {
mRefreshRateOverlay->setLayerStack(getLayerStack());
mRefreshRateOverlay->setViewport(getSize());
- updateRefreshRateOverlayRate(getActiveMode().modePtr->getVsyncRate(), getActiveMode().fps,
- setByHwc);
+ updateRefreshRateOverlayRate(refreshRate, renderFps, setByHwc);
}
}
-void DisplayDevice::updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc) {
+void DisplayDevice::updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, bool setByHwc) {
ATRACE_CALL();
if (mRefreshRateOverlay) {
if (!mRefreshRateOverlay->isSetByHwc() || setByHwc) {
- mRefreshRateOverlay->changeRefreshRate(vsyncRate, renderFps);
+ if (mRefreshRateSelector->isVrrDevice() && !mRefreshRateOverlay->isSetByHwc()) {
+ refreshRate = renderFps;
+ }
+ mRefreshRateOverlay->changeRefreshRate(refreshRate, renderFps);
} else {
mRefreshRateOverlay->changeRenderRate(renderFps);
}
@@ -510,6 +467,12 @@
return false;
}
+void DisplayDevice::onVrrIdle(bool idle) {
+ if (mRefreshRateOverlay) {
+ mRefreshRateOverlay->onVrrIdle(idle);
+ }
+}
+
void DisplayDevice::animateOverlay() {
if (mRefreshRateOverlay) {
mRefreshRateOverlay->animate();
@@ -529,59 +492,6 @@
}
}
-auto DisplayDevice::setDesiredMode(display::DisplayModeRequest&& desiredMode) -> DesiredModeAction {
- ATRACE_NAME(concatId(__func__).c_str());
- ALOGD("%s %s", concatId(__func__).c_str(), to_string(desiredMode).c_str());
-
- std::scoped_lock lock(mDesiredModeLock);
- if (mDesiredModeOpt) {
- // A mode transition was already scheduled, so just override the desired mode.
- const bool emitEvent = mDesiredModeOpt->emitEvent;
- const bool force = mDesiredModeOpt->force;
- mDesiredModeOpt = std::move(desiredMode);
- mDesiredModeOpt->emitEvent |= emitEvent;
- if (FlagManager::getInstance().connected_display()) {
- mDesiredModeOpt->force |= force;
- }
- return DesiredModeAction::None;
- }
-
- // If the desired mode is already active...
- const auto activeMode = refreshRateSelector().getActiveMode();
- if (const auto& desiredModePtr = desiredMode.mode.modePtr;
- !desiredMode.force && activeMode.modePtr->getId() == desiredModePtr->getId()) {
- if (activeMode == desiredMode.mode) {
- return DesiredModeAction::None;
- }
-
- // ...but the render rate changed:
- setActiveMode(desiredModePtr->getId(), desiredModePtr->getVsyncRate(),
- desiredMode.mode.fps);
- 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());
-
- // Initiate a mode change.
- mDesiredModeOpt = std::move(desiredMode);
- mHasDesiredModeTrace = true;
- return DesiredModeAction::InitiateDisplayModeSwitch;
-}
-
-auto DisplayDevice::getDesiredMode() const -> DisplayModeRequestOpt {
- std::scoped_lock lock(mDesiredModeLock);
- return mDesiredModeOpt;
-}
-
-void DisplayDevice::clearDesiredMode() {
- std::scoped_lock lock(mDesiredModeLock);
- mDesiredModeOpt.reset();
- mHasDesiredModeTrace = false;
-}
-
void DisplayDevice::adjustRefreshRate(Fps pacesetterDisplayRefreshRate) {
using fps_approx_ops::operator<=;
if (mRequestedRefreshRate <= 0_Hz) {
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index edd57cc..1b8a3a8 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -23,8 +23,6 @@
#include <android-base/thread_annotations.h>
#include <android/native_window.h>
#include <binder/IBinder.h>
-#include <ftl/concat.h>
-#include <ftl/optional.h>
#include <gui/LayerState.h>
#include <math/mat4.h>
#include <renderengine/RenderEngine.h>
@@ -42,7 +40,6 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include "Display/DisplayModeRequest.h"
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/Hal.h"
#include "DisplayHardware/PowerAdvisor.h"
@@ -89,7 +86,7 @@
return mCompositionDisplay;
}
- bool isVirtual() const { return VirtualDisplayId::tryCast(getId()).has_value(); }
+ bool isVirtual() const { return getId().isVirtual(); }
bool isPrimary() const { return mIsPrimary; }
// isSecure indicates whether this display can be trusted to display
@@ -183,37 +180,6 @@
ui::Dataspace getCompositionDataSpace() const;
- /* ------------------------------------------------------------------------
- * Display mode management.
- */
-
- enum class DesiredModeAction { None, InitiateDisplayModeSwitch, InitiateRenderRateSwitch };
-
- DesiredModeAction setDesiredMode(display::DisplayModeRequest&&) EXCLUDES(mDesiredModeLock);
-
- using DisplayModeRequestOpt = ftl::Optional<display::DisplayModeRequest>;
-
- DisplayModeRequestOpt getDesiredMode() const EXCLUDES(mDesiredModeLock);
- void clearDesiredMode() EXCLUDES(mDesiredModeLock);
-
- DisplayModeRequestOpt getPendingMode() const REQUIRES(kMainThreadContext) {
- return mPendingModeOpt;
- }
- bool isModeSetPending() const REQUIRES(kMainThreadContext) { return mIsModeSetPending; }
-
- scheduler::FrameRateMode getActiveMode() const REQUIRES(kMainThreadContext) {
- return mRefreshRateSelector->getActiveMode();
- }
-
- void setActiveMode(DisplayModeId, Fps vsyncRate, Fps renderFps);
-
- bool initiateModeChange(display::DisplayModeRequest&&, const hal::VsyncPeriodChangeConstraints&,
- hal::VsyncPeriodChangeTimeline& outTimeline)
- REQUIRES(kMainThreadContext);
-
- void finalizeModeChange(DisplayModeId, Fps vsyncRate, Fps renderFps)
- REQUIRES(kMainThreadContext);
-
scheduler::RefreshRateSelector& refreshRateSelector() const { return *mRefreshRateSelector; }
// Extends the lifetime of the RefreshRateSelector, so it can outlive this DisplayDevice.
@@ -221,14 +187,16 @@
return mRefreshRateSelector;
}
- void animateOverlay();
-
// Enables an overlay to be displayed with the current refresh rate
- void enableRefreshRateOverlay(bool enable, bool setByHwc, bool showSpinner, bool showRenderRate,
- bool showInMiddle) REQUIRES(kMainThreadContext);
- void updateRefreshRateOverlayRate(Fps vsyncRate, Fps renderFps, bool setByHwc = false);
+ // TODO(b/241285876): Move overlay to DisplayModeController.
+ void enableRefreshRateOverlay(bool enable, bool setByHwc, Fps refreshRate, Fps renderFps,
+ bool showSpinner, bool showRenderRate, bool showInMiddle)
+ REQUIRES(kMainThreadContext);
+ void updateRefreshRateOverlayRate(Fps refreshRate, Fps renderFps, bool setByHwc = false);
bool isRefreshRateOverlayEnabled() const { return mRefreshRateOverlay != nullptr; }
+ void animateOverlay();
bool onKernelTimerChanged(std::optional<DisplayModeId>, bool timerExpired);
+ void onVrrIdle(bool idle);
// Enables an overlay to be display with the hdr/sdr ratio
void enableHdrSdrRatioOverlay(bool enable) REQUIRES(kMainThreadContext);
@@ -249,11 +217,6 @@
void dump(utils::Dumper&) const;
private:
- template <size_t N>
- inline std::string concatId(const char (&str)[N]) const {
- return std::string(ftl::Concat(str, ' ', getId().value).str());
- }
-
const sp<SurfaceFlinger> mFlinger;
HWComposer& mHwComposer;
const wp<IBinder> mDisplayToken;
@@ -262,9 +225,6 @@
const std::shared_ptr<compositionengine::Display> mCompositionDisplay;
std::string mDisplayName;
- std::string mPendingModeFpsTrace;
- std::string mActiveModeFpsTrace;
- std::string mRenderRateFpsTrace;
const ui::Rotation mPhysicalOrientation;
ui::Rotation mOrientation = ui::ROTATION_0;
@@ -296,13 +256,6 @@
std::unique_ptr<HdrSdrRatioOverlay> mHdrSdrRatioOverlay;
// This parameter is only used for hdr/sdr ratio overlay
float mHdrSdrRatio = 1.0f;
-
- mutable std::mutex mDesiredModeLock;
- DisplayModeRequestOpt mDesiredModeOpt GUARDED_BY(mDesiredModeLock);
- TracedOrdinal<bool> mHasDesiredModeTrace GUARDED_BY(mDesiredModeLock);
-
- DisplayModeRequestOpt mPendingModeOpt GUARDED_BY(kMainThreadContext);
- bool mIsModeSetPending GUARDED_BY(kMainThreadContext) = false;
};
struct DisplayDeviceState {
@@ -329,6 +282,7 @@
uint32_t width = 0;
uint32_t height = 0;
std::string displayName;
+ std::string uniqueId;
bool isSecure = false;
bool isProtected = false;
// Refer to DisplayDevice::mRequestedRefreshRate, for virtual display only
@@ -363,7 +317,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..3d285a8 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 {
@@ -602,6 +600,13 @@
return NO_ERROR;
}
+status_t HWComposer::executeCommands(HalDisplayId displayId) {
+ auto& hwcDisplay = mDisplayData[displayId].hwcDisplay;
+ auto error = static_cast<hal::Error>(mComposer->executeCommands(hwcDisplay->getId()));
+ RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
status_t HWComposer::setPowerMode(PhysicalDisplayId displayId, hal::PowerMode mode) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
@@ -964,8 +969,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..9368b7b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -160,6 +160,8 @@
HalDisplayId,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) = 0;
+ virtual status_t executeCommands(HalDisplayId) = 0;
+
// set power mode
virtual status_t setPowerMode(PhysicalDisplayId, hal::PowerMode) = 0;
@@ -269,6 +271,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,
@@ -359,6 +363,8 @@
HalDisplayId,
std::optional<std::chrono::steady_clock::time_point> earliestPresentTime) override;
+ status_t executeCommands(HalDisplayId) override;
+
// set power mode
status_t setPowerMode(PhysicalDisplayId, hal::PowerMode mode) override;
@@ -468,6 +474,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 a0c943b..6c1a813 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -46,9 +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;
@@ -139,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() {
@@ -159,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");
@@ -204,21 +200,99 @@
return *mSupportsHintSession;
}
+bool PowerAdvisor::shouldCreateSessionWithConfig() {
+ 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() {
if (mHintSession == nullptr && !mHintSessionThreadIds.empty() && usePowerHintSession()) {
- auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
- mHintSessionThreadIds, mTargetDuration.ns());
-
- if (ret.isOk()) {
- mHintSession = ret.value();
+ if (shouldCreateSessionWithConfig()) {
+ auto ret = getPowerHal().createHintSessionWithConfig(getpid(),
+ static_cast<int32_t>(getuid()),
+ mHintSessionThreadIds,
+ mTargetDuration.ns(),
+ SessionTag::SURFACEFLINGER,
+ &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()) {
+ ALOGI("Hint session with config is unsupported, falling back to a legacy session");
+ mSessionConfigSupported = false;
+ }
+ mFirstConfigSupportCheck = false;
+ }
+ // Immediately try original method after, in case the first way returned unsupported
+ if (mHintSession == nullptr && !shouldCreateSessionWithConfig()) {
+ auto ret = getPowerHal().createHintSession(getpid(), static_cast<int32_t>(getuid()),
+ mHintSessionThreadIds, mTargetDuration.ns());
+ if (ret.isOk()) {
+ mHintSession = ret.value();
+ }
}
}
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();
@@ -226,14 +300,19 @@
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",
- ret.getDescription().c_str());
+ ret.errorMessage());
mHintSession = nullptr;
}
}
@@ -246,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();
@@ -274,34 +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.getDescription().c_str());
- 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;
}
@@ -317,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.
@@ -342,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,
@@ -368,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) {
@@ -378,8 +529,8 @@
}
void PowerAdvisor::setSfPresentTiming(TimePoint presentFenceTime, TimePoint presentEndTime) {
- mLastSfPresentEndTime = presentEndTime;
mLastPresentFenceTime = presentFenceTime;
+ mLastSfPresentEndTime = presentEndTime;
}
void PowerAdvisor::setFrameDelay(Duration frameDelayDuration) {
@@ -420,7 +571,7 @@
return sortedDisplays;
}
-std::optional<Duration> PowerAdvisor::estimateWorkDuration() {
+std::optional<WorkDuration> PowerAdvisor::estimateWorkDuration() {
if (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull()) {
return std::nullopt;
}
@@ -439,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) {
@@ -455,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;
@@ -477,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
@@ -484,9 +629,7 @@
estimatedGpuEndTime.value_or(TimePoint{0ns})) +
gpuTiming->duration;
}
- previousDisplayTiming = displayTiming;
}
- ATRACE_INT64("Idle duration", idleDuration.ns());
TimePoint estimatedFlingerEndTime = mLastSfPresentEndTime;
@@ -499,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) {
@@ -558,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 bbe51cc0..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,15 +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
@@ -259,17 +266,25 @@
std::optional<bool> mSupportsHintSession;
std::mutex mHintSessionMutex;
- std::shared_ptr<aidl::android::hardware::power::IPowerHintSession> mHintSession
- GUARDED_BY(mHintSessionMutex) = nullptr;
+ std::shared_ptr<power::PowerHintSessionWrapper> mHintSession GUARDED_BY(mHintSessionMutex) =
+ nullptr;
// Initialize to true so we try to call, to check if it's supported
bool mHasExpensiveRendering = true;
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;
@@ -278,6 +293,13 @@
std::promise<bool> mDelayReportActualMutexAcquisitonPromise;
bool mTimingTestingMode = false;
+ // Hint session configuration data
+ aidl::android::hardware::power::SessionConfig mSessionConfig;
+
+ // Whether createHintSessionWithConfig is supported, assume true until it fails
+ bool mSessionConfigSupported = true;
+ bool mFirstConfigSupportCheck = true;
+
// Whether we should emit ATRACE_INT data for hint sessions
static const bool sTraceHintSessionData;
@@ -295,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/DisplayRenderArea.cpp b/services/surfaceflinger/DisplayRenderArea.cpp
index 55b395b..c63c738 100644
--- a/services/surfaceflinger/DisplayRenderArea.cpp
+++ b/services/surfaceflinger/DisplayRenderArea.cpp
@@ -22,22 +22,20 @@
std::unique_ptr<RenderArea> DisplayRenderArea::create(wp<const DisplayDevice> displayWeak,
const Rect& sourceCrop, ui::Size reqSize,
ui::Dataspace reqDataSpace,
- bool hintForSeamlessTransition,
- bool allowSecureLayers) {
+ ftl::Flags<Options> options) {
if (auto display = displayWeak.promote()) {
// Using new to access a private constructor.
- return std::unique_ptr<DisplayRenderArea>(
- new DisplayRenderArea(std::move(display), sourceCrop, reqSize, reqDataSpace,
- hintForSeamlessTransition, allowSecureLayers));
+ return std::unique_ptr<DisplayRenderArea>(new DisplayRenderArea(std::move(display),
+ sourceCrop, reqSize,
+ reqDataSpace, options));
}
return nullptr;
}
DisplayRenderArea::DisplayRenderArea(sp<const DisplayDevice> display, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace reqDataSpace,
- bool hintForSeamlessTransition, bool allowSecureLayers)
- : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, hintForSeamlessTransition,
- allowSecureLayers),
+ ftl::Flags<Options> options)
+ : RenderArea(reqSize, CaptureFill::OPAQUE, reqDataSpace, options),
mDisplay(std::move(display)),
mSourceCrop(sourceCrop) {}
@@ -46,7 +44,7 @@
}
bool DisplayRenderArea::isSecure() const {
- return mAllowSecureLayers && mDisplay->isSecure();
+ return mOptions.test(Options::CAPTURE_SECURE_LAYERS) && mDisplay->isSecure();
}
sp<const DisplayDevice> DisplayRenderArea::getDisplayDevice() const {
diff --git a/services/surfaceflinger/DisplayRenderArea.h b/services/surfaceflinger/DisplayRenderArea.h
index 4555a9e..677d019 100644
--- a/services/surfaceflinger/DisplayRenderArea.h
+++ b/services/surfaceflinger/DisplayRenderArea.h
@@ -29,8 +29,7 @@
public:
static std::unique_ptr<RenderArea> create(wp<const DisplayDevice>, const Rect& sourceCrop,
ui::Size reqSize, ui::Dataspace,
- bool hintForSeamlessTransition,
- bool allowSecureLayers = true);
+ ftl::Flags<Options> options);
const ui::Transform& getTransform() const override;
bool isSecure() const override;
@@ -39,7 +38,7 @@
private:
DisplayRenderArea(sp<const DisplayDevice>, const Rect& sourceCrop, ui::Size reqSize,
- ui::Dataspace, bool hintForSeamlessTransition, bool allowSecureLayers = true);
+ ui::Dataspace, ftl::Flags<Options> options);
const sp<const DisplayDevice> mDisplay;
const Rect mSourceCrop;
diff --git a/services/surfaceflinger/Effects/Daltonizer.cpp b/services/surfaceflinger/Effects/Daltonizer.cpp
index a7090c5..65f2605 100644
--- a/services/surfaceflinger/Effects/Daltonizer.cpp
+++ b/services/surfaceflinger/Effects/Daltonizer.cpp
@@ -37,6 +37,18 @@
}
}
+void Daltonizer::setLevel(int32_t level) {
+ if (level < 0 || level > 10) {
+ return;
+ }
+
+ float newLevel = level / 10.0f;
+ if (std::fabs(mLevel - newLevel) > 0.09f) {
+ mDirty = true;
+ }
+ mLevel = newLevel;
+}
+
const mat4& Daltonizer::operator()() {
if (mDirty) {
mDirty = false;
@@ -117,25 +129,24 @@
// a color blind user and "spread" this error onto the healthy cones.
// The matrices below perform this last step and have been chosen arbitrarily.
- // The amount of correction can be adjusted here.
-
+ // Scale 0 represents no change (mColorTransform is identical matrix).
// error spread for protanopia
- const mat4 errp( 1.0, 0.7, 0.7, 0,
- 0.0, 1.0, 0.0, 0,
- 0.0, 0.0, 1.0, 0,
- 0, 0, 0, 1);
+ const mat4 errp(1.0, mLevel, mLevel, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
// error spread for deuteranopia
- const mat4 errd( 1.0, 0.0, 0.0, 0,
- 0.7, 1.0, 0.7, 0,
- 0.0, 0.0, 1.0, 0,
- 0, 0, 0, 1);
+ const mat4 errd( 1.0, 0.0, 0.0, 0.0,
+ mLevel, 1.0, mLevel, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
// error spread for tritanopia
- const mat4 errt( 1.0, 0.0, 0.0, 0,
- 0.0, 1.0, 0.0, 0,
- 0.7, 0.7, 1.0, 0,
- 0, 0, 0, 1);
+ const mat4 errt( 1.0, 0.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0, 0.0,
+ mLevel, mLevel, 1.0, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
// And the magic happens here...
// We construct the matrix that will perform the whole correction.
diff --git a/services/surfaceflinger/Effects/Daltonizer.h b/services/surfaceflinger/Effects/Daltonizer.h
index 2fb60e9..f5eaae7 100644
--- a/services/surfaceflinger/Effects/Daltonizer.h
+++ b/services/surfaceflinger/Effects/Daltonizer.h
@@ -21,6 +21,9 @@
namespace android {
+// Forward declare test class
+class DaltonizerTest;
+
enum class ColorBlindnessType {
None, // Disables the Daltonizer
Protanomaly, // L (red) cone deficient
@@ -37,10 +40,15 @@
public:
void setType(ColorBlindnessType type);
void setMode(ColorBlindnessMode mode);
+ // sets level for correction saturation, [0-10].
+ void setLevel(int32_t level);
// returns the color transform to apply in the shader
const mat4& operator()();
+ // For testing.
+ friend class DaltonizerTest;
+
private:
void update();
@@ -48,6 +56,8 @@
ColorBlindnessMode mMode = ColorBlindnessMode::Simulation;
bool mDirty = true;
mat4 mColorTransform;
+ // level of error spreading, [0.0-1.0].
+ float mLevel = 0.7f;
};
} /* namespace android */
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index d0e2d7a..2596a25 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -543,12 +543,14 @@
}
void SurfaceFrame::classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- Fps displayFrameRenderRate, nsecs_t& deadlineDelta) {
+ Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) {
if (mActuals.presentTime == Fence::SIGNAL_TIME_INVALID) {
// Cannot do any classification for invalid present time.
mJankType = JankType::Unknown;
mJankSeverityType = JankSeverityType::Unknown;
- deadlineDelta = -1;
+ if (outDeadlineDelta) {
+ *outDeadlineDelta = -1;
+ }
return;
}
@@ -559,7 +561,9 @@
mJankType = mPresentState != PresentState::Presented ? JankType::Dropped
: JankType::AppDeadlineMissed;
mJankSeverityType = JankSeverityType::Unknown;
- deadlineDelta = -1;
+ if (outDeadlineDelta) {
+ *outDeadlineDelta = -1;
+ }
return;
}
@@ -568,11 +572,14 @@
return;
}
- deadlineDelta = mActuals.endTime - mPredictions.endTime;
const nsecs_t presentDelta = mActuals.presentTime - mPredictions.presentTime;
const nsecs_t deltaToVsync = refreshRate.getPeriodNsecs() > 0
? std::abs(presentDelta) % refreshRate.getPeriodNsecs()
: 0;
+ const nsecs_t deadlineDelta = mActuals.endTime - mPredictions.endTime;
+ if (outDeadlineDelta) {
+ *outDeadlineDelta = deadlineDelta;
+ }
if (deadlineDelta > mJankClassificationThresholds.deadlineThreshold) {
mFrameReadyMetadata = FrameReadyMetadata::LateFinish;
@@ -671,7 +678,7 @@
mActuals.presentTime = presentTime;
nsecs_t deadlineDelta = 0;
- classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, deadlineDelta);
+ classifyJankLocked(displayFrameJankType, refreshRate, displayFrameRenderRate, &deadlineDelta);
if (mPredictionState != PredictionState::None) {
// Only update janky frames if the app used vsync predictions
@@ -681,6 +688,14 @@
}
}
+void SurfaceFrame::onCommitNotComposited(Fps refreshRate, Fps displayFrameRenderRate) {
+ std::scoped_lock lock(mMutex);
+
+ mDisplayFrameRenderRate = displayFrameRenderRate;
+ mActuals.presentTime = mPredictions.presentTime;
+ classifyJankLocked(JankType::None, refreshRate, displayFrameRenderRate, nullptr);
+}
+
void SurfaceFrame::tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
@@ -912,6 +927,15 @@
finalizeCurrentDisplayFrame();
}
+void FrameTimeline::onCommitNotComposited() {
+ ATRACE_CALL();
+ std::scoped_lock lock(mMutex);
+ mCurrentDisplayFrame->onCommitNotComposited();
+ mCurrentDisplayFrame.reset();
+ mCurrentDisplayFrame = std::make_shared<DisplayFrame>(mTimeStats, mJankClassificationThresholds,
+ &mTraceCookieCounter);
+}
+
void FrameTimeline::DisplayFrame::addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame) {
mSurfaceFrames.push_back(surfaceFrame);
}
@@ -1094,6 +1118,12 @@
}
}
+void FrameTimeline::DisplayFrame::onCommitNotComposited() {
+ for (auto& surfaceFrame : mSurfaceFrames) {
+ surfaceFrame->onCommitNotComposited(mRefreshRate, mRenderRate);
+ }
+}
+
void FrameTimeline::DisplayFrame::tracePredictions(pid_t surfaceFlingerPid,
nsecs_t monoBootOffset) const {
int64_t expectedTimelineCookie = mTraceCookieCounter.getCookieForTracing();
@@ -1142,7 +1172,7 @@
(static_cast<float>(mSurfaceFlingerPredictions.presentTime) -
kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
static_cast<float>(surfaceFrame->getPredictions().presentTime) >=
- (static_cast<float>(previousPredictionPresentTime) -
+ (static_cast<float>(previousPredictionPresentTime) +
kThresh * static_cast<float>(mRenderRate.getPeriodNsecs())) &&
// sf skipped frame is not considered if app is self janked
!surfaceFrame->isSelfJanky()) {
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index a76f7d4..94cfcb4 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -204,6 +204,8 @@
void onPresent(nsecs_t presentTime, int32_t displayFrameJankType, Fps refreshRate,
Fps displayFrameRenderRate, nsecs_t displayDeadlineDelta,
nsecs_t displayPresentDelta);
+ // Sets the frame as none janky as there was no real display frame.
+ void onCommitNotComposited(Fps refreshRate, Fps displayFrameRenderRate);
// All the timestamps are dumped relative to the baseTime
void dump(std::string& result, const std::string& indent, nsecs_t baseTime) const;
// Dumps only the layer, token, is buffer, jank metadata, prediction and present states.
@@ -235,7 +237,7 @@
void tracePredictions(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
void traceActuals(int64_t displayFrameToken, nsecs_t monoBootOffset) const;
void classifyJankLocked(int32_t displayFrameJankType, const Fps& refreshRate,
- Fps displayFrameRenderRate, nsecs_t& deadlineDelta) REQUIRES(mMutex);
+ Fps displayFrameRenderRate, nsecs_t* outDeadlineDelta) REQUIRES(mMutex);
const int64_t mToken;
const int32_t mInputEventId;
@@ -318,6 +320,10 @@
virtual void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence) = 0;
+ // Tells FrameTimeline that a frame was committed but not composited. This is used to flush
+ // all the associated surface frames.
+ virtual void onCommitNotComposited() = 0;
+
// Args:
// -jank : Dumps only the Display Frames that are either janky themselves
// or contain janky Surface Frames.
@@ -390,6 +396,8 @@
std::optional<TimelineItem> predictions, nsecs_t wakeUpTime);
// Sets the appropriate metadata and classifies the jank.
void onPresent(nsecs_t signalTime, nsecs_t previousPresentTime);
+ // Flushes all the surface frames as those were not generating any actual display frames.
+ void onCommitNotComposited();
// Adds the provided SurfaceFrame to the current display frame.
void addSurfaceFrame(std::shared_ptr<SurfaceFrame> surfaceFrame);
@@ -475,6 +483,7 @@
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate, Fps renderRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
const std::shared_ptr<FenceTime>& gpuFence = FenceTime::NO_FENCE) override;
+ void onCommitNotComposited() override;
void parseArgs(const Vector<String16>& args, std::string& result) override;
void setMaxDisplayFrames(uint32_t size) override;
float computeFps(const std::unordered_set<int32_t>& layerIds) override;
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..39a6b77 100644
--- a/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerHierarchy.cpp
@@ -52,8 +52,12 @@
mChildren = hierarchy.mChildren;
}
-void LayerHierarchy::traverse(const Visitor& visitor,
- LayerHierarchy::TraversalPath& traversalPath) const {
+void LayerHierarchy::traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& traversalPath,
+ uint32_t depth) const {
+ LLOG_ALWAYS_FATAL_WITH_TRACE_IF(depth > 50,
+ "Cycle detected in LayerHierarchy::traverse. See "
+ "traverse_stack_overflow_transactions.winscope");
+
if (mLayer) {
bool breakTraversal = !visitor(*this, traversalPath);
if (breakTraversal) {
@@ -66,7 +70,7 @@
for (auto& [child, childVariant] : mChildren) {
ScopedAddToTraversalPath addChildToTraversalPath(traversalPath, child->mLayer->id,
childVariant);
- child->traverse(visitor, traversalPath);
+ child->traverse(visitor, traversalPath, depth + 1);
}
}
@@ -153,7 +157,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;
@@ -256,27 +260,36 @@
hierarchy->mParent->updateChild(hierarchy, LayerHierarchy::Variant::Attached);
}
-void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
- if (root->mLayer) {
- attachToRelativeParent(root);
- }
- for (auto& [child, childVariant] : root->mChildren) {
- if (childVariant == LayerHierarchy::Variant::Detached ||
- childVariant == LayerHierarchy::Variant::Attached) {
- attachHierarchyToRelativeParent(child);
+std::vector<LayerHierarchy*> LayerHierarchyBuilder::getDescendants(LayerHierarchy* root) {
+ std::vector<LayerHierarchy*> hierarchies;
+ hierarchies.push_back(root);
+ std::vector<LayerHierarchy*> descendants;
+ for (size_t i = 0; i < hierarchies.size(); i++) {
+ LayerHierarchy* hierarchy = hierarchies[i];
+ if (hierarchy->mLayer) {
+ descendants.push_back(hierarchy);
}
+ for (auto& [child, childVariant] : hierarchy->mChildren) {
+ if (childVariant == LayerHierarchy::Variant::Detached ||
+ childVariant == LayerHierarchy::Variant::Attached) {
+ hierarchies.push_back(child);
+ }
+ }
+ }
+ return descendants;
+}
+
+void LayerHierarchyBuilder::attachHierarchyToRelativeParent(LayerHierarchy* root) {
+ std::vector<LayerHierarchy*> hierarchiesToAttach = getDescendants(root);
+ for (LayerHierarchy* hierarchy : hierarchiesToAttach) {
+ attachToRelativeParent(hierarchy);
}
}
void LayerHierarchyBuilder::detachHierarchyFromRelativeParent(LayerHierarchy* root) {
- if (root->mLayer) {
- detachFromRelativeParent(root);
- }
- for (auto& [child, childVariant] : root->mChildren) {
- if (childVariant == LayerHierarchy::Variant::Detached ||
- childVariant == LayerHierarchy::Variant::Attached) {
- detachHierarchyFromRelativeParent(child);
- }
+ std::vector<LayerHierarchy*> hierarchiesToDetach = getDescendants(root);
+ for (LayerHierarchy* hierarchy : hierarchiesToDetach) {
+ detachFromRelativeParent(hierarchy);
}
}
@@ -289,6 +302,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 +344,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 +354,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 +526,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 +541,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..d023f9e 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:
@@ -141,7 +147,7 @@
if (mLayer) {
root.id = mLayer->id;
}
- traverse(visitor, root);
+ traverse(visitor, root, /*depth=*/0);
}
// Traverse the hierarchy in z-order, skipping children that have relative parents.
@@ -184,7 +190,8 @@
void sortChildrenByZOrder();
void updateChild(LayerHierarchy*, LayerHierarchy::Variant);
void traverseInZOrder(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
- void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent) const;
+ void traverse(const Visitor& visitor, LayerHierarchy::TraversalPath& parent,
+ uint32_t depth = 0) const;
void dump(std::ostream& out, const std::string& prefix, LayerHierarchy::Variant variant,
bool isLastChild, bool includeMirroredHierarchy) const;
@@ -211,6 +218,7 @@
void detachFromParent(LayerHierarchy*);
void attachToRelativeParent(LayerHierarchy*);
void detachFromRelativeParent(LayerHierarchy*);
+ std::vector<LayerHierarchy*> getDescendants(LayerHierarchy*);
void attachHierarchyToRelativeParent(LayerHierarchy*);
void detachHierarchyFromRelativeParent(LayerHierarchy*);
void init(const std::vector<std::unique_ptr<RequestedLayerState>>&);
diff --git a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
index e864ff6..f1091a6 100644
--- a/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerLifecycleManager.cpp
@@ -73,8 +73,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);
@@ -151,6 +153,10 @@
if (swapErase(linkedLayer->mirrorIds, layer.id)) {
linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
}
+ if (linkedLayer->layerIdToMirror == layer.id) {
+ linkedLayer->layerIdToMirror = UNASSIGNED_LAYER_ID;
+ linkedLayer->changes |= RequestedLayerState::Changes::Mirror;
+ }
if (linkedLayer->touchCropId == layer.id) {
linkedLayer->touchCropId = UNASSIGNED_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..398e64a 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h
@@ -80,8 +80,11 @@
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::TrustedOverlay trustedOverlay;
gui::GameMode gameMode;
scheduler::LayerInfo::FrameRate frameRate;
scheduler::LayerInfo::FrameRate inheritedFrameRate;
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index 0966fe0..ca53a0d 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -22,6 +22,7 @@
#include <numeric>
#include <optional>
+#include <common/FlagManager.h>
#include <ftl/small_map.h>
#include <gui/TraceUtils.h>
#include <ui/DisplayMap.h>
@@ -255,6 +256,9 @@
}
void updateVisibility(LayerSnapshot& snapshot, bool visible) {
+ if (snapshot.isVisible != visible) {
+ snapshot.changes |= RequestedLayerState::Changes::Visibility;
+ }
snapshot.isVisible = visible;
// TODO(b/238781169) we are ignoring this compat for now, since we will have
@@ -358,10 +362,11 @@
snapshot.relativeLayerMetadata.mMap.clear();
snapshot.inputInfo.touchOcclusionMode = gui::TouchOcclusionMode::BLOCK_UNTRUSTED;
snapshot.dropInputMode = gui::DropInputMode::NONE;
- snapshot.isTrustedOverlay = false;
+ snapshot.trustedOverlay = gui::TrustedOverlay::UNSET;
snapshot.gameMode = gui::GameMode::Unsupported;
snapshot.frameRate = {};
snapshot.fixedTransformHint = ui::Transform::ROT_INVALID;
+ snapshot.ignoreLocalTransform = false;
return snapshot;
}
@@ -575,9 +580,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);
@@ -732,7 +739,19 @@
}
if (forceUpdate || snapshot.clientChanges & layer_state_t::eTrustedOverlayChanged) {
- snapshot.isTrustedOverlay = parentSnapshot.isTrustedOverlay || requested.isTrustedOverlay;
+ switch (requested.trustedOverlay) {
+ case gui::TrustedOverlay::UNSET:
+ snapshot.trustedOverlay = parentSnapshot.trustedOverlay;
+ break;
+ case gui::TrustedOverlay::DISABLED:
+ snapshot.trustedOverlay = FlagManager::getInstance().override_trusted_overlay()
+ ? requested.trustedOverlay
+ : parentSnapshot.trustedOverlay;
+ break;
+ case gui::TrustedOverlay::ENABLED:
+ snapshot.trustedOverlay = requested.trustedOverlay;
+ break;
+ }
}
if (snapshot.isHiddenByPolicyFromParent &&
@@ -1028,6 +1047,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 +1061,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 +1078,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 +1095,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 +1112,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()) {
@@ -1101,8 +1128,8 @@
// 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;
+ if (snapshot.trustedOverlay == gui::TrustedOverlay::ENABLED) {
+ snapshot.inputInfo.inputConfig |= InputConfig::TRUSTED_OVERLAY;
}
snapshot.inputInfo.contentSize = snapshot.croppedBufferSize.getSize();
@@ -1110,10 +1137,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 cb0e2a1..3e8d740 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.cpp
@@ -118,7 +118,7 @@
shadowRadius = 0.f;
fixedTransformHint = ui::Transform::ROT_INVALID;
destinationFrame.makeInvalid();
- isTrustedOverlay = false;
+ trustedOverlay = gui::TrustedOverlay::UNSET;
dropInputMode = gui::DropInputMode::NONE;
dimmingEnabled = true;
defaultFrameRateCompatibility = static_cast<int8_t>(scheduler::FrameRateCompatibility::Default);
@@ -163,7 +163,9 @@
LLOGV(layerId, "requested=%" PRIu64 "flags=%" PRIu64, clientState.what, clientChanges);
if (clientState.what & layer_state_t::eFlagsChanged) {
- if ((oldFlags ^ flags) & (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque)) {
+ if ((oldFlags ^ flags) &
+ (layer_state_t::eLayerHidden | layer_state_t::eLayerOpaque |
+ layer_state_t::eLayerSecure)) {
changes |= RequestedLayerState::Changes::Visibility |
RequestedLayerState::Changes::VisibleRegion;
}
@@ -583,23 +585,24 @@
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 |
+ const uint64_t deniedFlags = layer_state_t::eProducerDisconnect | layer_state_t::eLayerChanged |
+ layer_state_t::eRelativeLayerChanged | layer_state_t::eTransparentRegionChanged |
layer_state_t::eBlurRegionsChanged | layer_state_t::eLayerStackChanged |
- layer_state_t::eAutoRefreshChanged | layer_state_t::eReparent;
+ layer_state_t::eReparent |
+ (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
+ ? 0
+ : (layer_state_t::eAutoRefreshChanged | layer_state_t::eFlagsChanged));
if (s.what & deniedFlags) {
ATRACE_FORMAT_INSTANT("%s: false [has denied flags 0x%" PRIx64 "]", __func__,
s.what & deniedFlags);
return false;
}
- bool changedFlags = diff(s);
- static constexpr auto deniedChanges = layer_state_t::ePositionChanged |
- layer_state_t::eAlphaChanged | layer_state_t::eColorTransformChanged |
- layer_state_t::eBackgroundColorChanged | layer_state_t::eMatrixChanged |
- layer_state_t::eCornerRadiusChanged | layer_state_t::eBackgroundBlurRadiusChanged |
- layer_state_t::eBufferTransformChanged |
+ const uint64_t changedFlags = diff(s);
+ const uint64_t deniedChanges = layer_state_t::ePositionChanged | layer_state_t::eAlphaChanged |
+ layer_state_t::eColorTransformChanged | layer_state_t::eBackgroundColorChanged |
+ layer_state_t::eMatrixChanged | layer_state_t::eCornerRadiusChanged |
+ layer_state_t::eBackgroundBlurRadiusChanged | layer_state_t::eBufferTransformChanged |
layer_state_t::eTransformToDisplayInverseChanged | layer_state_t::eCropChanged |
layer_state_t::eDataspaceChanged | layer_state_t::eHdrMetadataChanged |
layer_state_t::eSidebandStreamChanged | layer_state_t::eColorSpaceAgnosticChanged |
@@ -607,10 +610,13 @@
layer_state_t::eTrustedOverlayChanged | layer_state_t::eStretchChanged |
layer_state_t::eBufferCropChanged | layer_state_t::eDestinationFrameChanged |
layer_state_t::eDimmingEnabledChanged | layer_state_t::eExtendedRangeBrightnessChanged |
- layer_state_t::eDesiredHdrHeadroomChanged;
+ layer_state_t::eDesiredHdrHeadroomChanged |
+ (FlagManager::getInstance().latch_unsignaled_with_auto_refresh_changed()
+ ? layer_state_t::eFlagsChanged
+ : 0);
if (changedFlags & deniedChanges) {
ATRACE_FORMAT_INSTANT("%s: false [has denied changes flags 0x%" PRIx64 "]", __func__,
- s.what & deniedChanges);
+ changedFlags & deniedChanges);
return false;
}
diff --git a/services/surfaceflinger/FrontEnd/RequestedLayerState.h b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
index 09f33de..48b9640 100644
--- a/services/surfaceflinger/FrontEnd/RequestedLayerState.h
+++ b/services/surfaceflinger/FrontEnd/RequestedLayerState.h
@@ -26,6 +26,7 @@
#include "TransactionState.h"
namespace android::surfaceflinger::frontend {
+using namespace ftl::flag_operators;
// Stores client requested states for a layer.
// This struct does not store any other states or states pertaining to
@@ -58,6 +59,13 @@
GameMode = 1u << 19,
BufferUsageFlags = 1u << 20,
};
+
+ static constexpr ftl::Flags<Changes> kMustComposite = Changes::Created | Changes::Destroyed |
+ Changes::Hierarchy | Changes::Geometry | Changes::Content | Changes::Input |
+ Changes::Z | Changes::Mirror | Changes::Parent | Changes::RelativeParent |
+ Changes::Metadata | Changes::Visibility | Changes::VisibleRegion | Changes::Buffer |
+ Changes::SidebandStream | Changes::Animation | Changes::BufferSize | Changes::GameMode |
+ Changes::BufferUsageFlags;
static Rect reduce(const Rect& win, const Region& exclude);
RequestedLayerState(const LayerCreationArgs&);
void merge(const ResolvedComposerState&);
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 9c8887d..c39b757 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -15,6 +15,7 @@
*/
// TODO(b/129481165): remove the #pragma below and fix conversion issues
+
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
@@ -23,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>
@@ -39,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>
@@ -73,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"
@@ -90,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;
@@ -150,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;
@@ -1235,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.
@@ -1572,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;
@@ -1588,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");
@@ -2539,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 =
@@ -2547,7 +2477,7 @@
WindowInfo info = mDrawingState.inputInfo;
info.id = sequence;
- info.displayId = getLayerStack().id;
+ info.displayId = toLogicalDisplayId(getLayerStack());
fillInputFrameInfo(info, displayTransform);
@@ -2688,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;
@@ -2800,7 +2717,7 @@
}
sp<Layer> clonedChild = clonedLayersMap[child];
if (clonedChild == nullptr) {
- clonedChild = child->createClone(mirrorRoot->getSequence());
+ clonedChild = child->createClone();
clonedLayersMap[child] = clonedChild;
}
addChildToDrawing(clonedChild);
@@ -2912,9 +2829,7 @@
currentMaxAcquiredBufferCount);
}
-void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
- ui::LayerStack layerStack,
- std::function<FenceResult(FenceResult)>&& continuation) {
+sp<CallbackHandle> Layer::findCallbackHandle() {
// If we are displayed on multiple displays in a single composition cycle then we would
// need to do careful tracking to enable the use of the mLastClientCompositionFence.
// For example we can only use it if all the displays are client comp, and we need
@@ -2949,6 +2864,40 @@
break;
}
}
+ return ch;
+}
+
+void Layer::prepareReleaseCallbacks(ftl::Future<FenceResult> futureFenceResult,
+ ui::LayerStack layerStack) {
+ sp<CallbackHandle> ch = findCallbackHandle();
+
+ if (ch != nullptr) {
+ ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
+ 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.
+ // 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) {
+ mPreviouslyPresentedLayerStacks.push_back(layerStack);
+ }
+
+ if (mDrawingState.frameNumber > 0) {
+ mDrawingState.previousFrameNumber = mDrawingState.frameNumber;
+ }
+}
+
+void Layer::onLayerDisplayed(ftl::SharedFuture<FenceResult> futureFenceResult,
+ ui::LayerStack layerStack,
+ std::function<FenceResult(FenceResult)>&& continuation) {
+ sp<CallbackHandle> ch = findCallbackHandle();
if (!FlagManager::getInstance().screenshot_fence_preservation() && continuation) {
futureFenceResult = ftl::Future(futureFenceResult).then(std::move(continuation)).share();
@@ -2956,32 +2905,32 @@
if (ch != nullptr) {
ch->previousReleaseCallbackId = mPreviousReleaseCallbackId;
- ch->previousReleaseFences.emplace_back(std::move(futureFenceResult));
+ ch->previousSharedReleaseFences.emplace_back(std::move(futureFenceResult));
ch->name = mName;
} else if (FlagManager::getInstance().screenshot_fence_preservation()) {
// 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::move(continuation));
+ mPreviousReleaseFenceAndContinuations.emplace_back(std::move(futureFenceResult),
+ std::move(continuation));
std::vector<FenceAndContinuation> 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 (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
- auto result = futureAndContinution.future.wait_for(0s);
+ for (const auto& futureAndContinuation : mPreviousReleaseFenceAndContinuations) {
+ auto result = futureAndContinuation.future.wait_for(0s);
if (result != std::future_status::ready) {
- mergedFences.emplace_back(futureAndContinution);
+ mergedFences.emplace_back(futureAndContinuation);
continue;
}
- mergeFence(getDebugName(), futureAndContinution.chain().get().value_or(Fence::NO_FENCE),
- prevFence);
+ mergeFence(getDebugName(),
+ futureAndContinuation.chain().get().value_or(Fence::NO_FENCE), prevFence);
}
if (prevFence != nullptr) {
mergedFences.emplace_back(ftl::yield(FenceResult(std::move(prevFence))).share());
}
- mAdditionalPreviousReleaseFences.swap(mergedFences);
+ mPreviousReleaseFenceAndContinuations.swap(mergedFences);
}
if (mBufferInfo.mBuffer) {
@@ -3014,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);
@@ -3208,8 +3151,7 @@
bool Layer::setBuffer(std::shared_ptr<renderengine::ExternalTexture>& buffer,
const BufferData& bufferData, nsecs_t postTime, nsecs_t desiredPresentTime,
- bool isAutoTimestamp, std::optional<nsecs_t> dequeueTime,
- const FrameTimelineInfo& info) {
+ bool isAutoTimestamp, const FrameTimelineInfo& info) {
ATRACE_FORMAT("setBuffer %s - hasBuffer=%s", getDebugName(), (buffer ? "true" : "false"));
const bool frameNumberChanged =
@@ -3281,16 +3223,13 @@
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) {
+ if (bufferData.dequeueTime > 0) {
const uint64_t bufferId = mDrawingState.buffer->getId();
mFlinger->mFrameTracer->traceNewLayer(layerId, getName().c_str());
- mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, *dequeueTime,
+ mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber,
+ bufferData.dequeueTime,
FrameTracer::FrameEvent::DEQUEUE);
mFlinger->mFrameTracer->traceTimestamp(layerId, bufferId, frameNumber, postTime,
FrameTracer::FrameEvent::QUEUE);
@@ -3483,16 +3422,23 @@
handle->acquireTimeOrFence = mCallbackHandleAcquireTimeOrFence;
handle->frameNumber = mDrawingState.frameNumber;
handle->previousFrameNumber = mDrawingState.previousFrameNumber;
- if (FlagManager::getInstance().screenshot_fence_preservation() &&
+ 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 (const auto& futureAndContinution : mAdditionalPreviousReleaseFences) {
- handle->previousReleaseFences.emplace_back(futureAndContinution.chain());
+ for (auto& [_, future] : mAdditionalPreviousReleaseFences) {
+ handle->previousReleaseFences.emplace_back(std::move(future));
}
mAdditionalPreviousReleaseFences.clear();
+ } else if (FlagManager::getInstance().screenshot_fence_preservation() &&
+ mPreviousReleaseBufferEndpoint == handle->listener) {
+ // Add fences from previous screenshots now so that they can be dispatched to the
+ // client.
+ for (const auto& futureAndContinution : mPreviousReleaseFenceAndContinuations) {
+ handle->previousSharedReleaseFences.emplace_back(futureAndContinution.chain());
+ }
+ mPreviousReleaseFenceAndContinuations.clear();
}
-
// Store so latched time and release fence can be set
mDrawingState.callbackHandles.push_back(handle);
@@ -3739,11 +3685,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;
}
@@ -3825,8 +3770,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__,
@@ -3957,7 +3904,7 @@
}
if (s.what & layer_state_t::eTrustedOverlayChanged) {
- if (mDrawingState.isTrustedOverlay != s.isTrustedOverlay) {
+ if (mDrawingState.isTrustedOverlay != (s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
ATRACE_FORMAT_INSTANT("%s: false [eTrustedOverlayChanged changed]", __func__);
return false;
}
@@ -4037,7 +3984,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;
}
@@ -4049,7 +3996,7 @@
return layerFE;
}
}
- auto layerFE = mFlinger->getFactory().createLayerFE(mName);
+ auto layerFE = mFlinger->getFactory().createLayerFE(mName, this);
mLayerFEs.emplace_back(path, layerFE);
return layerFE;
}
@@ -4357,7 +4304,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 0ceecec..b9fcd5c 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
@@ -313,7 +312,7 @@
bool setBuffer(std::shared_ptr<renderengine::ExternalTexture>& /* buffer */,
const BufferData& /* bufferData */, nsecs_t /* postTime */,
nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/,
- std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/);
+ const FrameTimelineInfo& /*info*/);
void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/, bool /*isAutoTimestamp*/);
bool setDataspace(ui::Dataspace /*dataspace*/);
bool setExtendedRangeBrightness(float currentBufferRatio, float desiredRatio);
@@ -559,6 +558,14 @@
void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack layerStack,
std::function<FenceResult(FenceResult)>&& continuation = nullptr);
+ // Tracks mLastClientCompositionFence and gets the callback handle for this layer.
+ sp<CallbackHandle> findCallbackHandle();
+
+ // Adds the future release fence to a list of fences that are used to release the
+ // last presented buffer. Also keeps track of the layerstack in a list of previous
+ // layerstacks that have been presented.
+ void prepareReleaseCallbacks(ftl::Future<FenceResult>, ui::LayerStack layerStack);
+
void setWasClientComposed(const sp<Fence>& fence) {
mLastClientCompositionFence = fence;
mClearClientCompositionFenceOnLayerDisplayed = false;
@@ -691,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;
@@ -874,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 */);
@@ -934,6 +934,7 @@
// the release fences from the correct displays when we release the last buffer
// from the layer.
std::vector<ui::LayerStack> mPreviouslyPresentedLayerStacks;
+
struct FenceAndContinuation {
ftl::SharedFuture<FenceResult> future;
std::function<FenceResult(FenceResult)> continuation;
@@ -946,7 +947,19 @@
}
}
};
- std::vector<FenceAndContinuation> mAdditionalPreviousReleaseFences;
+ std::vector<FenceAndContinuation> mPreviousReleaseFenceAndContinuations;
+
+ // Release fences for buffers that have not yet received a release
+ // callback. A release callback may not be given when capturing
+ // 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. 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;
@@ -963,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();
@@ -1238,10 +1250,6 @@
bool findInHierarchy(const sp<Layer>&);
- bool mBorderEnabled = false;
- float mBorderWidth;
- half4 mBorderColor;
-
void setTransformHintLegacy(ui::Transform::RotationFlags);
void releasePreviousBuffer();
void resetDrawingStateBufferInfo();
@@ -1249,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 2dbcb84..c2251a8 100644
--- a/services/surfaceflinger/LayerFE.cpp
+++ b/services/surfaceflinger/LayerFE.cpp
@@ -27,6 +27,9 @@
#include "LayerFE.h"
#include "SurfaceFlinger.h"
+#include "common/FlagManager.h"
+#include "ui/FenceResult.h"
+#include "ui/LayerStack.h"
namespace android {
@@ -78,12 +81,21 @@
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();
}
-bool LayerFE::onPreComposition(nsecs_t refreshStartTime, bool) {
- mCompositionResult.refreshStartTime = refreshStartTime;
+bool LayerFE::onPreComposition(bool) {
return mSnapshot->hasReadyFrame;
}
@@ -388,4 +400,29 @@
return mSnapshot->externalTexture ? mSnapshot->externalTexture->getBuffer() : nullptr;
}
+void LayerFE::setReleaseFence(const FenceResult& releaseFence) {
+ // Promises should not be fulfilled more than once. This case can occur if virtual
+ // displays with the same layerstack ID are being created and destroyed in quick
+ // succession, such as in tests. This would result in a race condition in which
+ // multiple displays have the same layerstack ID within the same vsync interval.
+ if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::FULFILLED) {
+ return;
+ }
+ mReleaseFence.set_value(releaseFence);
+ mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::FULFILLED;
+}
+
+// LayerFEs are reused and a new fence needs to be created whevever a buffer is latched.
+ftl::Future<FenceResult> LayerFE::createReleaseFenceFuture() {
+ if (mReleaseFencePromiseStatus == ReleaseFencePromiseStatus::INITIALIZED) {
+ LOG_ALWAYS_FATAL("Attempting to create a new promise while one is still unfulfilled.");
+ }
+ mReleaseFence = std::promise<FenceResult>();
+ mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::INITIALIZED;
+ return mReleaseFence.get_future();
+}
+
+LayerFE::ReleaseFencePromiseStatus LayerFE::getReleaseFencePromiseStatus() {
+ return mReleaseFencePromiseStatus;
+}
} // namespace android
diff --git a/services/surfaceflinger/LayerFE.h b/services/surfaceflinger/LayerFE.h
index d584fb7..658f949 100644
--- a/services/surfaceflinger/LayerFE.h
+++ b/services/surfaceflinger/LayerFE.h
@@ -22,13 +22,13 @@
#include "compositionengine/LayerFE.h"
#include "compositionengine/LayerFECompositionState.h"
#include "renderengine/LayerSettings.h"
+#include "ui/LayerStack.h"
+
+#include <ftl/future.h>
namespace android {
struct CompositionResult {
- // TODO(b/238781169) update CE to no longer pass refreshStartTime to LayerFE::onPreComposition
- // and remove this field.
- nsecs_t refreshStartTime = 0;
std::vector<std::pair<ftl::SharedFuture<FenceResult>, ui::LayerStack>> releaseFences;
sp<Fence> lastClientCompositionFence = nullptr;
};
@@ -36,10 +36,11 @@
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;
- bool onPreComposition(nsecs_t refreshStartTime, bool updatingOutputGeometryThisFrame) override;
+ bool onPreComposition(bool updatingOutputGeometryThisFrame) override;
void onLayerDisplayed(ftl::SharedFuture<FenceResult>, ui::LayerStack) override;
const char* getDebugName() const override;
int32_t getSequence() const override;
@@ -50,6 +51,9 @@
std::optional<compositionengine::LayerFE::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) const;
CompositionResult&& stealCompositionResult();
+ ftl::Future<FenceResult> createReleaseFenceFuture() override;
+ void setReleaseFence(const FenceResult& releaseFence) override;
+ LayerFE::ReleaseFencePromiseStatus getReleaseFencePromiseStatus() override;
std::unique_ptr<surfaceflinger::frontend::LayerSnapshot> mSnapshot;
@@ -79,6 +83,8 @@
CompositionResult mCompositionResult;
std::string mName;
+ std::promise<FenceResult> mReleaseFence;
+ ReleaseFencePromiseStatus mReleaseFencePromiseStatus = ReleaseFencePromiseStatus::UNINITIALIZED;
};
} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
index aa6026e..496033b 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) {
@@ -382,7 +382,8 @@
layerInfo->set_corner_radius(
(snapshot.roundedCorner.radius.x + snapshot.roundedCorner.radius.y) / 2.0);
layerInfo->set_background_blur_radius(snapshot.backgroundBlurRadius);
- layerInfo->set_is_trusted_overlay(snapshot.isTrustedOverlay);
+ layerInfo->set_is_trusted_overlay(snapshot.trustedOverlay == gui::TrustedOverlay::ENABLED);
+ // TODO(b/339701674) update protos
LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
[&]() { return layerInfo->mutable_position(); });
@@ -465,7 +466,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..bfe6d2a 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -24,38 +24,24 @@
#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)
- : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, hintForSeamlessTransition,
- allowSecureLayers),
+LayerRenderArea::LayerRenderArea(sp<Layer> layer, frontend::LayerSnapshot layerSnapshot,
+ const Rect& crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ const ui::Transform& layerTransform, const Rect& layerBufferSize,
+ ftl::Flags<RenderArea::Options> options)
+ : RenderArea(reqSize, CaptureFill::CLEAR, reqDataSpace, options),
mLayer(std::move(layer)),
- mLayerTransform(layerTransform),
+ mLayerSnapshot(std::move(layerSnapshot)),
mLayerBufferSize(layerBufferSize),
mCrop(crop),
- mFlinger(flinger),
- mChildrenOnly(childrenOnly) {}
+ mTransform(layerTransform) {}
const ui::Transform& LayerRenderArea::getTransform() const {
return mTransform;
}
bool LayerRenderArea::isSecure() const {
- return mAllowSecureLayers;
+ return mOptions.test(Options::CAPTURE_SECURE_LAYERS);
}
sp<const DisplayDevice> LayerRenderArea::getDisplayDevice() const {
@@ -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..f72c7c7 100644
--- a/services/surfaceflinger/LayerRenderArea.h
+++ b/services/surfaceflinger/LayerRenderArea.h
@@ -32,29 +32,26 @@
class LayerRenderArea : public RenderArea {
public:
- LayerRenderArea(SurfaceFlinger& flinger, sp<Layer> layer, const Rect& crop, ui::Size reqSize,
- ui::Dataspace reqDataSpace, bool childrenOnly, bool allowSecureLayers,
+ LayerRenderArea(sp<Layer> layer, frontend::LayerSnapshot layerSnapshot, const Rect& crop,
+ ui::Size reqSize, ui::Dataspace reqDataSpace,
const ui::Transform& layerTransform, const Rect& layerBufferSize,
- bool hintForSeamlessTransition);
+ ftl::Flags<RenderArea::Options> options);
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; }
+ sp<Layer> getParentLayer() const override { return mLayer; }
+ const frontend::LayerSnapshot* getLayerSnapshot() const override { return &mLayerSnapshot; }
private:
const sp<Layer> mLayer;
- const ui::Transform mLayerTransform;
+ const frontend::LayerSnapshot mLayerSnapshot;
const Rect mLayerBufferSize;
const Rect mCrop;
ui::Transform mTransform;
-
- SurfaceFlinger& mFlinger;
- const bool mChildrenOnly;
};
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index b960e33..35f12a0 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -28,10 +28,11 @@
namespace android {
-auto RefreshRateOverlay::draw(int vsyncRate, int renderFps, SkColor color,
+auto RefreshRateOverlay::draw(int refreshRate, int renderFps, bool idle, SkColor color,
ui::Transform::RotationFlags rotation, ftl::Flags<Features> features)
-> Buffers {
const size_t loopCount = features.test(Features::Spinner) ? 6 : 1;
+ const bool isSetByHwc = features.test(Features::SetByHwc);
Buffers buffers;
buffers.reserve(loopCount);
@@ -71,7 +72,11 @@
canvas->setMatrix(canvasTransform);
int left = 0;
- drawNumber(vsyncRate, left, color, *canvas);
+ if (idle && !isSetByHwc) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(refreshRate, left, color, *canvas);
+ }
left += 3 * (kDigitWidth + kDigitSpace);
if (features.test(Features::Spinner)) {
switch (i) {
@@ -104,7 +109,11 @@
left += kDigitWidth + kDigitSpace;
if (features.test(Features::RenderRate)) {
- drawNumber(renderFps, left, color, *canvas);
+ if (idle) {
+ drawDash(left, *canvas);
+ } else {
+ drawNumber(renderFps, left, color, *canvas);
+ }
}
left += 3 * (kDigitWidth + kDigitSpace);
@@ -138,6 +147,14 @@
SegmentDrawer::drawDigit(number % 10, left, color, canvas);
}
+void RefreshRateOverlay::drawDash(int left, SkCanvas& canvas) {
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+
+ left += kDigitWidth + kDigitSpace;
+ SegmentDrawer::drawSegment(SegmentDrawer::Segment::Middle, left, SK_ColorRED, canvas);
+}
+
std::unique_ptr<RefreshRateOverlay> RefreshRateOverlay::create(FpsRange range,
ftl::Flags<Features> features) {
std::unique_ptr<RefreshRateOverlay> overlay =
@@ -171,7 +188,8 @@
return mSurfaceControl != nullptr;
}
-auto RefreshRateOverlay::getOrCreateBuffers(Fps vsyncRate, Fps renderFps) -> const Buffers& {
+auto RefreshRateOverlay::getOrCreateBuffers(Fps refreshRate, Fps renderFps, bool idle)
+ -> const Buffers& {
static const Buffers kNoBuffers;
if (!mSurfaceControl) return kNoBuffers;
@@ -197,22 +215,16 @@
createTransaction().setTransform(mSurfaceControl->get(), transform).apply();
- BufferCache::const_iterator it =
- mBufferCache.find({vsyncRate.getIntValue(), renderFps.getIntValue(), transformHint});
+ BufferCache::const_iterator it = mBufferCache.find(
+ {refreshRate.getIntValue(), renderFps.getIntValue(), transformHint, idle});
if (it == mBufferCache.end()) {
- // HWC minFps is not known by the framework in order
- // to consider lower rates we set minFps to 0.
- const int minFps = isSetByHwc() ? 0 : mFpsRange.min.getIntValue();
const int maxFps = mFpsRange.max.getIntValue();
- // Clamp to the range. The current vsyncRate may be outside of this range if the display
- // has changed its set of supported refresh rates.
- const int displayIntFps = std::clamp(vsyncRate.getIntValue(), minFps, maxFps);
+ // Clamp to supported refresh rate range: the current refresh rate may be outside of this
+ // range if the display has changed its set of supported refresh rates.
+ const int refreshIntFps = std::clamp(refreshRate.getIntValue(), 0, maxFps);
const int renderIntFps = renderFps.getIntValue();
-
- // Ensure non-zero range to avoid division by zero.
- const float fpsScale =
- static_cast<float>(displayIntFps - minFps) / std::max(1, maxFps - minFps);
+ const float fpsScale = static_cast<float>(refreshIntFps) / maxFps;
constexpr SkColor kMinFpsColor = SK_ColorRED;
constexpr SkColor kMaxFpsColor = SK_ColorGREEN;
@@ -228,10 +240,10 @@
const SkColor color = colorBase.toSkColor();
- auto buffers = draw(displayIntFps, renderIntFps, color, transformHint, mFeatures);
+ auto buffers = draw(refreshIntFps, renderIntFps, idle, color, transformHint, mFeatures);
it = mBufferCache
- .try_emplace({displayIntFps, renderIntFps, transformHint}, std::move(buffers))
- .first;
+ .try_emplace({refreshIntFps, renderIntFps, transformHint, idle},
+ std::move(buffers)).first;
}
return it->second;
@@ -260,25 +272,34 @@
createTransaction().setLayerStack(mSurfaceControl->get(), stack).apply();
}
-void RefreshRateOverlay::changeRefreshRate(Fps vsyncRate, Fps renderFps) {
- mVsyncRate = vsyncRate;
+void RefreshRateOverlay::changeRefreshRate(Fps refreshRate, Fps renderFps) {
+ mRefreshRate = refreshRate;
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(vsyncRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(refreshRate, renderFps, mIsVrrIdle)[mFrame];
+ createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
+}
+
+void RefreshRateOverlay::onVrrIdle(bool idle) {
+ mIsVrrIdle = idle;
+ if (!mRefreshRate || !mRenderFps) return;
+
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
void RefreshRateOverlay::changeRenderRate(Fps renderFps) {
- if (mFeatures.test(Features::RenderRate) && mVsyncRate && FlagManager::getInstance().misc1()) {
+ if (mFeatures.test(Features::RenderRate) && mRefreshRate &&
+ FlagManager::getInstance().misc1()) {
mRenderFps = renderFps;
- const auto buffer = getOrCreateBuffers(*mVsyncRate, renderFps)[mFrame];
+ const auto buffer = getOrCreateBuffers(*mRefreshRate, renderFps, mIsVrrIdle)[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
}
}
void RefreshRateOverlay::animate() {
- if (!mFeatures.test(Features::Spinner) || !mVsyncRate) return;
+ if (!mFeatures.test(Features::Spinner) || !mRefreshRate) return;
- const auto& buffers = getOrCreateBuffers(*mVsyncRate, *mRenderFps);
+ const auto& buffers = getOrCreateBuffers(*mRefreshRate, *mRenderFps, mIsVrrIdle);
mFrame = (mFrame + 1) % buffers.size();
const auto buffer = buffers[mFrame];
createTransaction().setBuffer(mSurfaceControl->get(), buffer).apply();
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 0fec470..d8aa048 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -57,6 +57,7 @@
void changeRenderRate(Fps);
void animate();
bool isSetByHwc() const { return mFeatures.test(RefreshRateOverlay::Features::SetByHwc); }
+ void onVrrIdle(bool idle);
RefreshRateOverlay(ConstructorTag, FpsRange, ftl::Flags<Features>);
@@ -65,30 +66,33 @@
using Buffers = std::vector<sp<GraphicBuffer>>;
- static Buffers draw(int vsyncRate, int renderFps, SkColor, ui::Transform::RotationFlags,
- ftl::Flags<Features>);
+ static Buffers draw(int refreshRate, int renderFps, bool idle, SkColor,
+ ui::Transform::RotationFlags, ftl::Flags<Features>);
static void drawNumber(int number, int left, SkColor, SkCanvas&);
+ static void drawDash(int left, SkCanvas&);
- const Buffers& getOrCreateBuffers(Fps, Fps);
+ const Buffers& getOrCreateBuffers(Fps, Fps, bool);
SurfaceComposerClient::Transaction createTransaction() const;
struct Key {
- int vsyncRate;
+ int refreshRate;
int renderFps;
ui::Transform::RotationFlags flags;
+ bool idle;
bool operator==(Key other) const {
- return vsyncRate == other.vsyncRate && renderFps == other.renderFps &&
- flags == other.flags;
+ return refreshRate == other.refreshRate && renderFps == other.renderFps &&
+ flags == other.flags && idle == other.idle;
}
};
using BufferCache = ftl::SmallMap<Key, Buffers, 9>;
BufferCache mBufferCache;
- std::optional<Fps> mVsyncRate;
+ std::optional<Fps> mRefreshRate;
std::optional<Fps> mRenderFps;
+ bool mIsVrrIdle = false;
size_t mFrame = 0;
const FpsRange mFpsRange; // For color interpolation.
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index c888ccc..c77bcfa 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"
@@ -276,12 +277,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;
@@ -319,39 +314,15 @@
return true;
};
- std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> getLayerSnapshots;
- if (mFlinger.mLayerLifecycleManagerEnabled) {
- auto filterFn = [&](const frontend::LayerSnapshot& snapshot,
- bool& outStopTraversal) -> bool {
- const Rect bounds =
- frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
- snapshot.transparentRegionHint);
- const ui::Transform transform = snapshot.geomLayerTransform;
- return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
- outStopTraversal);
- };
- getLayerSnapshots =
- mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
- filterFn);
- } else {
- auto traverseLayers = [&](const LayerVector::Visitor& visitor) {
- bool stopLayerFound = false;
- auto filterVisitor = [&](Layer* layer) {
- // We don't want to capture any layers beyond the stop layer
- if (stopLayerFound) return;
-
- if (!layerFilterFn(layer->getDebugName(), layer->getSequence(),
- Rect(layer->getBounds()), layer->getTransform(),
- stopLayerFound)) {
- return;
- }
- visitor(layer);
- };
- mFlinger.traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {},
- filterVisitor);
- };
- getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
- }
+ auto filterFn = [&](const frontend::LayerSnapshot& snapshot, bool& outStopTraversal) -> bool {
+ const Rect bounds = frontend::RequestedLayerState::reduce(Rect(snapshot.geomLayerBounds),
+ snapshot.transparentRegionHint);
+ const ui::Transform transform = snapshot.geomLayerTransform;
+ return layerFilterFn(snapshot.name.c_str(), snapshot.path.id, bounds, transform,
+ outStopTraversal);
+ };
+ auto getLayerSnapshotsFn =
+ mFlinger.getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID, filterFn);
std::shared_ptr<renderengine::ExternalTexture> buffer = nullptr;
if (mCachedBuffer && mCachedBuffer->getBuffer()->getWidth() == sampledBounds.getWidth() &&
@@ -376,11 +347,29 @@
constexpr bool kGrayscale = false;
constexpr bool kIsProtected = false;
- if (const auto fenceResult =
- mFlinger.captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, buffer,
- kRegionSampling, kGrayscale, kIsProtected, nullptr)
+ SurfaceFlinger::RenderAreaBuilderVariant
+ renderAreaBuilder(std::in_place_type<DisplayRenderAreaBuilder>, sampledBounds,
+ sampledBounds.getSize(), ui::Dataspace::V0_SRGB, displayWeak,
+ RenderArea::Options::CAPTURE_SECURE_LAYERS);
+
+ FenceResult fenceResult;
+ if (FlagManager::getInstance().single_hop_screenshot() &&
+ FlagManager::getInstance().ce_fence_promise() && mFlinger.mRenderEngine->isThreaded()) {
+ std::vector<sp<LayerFE>> layerFEs;
+ auto displayState =
+ mFlinger.getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder,
+ getLayerSnapshotsFn, layerFEs);
+ fenceResult =
+ mFlinger.captureScreenshot(renderAreaBuilder, buffer, kRegionSampling, kGrayscale,
+ kIsProtected, nullptr, displayState, layerFEs)
.get();
- fenceResult.ok()) {
+ } else {
+ fenceResult =
+ mFlinger.captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, buffer,
+ kRegionSampling, kGrayscale, kIsProtected, nullptr)
+ .get();
+ }
+ if (fenceResult.ok()) {
fenceResult.value()->waitForever(LOG_TAG);
}
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 5de148e..034e467 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -4,6 +4,8 @@
#include <ui/Transform.h>
#include <functional>
+
+#include "FrontEnd/LayerSnapshot.h"
#include "Layer.h"
namespace android {
@@ -19,16 +21,23 @@
class RenderArea {
public:
enum class CaptureFill {CLEAR, OPAQUE};
+ enum class Options {
+ // If not set, the secure layer would be blacked out or skipped
+ // when rendered to an insecure render area
+ CAPTURE_SECURE_LAYERS = 1 << 0,
+ // If set, the render result may be used for system animations
+ // that must preserve the exact colors of the display
+ HINT_FOR_SEAMLESS_TRANSITION = 1 << 1,
+ };
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(ui::Size reqSize, CaptureFill captureFill, ui::Dataspace reqDataSpace,
- bool hintForSeamlessTransition, bool allowSecureLayers = false)
- : mAllowSecureLayers(allowSecureLayers),
+ ftl::Flags<Options> options)
+ : mOptions(options),
mReqSize(reqSize),
mReqDataSpace(reqDataSpace),
- mCaptureFill(captureFill),
- mHintForSeamlessTransition(hintForSeamlessTransition) {}
+ mCaptureFill(captureFill) {}
static std::function<std::vector<std::pair<Layer*, sp<LayerFE>>>()> fromTraverseLayersLambda(
std::function<void(const LayerVector::Visitor&)> traverseLayers) {
@@ -47,9 +56,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;
@@ -85,18 +91,23 @@
// capture operation.
virtual sp<Layer> getParentLayer() const { return nullptr; }
+ // If this is a LayerRenderArea, return the layer snapshot
+ // of the root layer of the capture operation
+ virtual const frontend::LayerSnapshot* getLayerSnapshot() const { return nullptr; }
+
// Returns whether the render result may be used for system animations that
// must preserve the exact colors of the display.
- bool getHintForSeamlessTransition() const { return mHintForSeamlessTransition; }
+ bool getHintForSeamlessTransition() const {
+ return mOptions.test(Options::HINT_FOR_SEAMLESS_TRANSITION);
+ }
protected:
- const bool mAllowSecureLayers;
+ ftl::Flags<Options> mOptions;
private:
const ui::Size mReqSize;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
- const bool mHintForSeamlessTransition;
};
} // namespace android
diff --git a/services/surfaceflinger/RenderAreaBuilder.h b/services/surfaceflinger/RenderAreaBuilder.h
new file mode 100644
index 0000000..599fa7e
--- /dev/null
+++ b/services/surfaceflinger/RenderAreaBuilder.h
@@ -0,0 +1,102 @@
+/*
+ * 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;
+
+ ftl::Flags<RenderArea::Options> options;
+ virtual std::unique_ptr<RenderArea> build() const = 0;
+
+ RenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ ftl::Flags<RenderArea::Options> options)
+ : crop(crop), reqSize(reqSize), reqDataSpace(reqDataSpace), options(options) {}
+
+ virtual ~RenderAreaBuilder() = default;
+};
+
+struct DisplayRenderAreaBuilder : RenderAreaBuilder {
+ DisplayRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace,
+ wp<const DisplayDevice> displayWeak,
+ ftl::Flags<RenderArea::Options> options)
+ : RenderAreaBuilder(crop, reqSize, reqDataSpace, options), 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, options);
+ }
+};
+
+struct LayerRenderAreaBuilder : RenderAreaBuilder {
+ LayerRenderAreaBuilder(Rect crop, ui::Size reqSize, ui::Dataspace reqDataSpace, sp<Layer> layer,
+ bool childrenOnly, ftl::Flags<RenderArea::Options> options)
+ : RenderAreaBuilder(crop, reqSize, reqDataSpace, options),
+ layer(layer),
+ childrenOnly(childrenOnly) {}
+
+ // Root layer of the render area
+ sp<Layer> layer;
+
+ // Layer snapshot of the root layer
+ frontend::LayerSnapshot layerSnapshot;
+
+ // 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 setLayerSnapshot(const frontend::LayerSnapshot& parentSnapshot) {
+ layerSnapshot = parentSnapshot;
+ if (!childrenOnly) {
+ layerTransform = parentSnapshot.localTransform.inverse();
+ }
+ layerBufferSize = parentSnapshot.bufferSize;
+ }
+
+ std::unique_ptr<RenderArea> build() const override {
+ return std::make_unique<LayerRenderArea>(layer, std::move(layerSnapshot), crop, reqSize,
+ reqDataSpace, layerTransform, layerBufferSize,
+ options);
+ }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
index 16776cf..5455fdc 100644
--- a/services/surfaceflinger/Scheduler/Android.bp
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -53,7 +53,10 @@
cc_test {
name: "libscheduler_test",
test_suites: ["device-tests"],
- defaults: ["libscheduler_defaults"],
+ defaults: [
+ "libscheduler_defaults",
+ "libsurfaceflinger_common_test_deps",
+ ],
srcs: [
"tests/FrameTargeterTest.cpp",
"tests/PresentLatencyTrackerTest.cpp",
@@ -63,9 +66,5 @@
"libgmock",
"libgtest",
"libscheduler",
- "libsurfaceflingerflags_test",
- ],
- shared_libs: [
- "server_configurable_flags",
],
}
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/ISchedulerCallback.h b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
index 9f4f5b6..f430526 100644
--- a/services/surfaceflinger/Scheduler/ISchedulerCallback.h
+++ b/services/surfaceflinger/Scheduler/ISchedulerCallback.h
@@ -32,6 +32,8 @@
virtual void onChoreographerAttached() = 0;
virtual void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>,
Fps renderRate) = 0;
+ virtual void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) = 0;
+ virtual void vrrDisplayIdle(bool idle) = 0;
protected:
~ISchedulerCallback() = default;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index b8d5e76..a819b79 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -40,14 +40,15 @@
namespace {
-bool isLayerActive(const LayerInfo& info, nsecs_t threshold) {
+bool isLayerActive(const LayerInfo& info, nsecs_t threshold, bool isVrrDevice) {
if (FlagManager::getInstance().misc1() && !info.isVisible()) {
return false;
}
// Layers with an explicit frame rate or frame rate category are kept active,
// but ignore NoVote.
- if (info.getSetFrameRateVote().isValid() && !info.getSetFrameRateVote().isNoVote()) {
+ const auto frameRate = info.getSetFrameRateVote();
+ if (frameRate.isValid() && !frameRate.isNoVote() && frameRate.isVoteValidForMrr(isVrrDevice)) {
return true;
}
@@ -194,7 +195,7 @@
std::lock_guard lock(mLock);
- partitionLayers(now);
+ partitionLayers(now, selector.isVrrDevice());
for (const auto& [key, value] : mActiveLayerInfos) {
auto& info = value.second;
@@ -236,7 +237,7 @@
return summary;
}
-void LayerHistory::partitionLayers(nsecs_t now) {
+void LayerHistory::partitionLayers(nsecs_t now, bool isVrrDevice) {
ATRACE_CALL();
const nsecs_t threshold = getActiveLayerThreshold(now);
@@ -244,7 +245,7 @@
LayerInfos::iterator it = mInactiveLayerInfos.begin();
while (it != mInactiveLayerInfos.end()) {
auto& [layerUnsafe, info] = it->second;
- if (isLayerActive(*info, threshold)) {
+ if (isLayerActive(*info, threshold, isVrrDevice)) {
// move this to the active map
mActiveLayerInfos.insert({it->first, std::move(it->second)});
@@ -262,7 +263,7 @@
it = mActiveLayerInfos.begin();
while (it != mActiveLayerInfos.end()) {
auto& [layerUnsafe, info] = it->second;
- if (isLayerActive(*info, threshold)) {
+ if (isLayerActive(*info, threshold, isVrrDevice)) {
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
@@ -279,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:
@@ -305,8 +315,9 @@
trace(*info, gameFrameRateOverrideVoteType,
gameModeFrameRateOverride.getIntValue());
}
- } else if (frameRate.isValid()) {
- info->setLayerVote({setFrameRateVoteType, frameRate.vote.rate,
+ } else if (frameRate.isValid() && frameRate.isVoteValidForMrr(isVrrDevice)) {
+ info->setLayerVote({setFrameRateVoteType,
+ isValuelessVote ? 0_Hz : frameRate.vote.rate,
frameRate.vote.seamlessness, frameRate.category});
if (CC_UNLIKELY(mTraceEnabled)) {
trace(*info, gameFrameRateOverrideVoteType,
@@ -321,14 +332,30 @@
gameDefaultFrameRateOverride.getIntValue());
}
} else {
+ if (frameRate.isValid() && !frameRate.isVoteValidForMrr(isVrrDevice)) {
+ ATRACE_FORMAT_INSTANT("Reset layer to ignore explicit vote on MRR %s: %s "
+ "%s %s",
+ info->getName().c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.category).c_str());
+ }
info->resetLayerVote();
}
} else {
- if (frameRate.isValid()) {
+ 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 "
+ "%s %s",
+ info->getName().c_str(),
+ ftl::enum_string(frameRate.vote.type).c_str(),
+ to_string(frameRate.vote.rate).c_str(),
+ ftl::enum_string(frameRate.category).c_str());
+ }
info->resetLayerVote();
}
}
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index a6f1b56..c09f148 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -111,9 +111,12 @@
std::string dumpGameFrameRateOverridesLocked() const REQUIRES(mLock);
// Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
- // layers to mInactiveLayerInfos.
+ // layers to mInactiveLayerInfos. Layer's active state is determined by multiple factors
+ // such as update activity, visibility, and frame rate vote.
// worst case time complexity is O(2 * inactive + active)
- void partitionLayers(nsecs_t now) REQUIRES(mLock);
+ // now: the current time (system time) when calling the method
+ // isVrrDevice: true if the device has DisplayMode with VrrConfig specified.
+ void partitionLayers(nsecs_t now, bool isVrrDevice) REQUIRES(mLock);
enum class LayerStatus {
NotFound,
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 9745452..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,37 @@
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 {
+ if (isVrrDevice || FlagManager::getInstance().frame_rate_category_mrr()) {
+ return true;
+ }
+
+ if (category == FrameRateCategory::Default) {
+ return true;
+ }
+
+ return false;
}
std::ostream& operator<<(std::ostream& stream, const LayerInfo::FrameRate& rate) {
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 326e444..a7847bc 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -146,6 +146,13 @@
// 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;
+
private:
static Seamlessness getSeamlessness(Fps rate, Seamlessness seamlessness) {
if (!rate.isValid()) {
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 ffd3463..dd86e4f 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,
@@ -474,21 +475,23 @@
}
auto RefreshRateSelector::getRankedFrameRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const -> RankedFrameRates {
+ GlobalSignals signals, Fps pacesetterFps) const
+ -> RankedFrameRates {
+ GetRankedFrameRatesCache cache{layers, signals, pacesetterFps};
+
std::lock_guard lock(mLock);
- if (mGetRankedFrameRatesCache &&
- mGetRankedFrameRatesCache->arguments == std::make_pair(layers, signals)) {
+ if (mGetRankedFrameRatesCache && mGetRankedFrameRatesCache->matches(cache)) {
return mGetRankedFrameRatesCache->result;
}
- const auto result = getRankedFrameRatesLocked(layers, signals);
- mGetRankedFrameRatesCache = GetRankedFrameRatesCache{{layers, signals}, result};
- return result;
+ cache.result = getRankedFrameRatesLocked(layers, signals, pacesetterFps);
+ mGetRankedFrameRatesCache = std::move(cache);
+ return mGetRankedFrameRatesCache->result;
}
auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const
+ GlobalSignals signals, Fps pacesetterFps) const
-> RankedFrameRates {
using namespace fps_approx_ops;
ATRACE_CALL();
@@ -496,6 +499,24 @@
const auto& activeMode = *getActiveModeLocked().modePtr;
+ if (pacesetterFps.isValid()) {
+ ALOGV("Follower display");
+
+ const auto ranking = rankFrameRates(activeMode.getGroup(), RefreshRateOrder::Descending,
+ std::nullopt, [&](FrameRateMode mode) {
+ return mode.modePtr->getPeakFps() == pacesetterFps;
+ });
+
+ if (!ranking.empty()) {
+ ATRACE_FORMAT_INSTANT("%s (Follower display)",
+ to_string(ranking.front().frameRateMode.fps).c_str());
+
+ return {ranking, kNoSignals, pacesetterFps};
+ }
+
+ ALOGW("Follower display cannot follow the pacesetter");
+ }
+
// Keep the display at max frame rate for the duration of powering on the display.
if (signals.powerOnImminent) {
ALOGV("Power On Imminent");
@@ -506,6 +527,8 @@
}
int noVoteLayers = 0;
+ // Layers that prefer the same mode ("no-op").
+ int noPreferenceLayers = 0;
int minVoteLayers = 0;
int maxVoteLayers = 0;
int explicitDefaultVoteLayers = 0;
@@ -549,10 +572,7 @@
explicitCategoryVoteLayers++;
}
if (layer.frameRateCategory == FrameRateCategory::NoPreference) {
- // Count this layer for Min vote as well. The explicit vote avoids
- // touch boost and idle for choosing a category, while Min vote is for correct
- // behavior when all layers are Min or no vote.
- minVoteLayers++;
+ noPreferenceLayers++;
}
break;
case LayerVoteType::Heuristic:
@@ -612,6 +632,16 @@
return {ranking, kNoSignals};
}
+ // If all layers are category NoPreference, use the current config.
+ if (noPreferenceLayers + noVoteLayers == layers.size()) {
+ ALOGV("All layers NoPreference");
+ const auto ascendingWithPreferred =
+ rankFrameRates(anchorGroup, RefreshRateOrder::Ascending, activeMode.getId());
+ ATRACE_FORMAT_INSTANT("%s (All layers NoPreference)",
+ to_string(ascendingWithPreferred.front().frameRateMode.fps).c_str());
+ return {ascendingWithPreferred, kNoSignals};
+ }
+
const bool smoothSwitchOnly = categorySmoothSwitchOnlyLayers > 0;
const DisplayModeId activeModeId = activeMode.getId();
@@ -643,6 +673,7 @@
ftl::enum_string(layer.frameRateCategory).c_str());
if (layer.isNoVote() || layer.frameRateCategory == FrameRateCategory::NoPreference ||
layer.vote == LayerVoteType::Min) {
+ ALOGV("%s scoring skipped due to vote", formatLayerInfo(layer, layer.weight).c_str());
continue;
}
@@ -831,18 +862,19 @@
// 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 != layers.size();
+ return explicitExact + noVoteLayers + explicitGteLayers != layers.size();
} else {
// Enable touch boost if there are no exact layers
return explicitExact == 0;
}
- }();
+ };
- 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
@@ -850,12 +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 &&
- 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
@@ -869,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};
}
@@ -1029,6 +1066,11 @@
ALOGV("%s: overriding to %s for uid=%d", __func__, to_string(overrideFps).c_str(), uid);
ATRACE_FORMAT_INSTANT("%s: overriding to %s for uid=%d", __func__,
to_string(overrideFps).c_str(), uid);
+ if (ATRACE_ENABLED() && FlagManager::getInstance().trace_frame_rate_override()) {
+ std::stringstream ss;
+ ss << "FrameRateOverride " << uid;
+ ATRACE_INT(ss.str().c_str(), overrideFps.getIntValue());
+ }
frameRateOverrides.emplace(uid, overrideFps);
}
@@ -1212,19 +1254,21 @@
LOG_ALWAYS_FATAL_IF(!activeModeOpt);
mActiveModeOpt.emplace(FrameRateMode{renderFrameRate, ftl::as_non_null(activeModeOpt->get())});
+ mIsVrrDevice = FlagManager::getInstance().vrr_config() &&
+ activeModeOpt->get()->getVrrConfig().has_value();
}
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()) {
@@ -1347,9 +1391,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();
@@ -1425,7 +1500,8 @@
}
return str;
};
- ALOGV("%s render rates: %s", rangeName, stringifyModes().c_str());
+ ALOGV("%s render rates: %s, isVrrDevice? %d", rangeName, stringifyModes().c_str(),
+ mIsVrrDevice.load());
return frameRateModes;
};
@@ -1434,6 +1510,10 @@
mAppRequestFrameRates = filterRefreshRates(policy->appRequestRanges, "app request");
}
+bool RefreshRateSelector::isVrrDevice() const {
+ return mIsVrrDevice;
+}
+
Fps RefreshRateSelector::findClosestKnownFrameRate(Fps frameRate) const {
using namespace fps_approx_ops;
@@ -1514,7 +1594,8 @@
std::lock_guard lock(mLock);
const auto activeMode = getActiveModeLocked();
- dumper.dump("activeMode"sv, to_string(activeMode));
+ dumper.dump("renderRate"sv, to_string(activeMode.fps));
+ dumper.dump("activeMode"sv, to_string(*activeMode.modePtr));
dumper.dump("displayModes"sv);
{
@@ -1545,7 +1626,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.
@@ -1554,19 +1638,17 @@
case FrameRateCategory::High:
return FpsRange{90_Hz, 120_Hz};
case FrameRateCategory::Normal:
- return FpsRange{60_Hz, 90_Hz};
+ return FpsRange{60_Hz, 120_Hz};
case FrameRateCategory::Low:
- return FpsRange{30_Hz, 30_Hz};
+ return FpsRange{30_Hz, 120_Hz};
case FrameRateCategory::HighHint:
case FrameRateCategory::NoPreference:
case FrameRateCategory::Default:
LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
default:
LOG_ALWAYS_FATAL("Invalid frame rate category for range: %s",
ftl::enum_string(category).c_str());
- return FpsRange{0_Hz, 0_Hz};
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.h b/services/surfaceflinger/Scheduler/RefreshRateSelector.h
index e8153f5..998b1b8 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;
};
@@ -233,14 +246,18 @@
struct RankedFrameRates {
FrameRateRanking ranking; // Ordered by descending score.
GlobalSignals consideredSignals;
+ Fps pacesetterFps;
bool operator==(const RankedFrameRates& other) const {
- return ranking == other.ranking && consideredSignals == other.consideredSignals;
+ return ranking == other.ranking && consideredSignals == other.consideredSignals &&
+ isApproxEqual(pacesetterFps, other.pacesetterFps);
}
};
- RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals) const
- EXCLUDES(mLock);
+ // If valid, `pacesetterFps` (used by follower displays) filters the ranking to modes matching
+ // that refresh rate.
+ RankedFrameRates getRankedFrameRates(const std::vector<LayerRequirement>&, GlobalSignals,
+ Fps pacesetterFps = {}) const EXCLUDES(mLock);
FpsRange getSupportedRefreshRateRange() const EXCLUDES(mLock) {
std::lock_guard lock(mLock);
@@ -287,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.
@@ -298,7 +315,7 @@
DisplayModes, DisplayModeId activeModeId,
Config config = {.enableFrameRateOverride = Config::FrameRateOverride::Disabled,
.frameRateMultipleThreshold = 0,
- .idleTimerTimeout = 0ms,
+ .legacyIdleTimerTimeout = 0ms,
.kernelIdleTimerController = {}});
RefreshRateSelector(const RefreshRateSelector&) = delete;
@@ -369,6 +386,7 @@
Callbacks platform;
Callbacks kernel;
+ Callbacks vrr;
};
void setIdleTimerCallbacks(IdleTimerCallbacks callbacks) EXCLUDES(mIdleTimerCallbacksMutex) {
@@ -382,12 +400,14 @@
}
void startIdleTimer() {
+ mIdleTimerStarted = true;
if (mIdleTimer) {
mIdleTimer->start();
}
}
void stopIdleTimer() {
+ mIdleTimerStarted = false;
if (mIdleTimer) {
mIdleTimer->stop();
}
@@ -409,6 +429,8 @@
std::chrono::milliseconds getIdleTimerTimeout();
+ bool isVrrDevice() const;
+
private:
friend struct TestableRefreshRateSelector;
@@ -418,7 +440,8 @@
const FrameRateMode& getActiveModeLocked() const REQUIRES(mLock);
RankedFrameRates getRankedFrameRatesLocked(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals) const REQUIRES(mLock);
+ GlobalSignals signals, Fps pacesetterFps) const
+ REQUIRES(mLock);
// Returns number of display frames and remainder when dividing the layer refresh period by
// display refresh period.
@@ -477,11 +500,14 @@
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) {
if (!mIdleTimerCallbacks) return {};
+
+ if (mIsVrrDevice) return mIdleTimerCallbacks->vrr;
+
return mConfig.kernelIdleTimerController.has_value() ? mIdleTimerCallbacks->kernel
: mIdleTimerCallbacks->platform;
}
@@ -516,6 +542,9 @@
std::vector<FrameRateMode> mPrimaryFrameRates GUARDED_BY(mLock);
std::vector<FrameRateMode> mAppRequestFrameRates GUARDED_BY(mLock);
+ // Caches whether the device is VRR-compatible based on the active display mode.
+ std::atomic_bool mIsVrrDevice = false;
+
Policy mDisplayManagerPolicy GUARDED_BY(mLock);
std::optional<Policy> mOverridePolicy GUARDED_BY(mLock);
@@ -537,8 +566,16 @@
Config::FrameRateOverride mFrameRateOverrideConfig;
struct GetRankedFrameRatesCache {
- std::pair<std::vector<LayerRequirement>, GlobalSignals> arguments;
+ std::vector<LayerRequirement> layers;
+ GlobalSignals signals;
+ Fps pacesetterFps;
+
RankedFrameRates result;
+
+ bool matches(const GetRankedFrameRatesCache& other) const {
+ return layers == other.layers && signals == other.signals &&
+ isApproxEqual(pacesetterFps, other.pacesetterFps);
+ }
};
mutable std::optional<GetRankedFrameRatesCache> mGetRankedFrameRatesCache GUARDED_BY(mLock);
@@ -547,6 +584,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 3f91682..5ec7e48 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -82,7 +82,7 @@
mTouchTimer.reset();
// Stop idle timer and clear callbacks, as the RefreshRateSelector may outlive the Scheduler.
- demotePacesetterDisplay();
+ demotePacesetterDisplay({.toggleIdleTimer = true});
}
void Scheduler::initVsync(frametimeline::TokenManager& tokenManager,
@@ -117,35 +117,45 @@
}
}
-void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
- demotePacesetterDisplay();
+void Scheduler::setPacesetterDisplay(PhysicalDisplayId pacesetterId) {
+ constexpr PromotionParams kPromotionParams = {.toggleIdleTimer = true};
- promotePacesetterDisplay(pacesetterIdOpt);
+ demotePacesetterDisplay(kPromotionParams);
+ promotePacesetterDisplay(pacesetterId, kPromotionParams);
}
-void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) {
+void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ PhysicalDisplayId activeDisplayId) {
auto schedulePtr =
std::make_shared<VsyncSchedule>(selectorPtr->getActiveMode().modePtr, mFeatures,
[this](PhysicalDisplayId id, bool enable) {
onHardwareVsyncRequest(id, enable);
});
- registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr));
+ registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr),
+ activeDisplayId);
}
void Scheduler::registerDisplayInternal(PhysicalDisplayId displayId,
RefreshRateSelectorPtr selectorPtr,
- VsyncSchedulePtr schedulePtr) {
- demotePacesetterDisplay();
+ VsyncSchedulePtr schedulePtr,
+ PhysicalDisplayId activeDisplayId) {
+ const bool isPrimary = (ftl::FakeGuard(mDisplayLock), !mPacesetterDisplayId);
- auto [pacesetterVsyncSchedule, isNew] = [&]() FTL_FAKE_GUARD(kMainThreadContext) {
+ // Start the idle timer for the first registered (i.e. primary) display.
+ const PromotionParams promotionParams = {.toggleIdleTimer = isPrimary};
+
+ demotePacesetterDisplay(promotionParams);
+
+ auto [pacesetterVsyncSchedule, isNew] = [&]() REQUIRES(kMainThreadContext) {
std::scoped_lock lock(mDisplayLock);
const bool isNew = mDisplays
.emplace_or_replace(displayId, displayId, std::move(selectorPtr),
std::move(schedulePtr), mFeatures)
.second;
- return std::make_pair(promotePacesetterDisplayLocked(), isNew);
+ return std::make_pair(promotePacesetterDisplayLocked(activeDisplayId, promotionParams),
+ isNew);
}();
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
@@ -158,10 +168,13 @@
dispatchHotplug(displayId, Hotplug::Connected);
}
-void Scheduler::unregisterDisplay(PhysicalDisplayId displayId) {
+void Scheduler::unregisterDisplay(PhysicalDisplayId displayId, PhysicalDisplayId activeDisplayId) {
+ LOG_ALWAYS_FATAL_IF(displayId == activeDisplayId, "Cannot unregister the active display!");
+
dispatchHotplug(displayId, Hotplug::Disconnected);
- demotePacesetterDisplay();
+ constexpr PromotionParams kPromotionParams = {.toggleIdleTimer = false};
+ demotePacesetterDisplay(kPromotionParams);
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
{
@@ -173,7 +186,7 @@
// headless virtual display.)
LOG_ALWAYS_FATAL_IF(mDisplays.empty(), "Cannot unregister all displays!");
- pacesetterVsyncSchedule = promotePacesetterDisplayLocked();
+ pacesetterVsyncSchedule = promotePacesetterDisplayLocked(activeDisplayId, kPromotionParams);
}
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
@@ -221,6 +234,7 @@
if (FlagManager::getInstance().vrr_config()) {
compositor.sendNotifyExpectedPresentHint(pacesetterPtr->displayId);
}
+ mSchedulerCallback.onCommitNotComposited(pacesetterPtr->displayId);
return;
}
}
@@ -250,18 +264,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);
@@ -306,8 +308,11 @@
const auto pacesetterOpt = pacesetterDisplayLocked();
LOG_ALWAYS_FATAL_IF(!pacesetterOpt);
const Display& pacesetter = *pacesetterOpt;
- return std::make_pair(pacesetter.selectorPtr->getActiveMode().fps,
- pacesetter.schedulePtr->period());
+ const FrameRateMode& frameRateMode = pacesetter.selectorPtr->getActiveMode();
+ const auto refreshRate = frameRateMode.fps;
+ const auto displayVsync = frameRateMode.modePtr->getVsyncRate();
+ const auto numPeriod = RefreshRateSelector::getFrameRateDivisor(displayVsync, refreshRate);
+ return std::make_pair(refreshRate, numPeriod * pacesetter.schedulePtr->period());
}();
const Period currentPeriod = period != Period::zero() ? period : refreshRate.getPeriod();
@@ -565,7 +570,7 @@
}));
}
-void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) {
+void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate, bool applyImmediately) {
std::scoped_lock lock(mDisplayLock);
ftl::FakeGuard guard(kMainThreadContext);
@@ -586,7 +591,7 @@
ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(),
to_string(mode.modePtr->getVsyncRate()).c_str());
- display.schedulePtr->getTracker().setRenderRate(renderFrameRate);
+ display.schedulePtr->getTracker().setRenderRate(renderFrameRate, applyImmediately);
}
Fps Scheduler::getNextFrameInterval(PhysicalDisplayId id,
@@ -920,35 +925,38 @@
return mFrameRateOverrideMappings.updateFrameRateOverridesByContent(frameRateOverrides);
}
-void Scheduler::promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt) {
+void Scheduler::promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams params) {
std::shared_ptr<VsyncSchedule> pacesetterVsyncSchedule;
-
{
std::scoped_lock lock(mDisplayLock);
- pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterIdOpt);
+ pacesetterVsyncSchedule = promotePacesetterDisplayLocked(pacesetterId, params);
}
applyNewVsyncSchedule(std::move(pacesetterVsyncSchedule));
}
std::shared_ptr<VsyncSchedule> Scheduler::promotePacesetterDisplayLocked(
- std::optional<PhysicalDisplayId> pacesetterIdOpt) {
- // TODO(b/241286431): Choose the pacesetter display.
- mPacesetterDisplayId = pacesetterIdOpt.value_or(mDisplays.begin()->first);
- ALOGI("Display %s is the pacesetter", to_string(*mPacesetterDisplayId).c_str());
+ PhysicalDisplayId pacesetterId, PromotionParams params) {
+ // TODO: b/241286431 - Choose the pacesetter among mDisplays.
+ mPacesetterDisplayId = pacesetterId;
+ ALOGI("Display %s is the pacesetter", to_string(pacesetterId).c_str());
std::shared_ptr<VsyncSchedule> newVsyncSchedulePtr;
if (const auto pacesetterOpt = pacesetterDisplayLocked()) {
const Display& pacesetter = *pacesetterOpt;
- pacesetter.selectorPtr->setIdleTimerCallbacks(
- {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
- .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
- .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
- .onExpired =
- [this] { kernelIdleTimerCallback(TimerState::Expired); }}});
+ if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ pacesetter.selectorPtr->setIdleTimerCallbacks(
+ {.platform = {.onReset = [this] { idleTimerCallback(TimerState::Reset); },
+ .onExpired = [this] { idleTimerCallback(TimerState::Expired); }},
+ .kernel = {.onReset = [this] { kernelIdleTimerCallback(TimerState::Reset); },
+ .onExpired =
+ [this] { kernelIdleTimerCallback(TimerState::Expired); }},
+ .vrr = {.onReset = [this] { mSchedulerCallback.vrrDisplayIdle(false); },
+ .onExpired = [this] { mSchedulerCallback.vrrDisplayIdle(true); }}});
- pacesetter.selectorPtr->startIdleTimer();
+ pacesetter.selectorPtr->startIdleTimer();
+ }
newVsyncSchedulePtr = pacesetter.schedulePtr;
@@ -968,11 +976,14 @@
}
}
-void Scheduler::demotePacesetterDisplay() {
- // No need to lock for reads on kMainThreadContext.
- if (const auto pacesetterPtr = FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
- pacesetterPtr->stopIdleTimer();
- pacesetterPtr->clearIdleTimerCallbacks();
+void Scheduler::demotePacesetterDisplay(PromotionParams params) {
+ if (!FlagManager::getInstance().connected_display() || params.toggleIdleTimer) {
+ // No need to lock for reads on kMainThreadContext.
+ if (const auto pacesetterPtr =
+ FTL_FAKE_GUARD(mDisplayLock, pacesetterSelectorPtrLocked())) {
+ pacesetterPtr->stopIdleTimer();
+ pacesetterPtr->clearIdleTimerCallbacks();
+ }
}
// Clear state that depends on the pacesetter's RefreshRateSelector.
@@ -1146,38 +1157,33 @@
auto Scheduler::chooseDisplayModes() const -> DisplayModeChoiceMap {
ATRACE_CALL();
- using RankedRefreshRates = RefreshRateSelector::RankedFrameRates;
- ui::PhysicalDisplayVector<RankedRefreshRates> perDisplayRanking;
- const auto globalSignals = makeGlobalSignals();
- Fps pacesetterFps;
-
- for (const auto& [id, display] : mDisplays) {
- auto rankedFrameRates =
- display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements,
- globalSignals);
- if (id == *mPacesetterDisplayId) {
- pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
- }
- perDisplayRanking.push_back(std::move(rankedFrameRates));
- }
-
DisplayModeChoiceMap modeChoices;
- using fps_approx_ops::operator==;
+ const auto globalSignals = makeGlobalSignals();
- for (auto& [rankings, signals] : perDisplayRanking) {
- const auto chosenFrameRateMode =
- ftl::find_if(rankings,
- [&](const auto& ranking) {
- return ranking.frameRateMode.fps == pacesetterFps;
- })
- .transform([](const auto& scoredFrameRate) {
- return scoredFrameRate.get().frameRateMode;
- })
- .value_or(rankings.front().frameRateMode);
+ const Fps pacesetterFps = [&]() REQUIRES(mPolicyLock, mDisplayLock, kMainThreadContext) {
+ auto rankedFrameRates =
+ pacesetterSelectorPtrLocked()->getRankedFrameRates(mPolicy.contentRequirements,
+ globalSignals);
- modeChoices.try_emplace(chosenFrameRateMode.modePtr->getPhysicalDisplayId(),
- DisplayModeChoice{chosenFrameRateMode, signals});
+ const Fps pacesetterFps = rankedFrameRates.ranking.front().frameRateMode.fps;
+
+ modeChoices.try_emplace(*mPacesetterDisplayId,
+ DisplayModeChoice::from(std::move(rankedFrameRates)));
+ return pacesetterFps;
+ }();
+
+ // Choose a mode for powered-on follower displays.
+ for (const auto& [id, display] : mDisplays) {
+ if (id == *mPacesetterDisplayId) continue;
+ if (display.powerMode != hal::PowerMode::ON) continue;
+
+ auto rankedFrameRates =
+ display.selectorPtr->getRankedFrameRates(mPolicy.contentRequirements, globalSignals,
+ pacesetterFps);
+
+ modeChoices.try_emplace(id, DisplayModeChoice::from(std::move(rankedFrameRates)));
}
+
return modeChoices;
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 09f75fd..94583db 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -92,8 +92,8 @@
void startTimers();
- // TODO(b/241285191): Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
- void setPacesetterDisplay(std::optional<PhysicalDisplayId>) REQUIRES(kMainThreadContext)
+ // TODO: b/241285191 - Remove this API by promoting pacesetter in onScreen{Acquired,Released}.
+ void setPacesetterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
using RefreshRateSelectorPtr = std::shared_ptr<RefreshRateSelector>;
@@ -101,9 +101,16 @@
using ConstVsyncSchedulePtr = std::shared_ptr<const VsyncSchedule>;
using VsyncSchedulePtr = std::shared_ptr<VsyncSchedule>;
- void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr) REQUIRES(kMainThreadContext)
+ // After registration/unregistration, `activeDisplayId` is promoted to pacesetter. Note that the
+ // active display is never unregistered, since hotplug disconnect never happens for activatable
+ // displays, i.e. a foldable's internal displays or otherwise the (internal or external) primary
+ // display.
+ // TODO: b/255635821 - Remove active display parameters.
+ void registerDisplay(PhysicalDisplayId, RefreshRateSelectorPtr,
+ PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext)
EXCLUDES(mDisplayLock);
- void unregisterDisplay(PhysicalDisplayId) REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void unregisterDisplay(PhysicalDisplayId, PhysicalDisplayId activeDisplayId)
+ REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
void run();
@@ -188,7 +195,7 @@
const VsyncConfiguration& getVsyncConfiguration() const { return *mVsyncConfiguration; }
// Sets the render rate for the scheduler to run at.
- void setRenderRate(PhysicalDisplayId, Fps);
+ void setRenderRate(PhysicalDisplayId, Fps, bool applyImmediately);
void enableHardwareVsync(PhysicalDisplayId) REQUIRES(kMainThreadContext);
void disableHardwareVsync(PhysicalDisplayId, bool disallow) REQUIRES(kMainThreadContext);
@@ -346,7 +353,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 {
@@ -368,9 +377,16 @@
void resyncAllToHardwareVsync(bool allowToEnable) EXCLUDES(mDisplayLock);
void setVsyncConfig(const VsyncConfig&, Period vsyncPeriod);
- // Chooses a pacesetter among the registered displays, unless `pacesetterIdOpt` is specified.
- // The new `mPacesetterDisplayId` is never `std::nullopt`.
- void promotePacesetterDisplay(std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+ // TODO: b/241286431 - Remove this option, which assumes that the pacesetter does not change
+ // when a (secondary) display is registered or unregistered. In the short term, this avoids
+ // a deadlock where the main thread joins with the timer thread as the timer thread waits to
+ // lock a mutex held by the main thread.
+ struct PromotionParams {
+ // Whether to stop and start the idle timer. Ignored unless connected_display flag is set.
+ bool toggleIdleTimer;
+ };
+
+ void promotePacesetterDisplay(PhysicalDisplayId pacesetterId, PromotionParams)
REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
// Changes to the displays (e.g. registering and unregistering) must be made
@@ -379,17 +395,20 @@
// MessageQueue and EventThread need to use the new pacesetter's
// VsyncSchedule, and this must happen while mDisplayLock is *not* locked,
// or else we may deadlock with EventThread.
- std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(
- std::optional<PhysicalDisplayId> pacesetterIdOpt = std::nullopt)
+ std::shared_ptr<VsyncSchedule> promotePacesetterDisplayLocked(PhysicalDisplayId pacesetterId,
+ PromotionParams)
REQUIRES(kMainThreadContext, mDisplayLock);
void applyNewVsyncSchedule(std::shared_ptr<VsyncSchedule>) EXCLUDES(mDisplayLock);
- // Blocks until the pacesetter's idle timer thread exits. `mDisplayLock` must not be locked by
- // the caller on the main thread to avoid deadlock, since the timer thread locks it before exit.
- void demotePacesetterDisplay() REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock, mPolicyLock);
+ // If toggleIdleTimer is true, the calling thread blocks until the pacesetter's idle timer
+ // thread exits, in which case mDisplayLock must not be locked by the caller to avoid deadlock,
+ // since the timer thread locks it before exit.
+ void demotePacesetterDisplay(PromotionParams) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock, mPolicyLock);
- void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr)
- REQUIRES(kMainThreadContext) EXCLUDES(mDisplayLock);
+ void registerDisplayInternal(PhysicalDisplayId, RefreshRateSelectorPtr, VsyncSchedulePtr,
+ PhysicalDisplayId activeDisplayId) REQUIRES(kMainThreadContext)
+ EXCLUDES(mDisplayLock);
struct Policy;
@@ -402,6 +421,11 @@
DisplayModeChoice(FrameRateMode mode, GlobalSignals consideredSignals)
: mode(std::move(mode)), consideredSignals(consideredSignals) {}
+ static DisplayModeChoice from(RefreshRateSelector::RankedFrameRates rankedFrameRates) {
+ return {rankedFrameRates.ranking.front().frameRateMode,
+ rankedFrameRates.consideredSignals};
+ }
+
FrameRateMode mode;
GlobalSignals consideredSignals;
@@ -437,6 +461,7 @@
// IEventThreadCallback overrides
bool throttleVsync(TimePoint, uid_t) override;
+ // Get frame interval
Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock);
void resync() override EXCLUDES(mDisplayLock);
void onExpectedPresentTimePosted(TimePoint expectedPresentTime) override EXCLUDES(mDisplayLock);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 2f9dfea..dd3c4b0 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -46,24 +46,11 @@
static auto constexpr kMaxPercent = 100u;
namespace {
-nsecs_t getVsyncFixup(VSyncPredictor::Model model, Period minFramePeriod, nsecs_t vsyncTime,
- std::optional<nsecs_t> lastVsyncOpt) {
- const auto threshold = model.slope / 2;
-
- if (FlagManager::getInstance().vrr_config() && lastVsyncOpt) {
- const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
- if (vsyncDiff >= threshold && vsyncDiff <= minFramePeriod.ns() - threshold) {
- const auto vsyncFixup = *lastVsyncOpt + minFramePeriod.ns() - vsyncTime;
- ATRACE_FORMAT_INSTANT("minFramePeriod violation. next in %.2f which is %.2f from prev. "
- "adjust by %.2f",
- static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
- static_cast<float>(vsyncTime - *lastVsyncOpt) / 1e6f,
- static_cast<float>(vsyncFixup) / 1e6f);
- return vsyncFixup;
- }
- }
-
- return 0;
+int numVsyncsPerFrame(const ftl::NonNull<DisplayModePtr>& displayModePtr) {
+ const auto idealPeakRefreshPeriod = displayModePtr->getPeakFps().getPeriodNsecs();
+ const auto idealRefreshPeriod = displayModePtr->getVsyncRate().getPeriodNsecs();
+ return static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
+ static_cast<float>(idealRefreshPeriod)));
}
} // namespace
@@ -78,7 +65,8 @@
kHistorySize(historySize),
kMinimumSamplesForPrediction(minimumSamplesForPrediction),
kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)),
- mDisplayModePtr(modePtr) {
+ mDisplayModePtr(modePtr),
+ mNumVsyncsForFrame(numVsyncsPerFrame(mDisplayModePtr)) {
resetModel();
}
@@ -142,11 +130,8 @@
}
Period VSyncPredictor::minFramePeriodLocked() const {
- const auto idealPeakRefreshPeriod = mDisplayModePtr->getPeakFps().getPeriodNsecs();
- const auto numPeriods = static_cast<int>(std::round(static_cast<float>(idealPeakRefreshPeriod) /
- static_cast<float>(idealPeriod())));
const auto slope = mRateMap.find(idealPeriod())->second.slope;
- return Period::fromNs(slope * numPeriods);
+ return Period::fromNs(slope * mNumVsyncsForFrame);
}
bool VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
@@ -318,12 +303,25 @@
const auto now = TimePoint::fromNs(mClock->now());
purgeTimelines(now);
+ if (lastVsyncOpt && *lastVsyncOpt > timePoint) {
+ timePoint = *lastVsyncOpt;
+ }
+
+ const auto model = getVSyncPredictionModelLocked();
+ const auto threshold = model.slope / 2;
+ std::optional<Period> minFramePeriodOpt;
+
+ if (mNumVsyncsForFrame > 1) {
+ minFramePeriodOpt = minFramePeriodLocked();
+ }
+
std::optional<TimePoint> vsyncOpt;
for (auto& timeline : mTimelines) {
- vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(getVSyncPredictionModelLocked(),
- minFramePeriodLocked(),
+ vsyncOpt = timeline.nextAnticipatedVSyncTimeFrom(model, minFramePeriodOpt,
snapToVsync(timePoint), mMissedVsync,
- lastVsyncOpt);
+ lastVsyncOpt ? snapToVsync(*lastVsyncOpt -
+ threshold)
+ : lastVsyncOpt);
if (vsyncOpt) {
break;
}
@@ -371,13 +369,36 @@
return mTimelines.back().isVSyncInPhase(model, vsync, frameRate);
}
-void VSyncPredictor::setRenderRate(Fps renderRate) {
+void VSyncPredictor::setRenderRate(Fps renderRate, bool applyImmediately) {
ATRACE_FORMAT("%s %s", __func__, to_string(renderRate).c_str());
ALOGV("%s %s: RenderRate %s ", __func__, to_string(mId).c_str(), to_string(renderRate).c_str());
std::lock_guard lock(mMutex);
+ const auto prevRenderRate = mRenderRateOpt;
mRenderRateOpt = renderRate;
- mTimelines.back().freeze(TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
- mTimelines.emplace_back(mIdealPeriod, renderRate);
+ const auto renderPeriodDelta =
+ prevRenderRate ? prevRenderRate->getPeriodNsecs() - renderRate.getPeriodNsecs() : 0;
+ if (applyImmediately) {
+ ATRACE_FORMAT_INSTANT("applyImmediately");
+ while (mTimelines.size() > 1) {
+ mTimelines.pop_front();
+ }
+
+ mTimelines.front().setRenderRate(renderRate);
+ return;
+ }
+
+ const bool newRenderRateIsHigher = renderPeriodDelta > renderRate.getPeriodNsecs() &&
+ mLastCommittedVsync.ns() - mClock->now() > 2 * renderRate.getPeriodNsecs();
+ if (newRenderRateIsHigher) {
+ ATRACE_FORMAT_INSTANT("newRenderRateIsHigher");
+ mTimelines.clear();
+ mLastCommittedVsync = TimePoint::fromNs(0);
+
+ } else {
+ mTimelines.back().freeze(
+ TimePoint::fromNs(mLastCommittedVsync.ns() + mIdealPeriod.ns() / 2));
+ }
+ mTimelines.emplace_back(mLastCommittedVsync, mIdealPeriod, renderRate);
purgeTimelines(TimePoint::fromNs(mClock->now()));
}
@@ -394,6 +415,7 @@
std::lock_guard lock(mMutex);
mDisplayModePtr = modePtr;
+ mNumVsyncsForFrame = numVsyncsPerFrame(mDisplayModePtr);
traceInt64("VSP-setPeriod", modePtr->getVsyncRate().getPeriodNsecs());
static constexpr size_t kSizeLimit = 30;
@@ -405,12 +427,18 @@
mRateMap[idealPeriod()] = {idealPeriod(), 0};
}
+ mTimelines.clear();
clearTimestamps();
}
Duration VSyncPredictor::ensureMinFrameDurationIsKept(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) {
ATRACE_CALL();
+
+ if (mNumVsyncsForFrame <= 1) {
+ return 0ns;
+ }
+
const auto currentPeriod = mRateMap.find(idealPeriod())->second.slope;
const auto threshold = currentPeriod / 2;
const auto minFramePeriod = minFramePeriodLocked().ns();
@@ -526,10 +554,23 @@
mLastTimestampIndex = 0;
}
- mTimelines.clear();
- mLastCommittedVsync = TimePoint::fromNs(0);
mIdealPeriod = Period::fromNs(idealPeriod());
- mTimelines.emplace_back(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 {
@@ -557,6 +598,17 @@
}
void VSyncPredictor::purgeTimelines(android::TimePoint now) {
+ const auto kEnoughFramesToBreakPhase = 5;
+ 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);
+ return;
+ }
+
while (mTimelines.size() > 1) {
const auto validUntilOpt = mTimelines.front().validUntil();
if (validUntilOpt && *validUntilOpt < now) {
@@ -569,8 +621,17 @@
LOG_ALWAYS_FATAL_IF(mTimelines.back().validUntil().has_value());
}
-VSyncPredictor::VsyncTimeline::VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt)
- : mIdealPeriod(idealPeriod), mRenderRateOpt(renderRateOpt) {}
+auto VSyncPredictor::VsyncTimeline::makeVsyncSequence(TimePoint knownVsync)
+ -> std::optional<VsyncSequence> {
+ if (knownVsync.ns() == 0) return std::nullopt;
+ return std::make_optional<VsyncSequence>({knownVsync.ns(), 0});
+}
+
+VSyncPredictor::VsyncTimeline::VsyncTimeline(TimePoint knownVsync, Period idealPeriod,
+ std::optional<Fps> renderRateOpt)
+ : mIdealPeriod(idealPeriod),
+ mRenderRateOpt(renderRateOpt),
+ mLastVsyncSequence(makeVsyncSequence(knownVsync)) {}
void VSyncPredictor::VsyncTimeline::freeze(TimePoint lastVsync) {
LOG_ALWAYS_FATAL_IF(mValidUntil.has_value());
@@ -581,21 +642,42 @@
}
std::optional<TimePoint> VSyncPredictor::VsyncTimeline::nextAnticipatedVSyncTimeFrom(
- Model model, Period minFramePeriod, nsecs_t vsync, MissedVsync missedVsync,
- std::optional<nsecs_t> lastVsyncOpt) {
+ Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsync,
+ MissedVsync missedVsync, std::optional<nsecs_t> lastVsyncOpt) {
ATRACE_FORMAT("renderRate %s", mRenderRateOpt ? to_string(*mRenderRateOpt).c_str() : "NA");
+ nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
const auto threshold = model.slope / 2;
const auto lastFrameMissed =
lastVsyncOpt && std::abs(*lastVsyncOpt - missedVsync.vsync.ns()) < threshold;
- nsecs_t vsyncTime = snapToVsyncAlignedWithRenderRate(model, vsync);
- nsecs_t vsyncFixupTime = 0;
- if (FlagManager::getInstance().vrr_config() && lastFrameMissed) {
- vsyncTime += missedVsync.fixup.ns();
- ATRACE_FORMAT_INSTANT("lastFrameMissed");
- } else {
- vsyncFixupTime = getVsyncFixup(model, minFramePeriod, vsyncTime, lastVsyncOpt);
- vsyncTime += vsyncFixupTime;
+ const auto mightBackpressure = minFramePeriodOpt && mRenderRateOpt &&
+ mRenderRateOpt->getPeriod() < 2 * (*minFramePeriodOpt);
+ if (FlagManager::getInstance().vrr_config()) {
+ if (lastFrameMissed) {
+ // If the last frame missed is the last vsync, we already shifted the timeline. Depends
+ // on whether we skipped the frame (onFrameMissed) or not (onFrameBegin) we apply a
+ // different fixup. There is no need to to shift the vsync timeline again.
+ vsyncTime += missedVsync.fixup.ns();
+ ATRACE_FORMAT_INSTANT("lastFrameMissed");
+ } else if (mightBackpressure && lastVsyncOpt) {
+ // lastVsyncOpt is based on the old timeline before we shifted it. we should correct it
+ // first before trying to use it.
+ lastVsyncOpt = snapToVsyncAlignedWithRenderRate(model, *lastVsyncOpt);
+ const auto vsyncDiff = vsyncTime - *lastVsyncOpt;
+ if (vsyncDiff <= minFramePeriodOpt->ns() - threshold) {
+ // avoid a duplicate vsync
+ ATRACE_FORMAT_INSTANT("skipping a vsync to avoid duplicate frame. next in %.2f "
+ "which "
+ "is %.2f "
+ "from "
+ "prev. "
+ "adjust by %.2f",
+ static_cast<float>(vsyncTime - TimePoint::now().ns()) / 1e6f,
+ static_cast<float>(vsyncDiff) / 1e6f,
+ static_cast<float>(mRenderRateOpt->getPeriodNsecs()) / 1e6f);
+ vsyncTime += mRenderRateOpt->getPeriodNsecs();
+ }
+ }
}
ATRACE_FORMAT_INSTANT("vsync in %.2fms", float(vsyncTime - TimePoint::now().ns()) / 1e6f);
@@ -605,10 +687,6 @@
return std::nullopt;
}
- if (vsyncFixupTime > 0) {
- shiftVsyncSequence(Duration::fromNs(vsyncFixupTime));
- }
-
return TimePoint::fromNs(vsyncTime);
}
@@ -659,7 +737,9 @@
return ticks<std::milli, float>(TimePoint::fromNs(timePoint) - now);
};
- Fps displayFps = mRenderRateOpt ? *mRenderRateOpt : Fps::fromPeriodNsecs(mIdealPeriod.ns());
+ Fps displayFps = !FlagManager::getInstance().vrr_bugfix_24q4() && mRenderRateOpt
+ ? *mRenderRateOpt
+ : Fps::fromPeriodNsecs(mIdealPeriod.ns());
const auto divisor = RefreshRateSelector::getFrameRateDivisor(displayFps, frameRate);
const auto now = TimePoint::now();
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index c175765..8ce61d8 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -68,7 +68,14 @@
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final EXCLUDES(mMutex);
- void setRenderRate(Fps) 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
EXCLUDES(mMutex);
@@ -83,27 +90,29 @@
};
struct MissedVsync {
- TimePoint vsync;
+ TimePoint vsync = TimePoint::fromNs(0);
Duration fixup = Duration::fromNs(0);
};
class VsyncTimeline {
public:
- VsyncTimeline(Period idealPeriod, std::optional<Fps> renderRateOpt);
+ VsyncTimeline(TimePoint knownVsync, Period idealPeriod, std::optional<Fps> renderRateOpt);
std::optional<TimePoint> nextAnticipatedVSyncTimeFrom(
- Model model, Period minFramePeriod, nsecs_t vsyncTime, MissedVsync lastMissedVsync,
- std::optional<nsecs_t> lastVsyncOpt = {});
+ Model model, std::optional<Period> minFramePeriodOpt, nsecs_t vsyncTime,
+ MissedVsync lastMissedVsync, std::optional<nsecs_t> lastVsyncOpt = {});
void freeze(TimePoint lastVsync);
std::optional<TimePoint> validUntil() const { return mValidUntil; }
bool isVSyncInPhase(Model, nsecs_t vsync, Fps frameRate);
void shiftVsyncSequence(Duration phase);
+ void setRenderRate(std::optional<Fps> renderRateOpt) { mRenderRateOpt = renderRateOpt; }
private:
nsecs_t snapToVsyncAlignedWithRenderRate(Model model, nsecs_t vsync);
VsyncSequence getVsyncSequenceLocked(Model, nsecs_t vsync);
+ std::optional<VsyncSequence> makeVsyncSequence(TimePoint knownVsync);
const Period mIdealPeriod = Duration::fromNs(0);
- const std::optional<Fps> mRenderRateOpt;
+ std::optional<Fps> mRenderRateOpt;
std::optional<TimePoint> mValidUntil;
std::optional<VsyncSequence> mLastVsyncSequence;
};
@@ -143,6 +152,7 @@
std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex);
ftl::NonNull<DisplayModePtr> mDisplayModePtr GUARDED_BY(mMutex);
+ int mNumVsyncsForFrame GUARDED_BY(mMutex);
std::deque<TimePoint> mPastExpectedPresentTimes GUARDED_BY(mMutex);
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 1e55a87..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;
@@ -102,8 +107,10 @@
* when a display is running at 120Hz but the render frame rate is 60Hz.
*
* \param [in] Fps The render rate the tracker should operate at.
+ * \param [in] applyImmediately Whether to apply the new render rate immediately regardless of
+ * already committed vsyncs.
*/
- virtual void setRenderRate(Fps) = 0;
+ virtual void setRenderRate(Fps, bool applyImmediately) = 0;
virtual void onFrameBegin(TimePoint expectedPresentTime,
TimePoint lastConfirmedPresentTime) = 0;
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
index 59a6df2..f2be316 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h
@@ -36,8 +36,11 @@
};
inline std::string to_string(const FrameRateMode& mode) {
- return to_string(mode.fps) + " (" + to_string(mode.modePtr->getPeakFps()) + "(" +
- to_string(mode.modePtr->getVsyncRate()) + "))";
+ return base::StringPrintf("{fps=%s, modePtr={id=%d, vsyncRate=%s, peakRefreshRate=%s}}",
+ to_string(mode.fps).c_str(),
+ ftl::to_underlying(mode.modePtr->getId()),
+ to_string(mode.modePtr->getVsyncRate()).c_str(),
+ to_string(mode.modePtr->getPeakFps()).c_str());
}
} // namespace android::scheduler
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/ScreenCaptureOutput.cpp b/services/surfaceflinger/ScreenCaptureOutput.cpp
index dd03366..8bb72b8 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.cpp
+++ b/services/surfaceflinger/ScreenCaptureOutput.cpp
@@ -30,7 +30,7 @@
ScreenCaptureOutput, compositionengine::CompositionEngine, const RenderArea&,
const compositionengine::Output::ColorProfile&,
bool>(args.compositionEngine, args.renderArea, args.colorProfile, args.regionSampling,
- args.dimInGammaSpaceForEnhancedScreenshots);
+ args.dimInGammaSpaceForEnhancedScreenshots, args.enableLocalTonemapping);
output->editState().isSecure = args.renderArea.isSecure();
output->editState().isProtected = args.isProtected;
output->setCompositionEnabled(true);
@@ -63,11 +63,13 @@
ScreenCaptureOutput::ScreenCaptureOutput(
const RenderArea& renderArea, const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots)
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
+ bool enableLocalTonemapping)
: mRenderArea(renderArea),
mColorProfile(colorProfile),
mRegionSampling(regionSampling),
- mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots) {}
+ mDimInGammaSpaceForEnhancedScreenshots(dimInGammaSpaceForEnhancedScreenshots),
+ mEnableLocalTonemapping(enableLocalTonemapping) {}
void ScreenCaptureOutput::updateColorProfile(const compositionengine::CompositionRefreshArgs&) {
auto& outputState = editState();
@@ -88,6 +90,11 @@
aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF;
}
+ if (mEnableLocalTonemapping) {
+ clientCompositionDisplay.tonemapStrategy =
+ renderengine::DisplaySettings::TonemapStrategy::Local;
+ }
+
return clientCompositionDisplay;
}
diff --git a/services/surfaceflinger/ScreenCaptureOutput.h b/services/surfaceflinger/ScreenCaptureOutput.h
index 069f458..c233ead 100644
--- a/services/surfaceflinger/ScreenCaptureOutput.h
+++ b/services/surfaceflinger/ScreenCaptureOutput.h
@@ -39,6 +39,7 @@
bool treat170mAsSrgb;
bool dimInGammaSpaceForEnhancedScreenshots;
bool isProtected = false;
+ bool enableLocalTonemapping = false;
};
// ScreenCaptureOutput is used to compose a set of layers into a preallocated buffer.
@@ -49,7 +50,8 @@
public:
ScreenCaptureOutput(const RenderArea& renderArea,
const compositionengine::Output::ColorProfile& colorProfile,
- bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots);
+ bool regionSampling, bool dimInGammaSpaceForEnhancedScreenshots,
+ bool enableLocalTonemapping);
void updateColorProfile(const compositionengine::CompositionRefreshArgs&) override;
@@ -67,6 +69,7 @@
const compositionengine::Output::ColorProfile& mColorProfile;
const bool mRegionSampling;
const bool mDimInGammaSpaceForEnhancedScreenshots;
+ const bool mEnableLocalTonemapping;
};
std::shared_ptr<ScreenCaptureOutput> createScreenCaptureOutput(ScreenCaptureOutputArgs);
diff --git a/services/surfaceflinger/StartPropertySetThread.cpp b/services/surfaceflinger/StartPropertySetThread.cpp
deleted file mode 100644
index f42cd53..0000000
--- a/services/surfaceflinger/StartPropertySetThread.cpp
+++ /dev/null
@@ -1,41 +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 <cutils/properties.h>
-#include "StartPropertySetThread.h"
-
-namespace android {
-
-StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
- Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
-
-status_t StartPropertySetThread::Start() {
- return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);
-}
-
-bool StartPropertySetThread::threadLoop() {
- // Set property service.sf.present_timestamp, consumer need check its readiness
- property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
- // Clear BootAnimation exit flag
- property_set("service.bootanim.exit", "0");
- property_set("service.bootanim.progress", "0");
- // Start BootAnimation if not started
- property_set("ctl.start", "bootanim");
- // Exit immediately
- return false;
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/StartPropertySetThread.h b/services/surfaceflinger/StartPropertySetThread.h
deleted file mode 100644
index bbdcde2..0000000
--- a/services/surfaceflinger/StartPropertySetThread.h
+++ /dev/null
@@ -1,46 +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.
- */
-
-#ifndef ANDROID_STARTBOOTANIMTHREAD_H
-#define ANDROID_STARTBOOTANIMTHREAD_H
-
-#include <stddef.h>
-
-#include <utils/Mutex.h>
-#include <utils/Thread.h>
-
-namespace android {
-
-class StartPropertySetThread : public Thread {
-// Boot animation is triggered via calls to "property_set()" which can block
-// if init's executing slow operation such as 'mount_all --late' (currently
-// happening 1/10th with fsck) concurrently. Running in a separate thread
-// allows to pursue the SurfaceFlinger's init process without blocking.
-// see b/34499826.
-// Any property_set() will block during init stage so need to be offloaded
-// to this thread. see b/63844978.
-public:
- explicit StartPropertySetThread(bool timestampPropertyValue);
- status_t Start();
-private:
- virtual bool threadLoop();
- static constexpr const char* kTimestampProperty = "service.sf.present_timestamp";
- const bool mTimestampPropertyValue;
-};
-
-}
-
-#endif // ANDROID_STARTBOOTANIMTHREAD_H
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0f8e3bf..fcead9f 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -40,6 +40,8 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <com_android_graphics_surfaceflinger_flags.h>
+#include <common/FlagManager.h>
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
@@ -55,6 +57,7 @@
#include <configstore/Utils.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
+#include <fmt/format.h>
#include <ftl/algorithm.h>
#include <ftl/concat.h>
#include <ftl/fake_guard.h>
@@ -64,7 +67,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>
@@ -148,13 +150,13 @@
#include "MutexUtils.h"
#include "NativeWindowSurface.h"
#include "RegionSamplingThread.h"
+#include "RenderAreaBuilder.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncModulator.h"
#include "ScreenCaptureOutput.h"
-#include "StartPropertySetThread.h"
#include "SurfaceFlingerProperties.h"
#include "TimeStats/TimeStats.h"
#include "TunnelModeEnabledReporter.h"
@@ -169,10 +171,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;
@@ -233,7 +231,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),
@@ -247,7 +245,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),
@@ -429,7 +427,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());
}
@@ -536,8 +535,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 =
@@ -567,20 +564,25 @@
initializeDisplays();
}));
- startBootAnim();
+ mInitBootPropsFuture.callOnce([this] {
+ return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this);
+ });
+
+ mInitBootPropsFuture.wait();
}
void SurfaceFlinger::run() {
mScheduler->run();
}
-sp<IBinder> SurfaceFlinger::createDisplay(const String8& displayName, bool secure,
- float requestedRefreshRate) {
- // onTransact already checks for some permissions, but adding an additional check here.
- // This is to ensure that only system and graphics can request to create a secure
+sp<IBinder> SurfaceFlinger::createVirtualDisplay(const std::string& displayName, bool isSecure,
+ const std::string& uniqueId,
+ float requestedRefreshRate) {
+ // SurfaceComposerAIDL checks for some permissions, but adding an additional check here.
+ // This is to ensure that only root, system, and graphics can request to create a secure
// display. Secure displays can show secure content so we add an additional restriction on it.
- const int uid = IPCThreadState::self()->getCallingUid();
- if (secure && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (isSecure && uid != AID_ROOT && uid != AID_GRAPHICS && uid != AID_SYSTEM) {
ALOGE("Only privileged processes can create a secure display");
return nullptr;
}
@@ -604,32 +606,34 @@
Mutex::Autolock _l(mStateLock);
// Display ID is assigned when virtual display is allocated by HWC.
DisplayDeviceState state;
- state.isSecure = secure;
+ state.isSecure = isSecure;
// Set display as protected when marked as secure to ensure no behavior change
// TODO (b/314820005): separate as a different arg when creating the display.
- state.isProtected = secure;
+ state.isProtected = isSecure;
state.displayName = displayName;
+ state.uniqueId = uniqueId;
state.requestedRefreshRate = Fps::fromValue(requestedRefreshRate);
mCurrentState.displays.add(token, state);
return token;
}
-void SurfaceFlinger::destroyDisplay(const sp<IBinder>& displayToken) {
+status_t SurfaceFlinger::destroyVirtualDisplay(const sp<IBinder>& displayToken) {
Mutex::Autolock lock(mStateLock);
const ssize_t index = mCurrentState.displays.indexOfKey(displayToken);
if (index < 0) {
ALOGE("%s: Invalid display token %p", __func__, displayToken.get());
- return;
+ return NAME_NOT_FOUND;
}
const DisplayDeviceState& state = mCurrentState.displays.valueAt(index);
if (state.physical) {
ALOGE("%s: Invalid operation on physical display", __func__);
- return;
+ return INVALID_OPERATION;
}
mCurrentState.displays.removeItemsAt(index);
setTransactionFlags(eDisplayTransactionNeeded);
+ return NO_ERROR;
}
void SurfaceFlinger::enableHalVirtualDisplays(bool enable) {
@@ -723,13 +727,10 @@
}
mBootFinished = true;
FlagManager::getMutableInstance().markBootCompleted();
- if (mStartPropertySetThread->join() != NO_ERROR) {
- ALOGE("Join StartPropertySetThread failed!");
- }
- if (mRenderEnginePrimeCacheFuture.valid()) {
- mRenderEnginePrimeCacheFuture.get();
- }
+ mInitBootPropsFuture.wait();
+ mRenderEnginePrimeCacheFuture.wait();
+
const nsecs_t now = systemTime();
const nsecs_t duration = now - mBootTime;
ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
@@ -797,6 +798,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);
@@ -811,14 +814,51 @@
.setGraphicsApi(renderengine::RenderEngine::GraphicsApi::VK);
} else {
const auto kVulkan = renderengine::RenderEngine::GraphicsApi::VK;
- const bool useVulkan = FlagManager::getInstance().vulkan_renderengine() &&
+// TODO: b/341728634 - Clean up conditional compilation.
+// Note: this guard in particular must check e.g.
+// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE directly (instead of calling e.g.
+// COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS(GRAPHITE_RENDERENGINE)) because that macro is undefined
+// in the libsurfaceflingerflags_test variant of com_android_graphics_surfaceflinger_flags.h, which
+// is used by layertracegenerator (which also needs SurfaceFlinger.cpp). :)
+#if COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_GRAPHITE_RENDERENGINE || \
+ COM_ANDROID_GRAPHICS_SURFACEFLINGER_FLAGS_FORCE_COMPILE_GRAPHITE_RENDERENGINE
+ const bool useGraphite = FlagManager::getInstance().graphite_renderengine() &&
renderengine::RenderEngine::canSupport(kVulkan);
+#else
+ const bool useGraphite = false;
+ if (FlagManager::getInstance().graphite_renderengine()) {
+ ALOGE("RenderEngine's Graphite Skia backend was requested with the "
+ "debug.renderengine.graphite system property, but it is not compiled in this "
+ "build! Falling back to Ganesh backend selection logic.");
+ }
+#endif
+ 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);
}
}
-// Do not call property_set on main thread which will be blocked by init
-// Use StartPropertySetThread instead.
+/**
+ * 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. "
@@ -834,7 +874,7 @@
.setImageCacheSize(maxFrameBufferAcquiredBuffers)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
+ .setBlurAlgorithm(chooseBlurAlgorithm(mSupportsBlur))
.setContextPriority(
useContextPriority
? renderengine::RenderEngine::ContextPriority::REALTIME
@@ -851,10 +891,17 @@
}
mCompositionEngine->setTimeStats(mTimeStats);
+
mCompositionEngine->setHwComposer(getFactory().createHWComposer(mHwcServiceName));
- mCompositionEngine->getHwComposer().setCallback(*this);
+ auto& composer = mCompositionEngine->getHwComposer();
+ composer.setCallback(*this);
+ mDisplayModeController.setHwComposer(&composer);
+
ClientCache::getInstance().setRenderEngine(&getRenderEngine());
+ mHasReliablePresentFences =
+ !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
+
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
if (base::GetBoolProperty("debug.sf.enable_hwc_vds"s, false)) {
@@ -888,6 +935,20 @@
// initializing the Scheduler after configureLocked, once decoupled from DisplayDevice.
initScheduler(display);
+ // Start listening after creating the Scheduler, since the listener calls into it.
+ mDisplayModeController.setActiveModeListener(
+ display::DisplayModeController::ActiveModeListener::make(
+ [this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderRate) {
+ // This callback cannot lock mStateLock, as some callers already lock it.
+ // Instead, switch context to the main thread.
+ static_cast<void>(mScheduler->schedule([=,
+ this]() FTL_FAKE_GUARD(mStateLock) {
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ display->updateRefreshRateOverlayRate(vsyncRate, renderRate);
+ }
+ }));
+ }));
+
mLayerTracing.setTakeLayersSnapshotProtoFunction([&](uint32_t traceFlags) {
auto snapshot = perfetto::protos::LayersSnapshotProto{};
mScheduler
@@ -917,29 +978,65 @@
ALOGW("Can't set SCHED_OTHER for primeCache");
}
- bool shouldPrimeUltraHDR =
- base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
- mRenderEnginePrimeCacheFuture = getRenderEngine().primeCache(shouldPrimeUltraHDR);
+ mRenderEnginePrimeCacheFuture.callOnce([this] {
+ renderengine::PrimeCacheConfig config;
+ config.cacheHolePunchLayer =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.hole_punch"s, true);
+ config.cacheSolidLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.solid_layers"s, true);
+ config.cacheSolidDimmedLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.solid_dimmed_layers"s, true);
+ config.cacheImageLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.image_layers"s, true);
+ config.cacheImageDimmedLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.image_dimmed_layers"s, true);
+ config.cacheClippedLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.clipped_layers"s, true);
+ config.cacheShadowLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.shadow_layers"s, true);
+ config.cachePIPImageLayers =
+ base::GetBoolProperty("debug.sf.prime_shader_cache.pip_image_layers"s, true);
+ config.cacheTransparentImageDimmedLayers = base::
+ GetBoolProperty("debug.sf.prime_shader_cache.transparent_image_dimmed_layers"s,
+ true);
+ config.cacheClippedDimmedImageLayers = base::
+ GetBoolProperty("debug.sf.prime_shader_cache.clipped_dimmed_image_layers"s,
+ true);
+ // ro.surface_flinger.prime_chader_cache.ultrahdr exists as a previous ro property
+ // which we maintain for backwards compatibility.
+ config.cacheUltraHDR =
+ base::GetBoolProperty("ro.surface_flinger.prime_shader_cache.ultrahdr"s, false);
+ return getRenderEngine().primeCache(config);
+ });
if (setSchedFifo(true) != NO_ERROR) {
ALOGW("Can't set SCHED_FIFO after primeCache");
}
}
- // Inform native graphics APIs whether the present timestamp is supported:
-
- const bool presentFenceReliable =
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
- mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
-
- if (mStartPropertySetThread->Start() != NO_ERROR) {
- ALOGE("Run StartPropertySetThread failed!");
- }
+ // Avoid blocking the main thread on `init` to set properties.
+ mInitBootPropsFuture.callOnce([this] {
+ return std::async(std::launch::async, &SurfaceFlinger::initBootProperties, this);
+ });
initTransactionTraceWriter();
ALOGV("Done initializing");
}
+// During boot, offload `initBootProperties` to another thread. `property_set` depends on
+// `property_service`, which may be delayed by slow operations like `mount_all --late` in
+// the `init` process. See b/34499826 and b/63844978.
+void SurfaceFlinger::initBootProperties() {
+ property_set("service.sf.present_timestamp", mHasReliablePresentFences ? "1" : "0");
+
+ if (base::GetBoolProperty("debug.sf.boot_animation"s, true)) {
+ // Reset and (if needed) start BootAnimation.
+ property_set("service.bootanim.exit", "0");
+ property_set("service.bootanim.progress", "0");
+ property_set("ctl.start", "bootanim");
+ }
+}
+
void SurfaceFlinger::initTransactionTraceWriter() {
if (!mTransactionTracing) {
return;
@@ -951,8 +1048,9 @@
ALOGD("TransactionTraceWriter: file=%s already exists", filename.c_str());
return;
}
- mTransactionTracing->flush();
+ ALOGD("TransactionTraceWriter: writing file=%s", filename.c_str());
mTransactionTracing->writeToFile(filename);
+ mTransactionTracing->flush();
};
if (std::this_thread::get_id() == mMainThreadId) {
writeFn();
@@ -979,18 +1077,6 @@
static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0));
}
-void SurfaceFlinger::startBootAnim() {
- // Start boot animation service by setting a property mailbox
- // if property setting thread is already running, Start() will be just a NOP
- mStartPropertySetThread->Start();
- // Wait until property was set
- if (mStartPropertySetThread->join() != NO_ERROR) {
- ALOGE("Join StartPropertySetThread failed!");
- }
-}
-
-// ----------------------------------------------------------------------------
-
status_t SurfaceFlinger::getSupportedFrameTimestamps(
std::vector<FrameEvent>* outSupported) const {
*outSupported = {
@@ -1004,9 +1090,7 @@
FrameEvent::RELEASE,
};
- ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
-
- if (!getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ if (mHasReliablePresentFences) {
outSupported->push_back(FrameEvent::DISPLAY_PRESENT);
}
return NO_ERROR;
@@ -1230,19 +1314,19 @@
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto display = getDisplayDeviceLocked(displayId);
- if (!display) {
- ALOGW("%s: display is no longer valid", __func__);
- return;
- }
-
const bool emitEvent = desiredMode.emitEvent;
- switch (display->setDesiredMode(std::move(desiredMode))) {
- case DisplayDevice::DesiredModeAction::InitiateDisplayModeSwitch:
- // DisplayDevice::setDesiredMode updated the render rate, so inform Scheduler.
- mScheduler->setRenderRate(displayId,
- display->refreshRateSelector().getActiveMode().fps);
+ using DesiredModeAction = display::DisplayModeController::DesiredModeAction;
+
+ switch (mDisplayModeController.setDesiredMode(displayId, std::move(desiredMode))) {
+ case DesiredModeAction::InitiateDisplayModeSwitch: {
+ const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
+ if (!selectorPtr) break;
+
+ const Fps renderRate = selectorPtr->getActiveMode().fps;
+
+ // DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
+ mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
// Schedule a new frame to initiate the display mode switch.
scheduleComposite(FrameHint::kNone);
@@ -1262,8 +1346,9 @@
mScheduler->setModeChangePending(true);
break;
- case DisplayDevice::DesiredModeAction::InitiateRenderRateSwitch:
- mScheduler->setRenderRate(displayId, mode.fps);
+ }
+ case DesiredModeAction::InitiateRenderRateSwitch:
+ mScheduler->setRenderRate(displayId, mode.fps, /*applyImmediately*/ false);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(mode.fps);
@@ -1273,7 +1358,7 @@
dispatchDisplayModeChangeEvent(displayId, mode);
}
break;
- case DisplayDevice::DesiredModeAction::None:
+ case DesiredModeAction::None:
break;
}
}
@@ -1329,11 +1414,12 @@
return future.get();
}
-void SurfaceFlinger::finalizeDisplayModeChange(DisplayDevice& display) {
- const auto displayId = display.getPhysicalId();
+// TODO: b/241285876 - Restore thread safety analysis once mStateLock below is unconditional.
+[[clang::no_thread_safety_analysis]]
+void SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
ATRACE_NAME(ftl::Concat(__func__, ' ', displayId.value).c_str());
- const auto pendingModeOpt = display.getPendingMode();
+ const auto pendingModeOpt = mDisplayModeController.getPendingMode(displayId);
if (!pendingModeOpt) {
// There is no pending mode change. This can happen if the active
// display changed and the mode change happened on a different display.
@@ -1342,8 +1428,12 @@
const auto& activeMode = pendingModeOpt->mode;
- if (display.getActiveMode().modePtr->getResolution() != activeMode.modePtr->getResolution()) {
- auto& state = mCurrentState.displays.editValueFor(display.getDisplayToken());
+ if (const auto oldResolution =
+ mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
+ oldResolution != activeMode.modePtr->getResolution()) {
+ ConditionalLock lock(mStateLock, !FlagManager::getInstance().connected_display());
+
+ auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
// We need to generate new sequenceId in order to recreate the display (and this
// way the framebuffer).
state.sequenceId = DisplayDeviceState{}.sequenceId;
@@ -1354,8 +1444,8 @@
return;
}
- display.finalizeModeChange(activeMode.modePtr->getId(), activeMode.modePtr->getVsyncRate(),
- activeMode.fps);
+ mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
+ activeMode.modePtr->getVsyncRate(), activeMode.fps);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(activeMode.fps);
@@ -1366,25 +1456,24 @@
}
}
-void SurfaceFlinger::dropModeRequest(const sp<DisplayDevice>& display) {
- display->clearDesiredMode();
- if (display->getPhysicalId() == mActiveDisplayId) {
+void SurfaceFlinger::dropModeRequest(PhysicalDisplayId displayId) {
+ mDisplayModeController.clearDesiredMode(displayId);
+ if (displayId == mActiveDisplayId) {
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
}
}
-void SurfaceFlinger::applyActiveMode(const sp<DisplayDevice>& display) {
- const auto activeModeOpt = display->getDesiredMode();
+void SurfaceFlinger::applyActiveMode(PhysicalDisplayId displayId) {
+ const auto activeModeOpt = mDisplayModeController.getDesiredMode(displayId);
auto activeModePtr = activeModeOpt->mode.modePtr;
- const auto displayId = activeModePtr->getPhysicalDisplayId();
const auto renderFps = activeModeOpt->mode.fps;
- dropModeRequest(display);
+ dropModeRequest(displayId);
constexpr bool kAllowToEnable = true;
mScheduler->resyncToHardwareVsync(displayId, kAllowToEnable, std::move(activeModePtr).take());
- mScheduler->setRenderRate(displayId, renderFps);
+ mScheduler->setRenderRate(displayId, renderFps, /*applyImmediately*/ true);
if (displayId == mActiveDisplayId) {
mScheduler->updatePhaseConfiguration(renderFps);
@@ -1396,20 +1485,12 @@
std::optional<PhysicalDisplayId> displayToUpdateImmediately;
- for (const auto& [id, physical] : mPhysicalDisplays) {
- const auto display = getDisplayDeviceLocked(id);
- if (!display) continue;
-
- auto desiredModeOpt = display->getDesiredMode();
+ for (const auto& [displayId, physical] : mPhysicalDisplays) {
+ auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
if (!desiredModeOpt) {
continue;
}
- if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
- dropModeRequest(display);
- continue;
- }
-
const auto desiredModeId = desiredModeOpt->mode.modePtr->getId();
const auto displayModePtrOpt = physical.snapshot().displayModes().get(desiredModeId);
@@ -1422,19 +1503,21 @@
ALOGV("%s changing active mode to %d(%s) for display %s", __func__,
ftl::to_underlying(desiredModeId),
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
- to_string(display->getId()).c_str());
+ to_string(displayId).c_str());
if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
- display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
+ mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ applyActiveMode(displayId);
continue;
}
+ const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
+
// Desired active mode was set, it is different than the mode currently in use, however
// allowed modes might have changed by the time we process the refresh.
// Make sure the desired mode is still allowed
- if (!display->refreshRateSelector().isModeAllowed(desiredModeOpt->mode)) {
- dropModeRequest(display);
+ if (!selectorPtr->isModeAllowed(desiredModeOpt->mode)) {
+ dropModeRequest(displayId);
continue;
}
@@ -1444,11 +1527,12 @@
constraints.seamlessRequired = false;
hal::VsyncPeriodChangeTimeline outTimeline;
- if (!display->initiateModeChange(std::move(*desiredModeOpt), constraints, outTimeline)) {
+ if (!mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
+ constraints, outTimeline)) {
continue;
}
- display->refreshRateSelector().onModeChangeInitiated();
+ selectorPtr->onModeChangeInitiated();
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
if (outTimeline.refreshRequired) {
@@ -1457,17 +1541,18 @@
// TODO(b/255635711): Remove `displayToUpdateImmediately` to `finalizeDisplayModeChange`
// for all displays. This was only needed when the loop iterated over `mDisplays` rather
// than `mPhysicalDisplays`.
- displayToUpdateImmediately = display->getPhysicalId();
+ displayToUpdateImmediately = displayId;
}
}
if (displayToUpdateImmediately) {
- const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
- finalizeDisplayModeChange(*display);
+ const auto displayId = *displayToUpdateImmediately;
+ finalizeDisplayModeChange(displayId);
- const auto desiredModeOpt = display->getDesiredMode();
- if (desiredModeOpt && display->getActiveMode() == desiredModeOpt->mode) {
- applyActiveMode(display);
+ const auto desiredModeOpt = mDisplayModeController.getDesiredMode(displayId);
+ if (desiredModeOpt &&
+ mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
+ applyActiveMode(displayId);
}
}
}
@@ -1840,19 +1925,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,
@@ -2085,10 +2157,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);
}
@@ -2212,19 +2291,22 @@
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 refreshRate = Fps::fromPeriodNsecs(
+ getHwComposer().getComposer()->isVrrSupported() ? data.refreshPeriodNanos
+ : data.vsyncPeriodNanos);
+ ATRACE_FORMAT("%s refresh rate = %d", whence, refreshRate.getIntValue());
+
+ const auto renderRate = mDisplayModeController.getActiveMode(*displayIdOpt).fps;
+ constexpr bool kSetByHwc = true;
+ display->updateRefreshRateOverlayRate(refreshRate, renderRate, kSetByHwc);
+ }
+ }
+ }));
}
void SurfaceFlinger::configure() {
@@ -2395,6 +2477,8 @@
frontend::LayerSnapshotBuilder::Args
args{.root = mLayerHierarchyBuilder.getHierarchy(),
.layerLifecycleManager = mLayerLifecycleManager,
+ .includeMetadata = mCompositionEngine->getFeatureFlags().test(
+ compositionengine::Feature::kSnapshotLayerMetadata),
.displays = mFrontEndDisplayInfos,
.displayChanges = mFrontEndDisplayInfosChanged,
.globalShadowSettings = mDrawingState.globalShadowSettings,
@@ -2422,89 +2506,92 @@
mUpdateAttachedChoreographer = true;
}
outTransactionsAreEmpty = mLayerLifecycleManager.getGlobalChanges().get() == 0;
- mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
+ if (FlagManager::getInstance().vrr_bugfix_24q4()) {
+ mustComposite |= mLayerLifecycleManager.getGlobalChanges().any(
+ frontend::RequestedLayerState::kMustComposite);
+ } else {
+ mustComposite |= mLayerLifecycleManager.getGlobalChanges().get() != 0;
+ }
bool newDataLatched = false;
- if (!mLegacyFrontEndEnabled) {
- ATRACE_NAME("DisplayCallbackAndStatsUpdates");
- mustComposite |= applyTransactionsLocked(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();
}
@@ -2524,32 +2611,21 @@
// If a mode set is pending and the fence hasn't fired yet, wait for the next commit.
if (std::any_of(frameTargets.begin(), frameTargets.end(),
- [this](const auto& pair) FTL_FAKE_GUARD(mStateLock)
- FTL_FAKE_GUARD(kMainThreadContext) {
- if (!pair.second->isFramePending()) return false;
-
- if (const auto display = getDisplayDeviceLocked(pair.first)) {
- return display->isModeSetPending();
- }
-
- return false;
- })) {
+ [this](const auto& pair) FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto [displayId, target] = pair;
+ return target->isFramePending() &&
+ mDisplayModeController.isModeSetPending(displayId);
+ })) {
mScheduler->scheduleFrame();
return false;
}
{
- Mutex::Autolock lock(mStateLock);
+ ConditionalLock lock(mStateLock, FlagManager::getInstance().connected_display());
- for (const auto [id, target] : frameTargets) {
- // TODO(b/241285876): This is `nullptr` when the DisplayDevice is about to be removed in
- // this commit, since the PhysicalDisplay has already been removed. Rather than checking
- // for `nullptr` below, change Scheduler::onFrameSignal to filter out the FrameTarget of
- // the removed display.
- const auto display = getDisplayDeviceLocked(id);
-
- if (display && display->isModeSetPending()) {
- finalizeDisplayModeChange(*display);
+ for (const auto [displayId, _] : frameTargets) {
+ if (mDisplayModeController.isModeSetPending(displayId)) {
+ finalizeDisplayModeChange(displayId);
}
}
}
@@ -2585,8 +2661,8 @@
mPowerAdvisor->setFrameDelay(frameDelay);
mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration);
- const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get();
- const Period idealVsyncPeriod = display->getActiveMode().fps.getPeriod();
+ const Period idealVsyncPeriod =
+ mDisplayModeController.getActiveMode(pacesetterId).fps.getPeriod();
mPowerAdvisor->updateTargetWorkDuration(idealVsyncPeriod);
}
@@ -2606,18 +2682,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);
}
@@ -2646,6 +2725,14 @@
? &mLayerHierarchyBuilder.getHierarchy()
: nullptr,
updateAttachedChoreographer);
+
+ if (FlagManager::getInstance().connected_display()) {
+ initiateDisplayModeChanges();
+ }
+ }
+
+ if (!FlagManager::getInstance().connected_display()) {
+ ftl::FakeGuard guard(mStateLock);
initiateDisplayModeChanges();
}
@@ -2711,34 +2798,22 @@
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);
- refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
- for (auto layer : mLayersWithQueuedFrames) {
- if (auto layerFE = layer->getCompositionEngineLayerFE())
- refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+ if (!FlagManager::getInstance().ce_fence_promise()) {
+ refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+ for (auto& layer : mLayersWithQueuedFrames) {
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE())
+ refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+ }
}
refreshArgs.outputColorSetting = mDisplayColorSetting;
refreshArgs.forceOutputColorMode = mForceColorMode;
refreshArgs.updatingOutputGeometryThisFrame = mVisibleRegionsDirty;
- refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) || mVisibleRegionsDirty;
+ refreshArgs.updatingGeometryThisFrame = mGeometryDirty.exchange(false) ||
+ mVisibleRegionsDirty || mDrawingState.colorMatrixChanged;
refreshArgs.internalDisplayRotationFlags = getActiveDisplayRotationFlags();
if (CC_UNLIKELY(mDrawingState.colorMatrixChanged)) {
@@ -2791,19 +2866,57 @@
}
}
- mCompositionEngine->present(refreshArgs);
- moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+ refreshArgs.refreshStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ for (auto& [layer, layerFE] : layers) {
+ layer->onPreComposition(refreshArgs.refreshStartTime);
+ }
- for (auto [layer, layerFE] : layers) {
- CompositionResult compositionResult{layerFE->stealCompositionResult()};
- layer->onPreComposition(compositionResult.refreshStartTime);
- for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
- Layer* clonedFrom = layer->getClonedFrom().get();
- auto owningLayer = clonedFrom ? clonedFrom : layer;
- owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE,
+ layerFE->mSnapshot->outputFilter.layerStack);
}
- if (compositionResult.lastClientCompositionFence) {
- layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+
+ refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
+ for (auto& layer : mLayersWithQueuedFrames) {
+ if (const auto& layerFE = layer->getCompositionEngineLayerFE()) {
+ refreshArgs.layersWithQueuedFrames.push_back(layerFE);
+ // Some layers are not displayed and do not yet have a future release fence
+ if (layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::UNINITIALIZED ||
+ layerFE->getReleaseFencePromiseStatus() ==
+ LayerFE::ReleaseFencePromiseStatus::FULFILLED) {
+ // layerStack is invalid because layer is not on a display
+ attachReleaseFenceFutureToLayer(layer.get(), layerFE.get(),
+ ui::INVALID_LAYER_STACK);
+ }
+ }
+ }
+
+ mCompositionEngine->present(refreshArgs);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+
+ for (auto& [layer, layerFE] : layers) {
+ CompositionResult compositionResult{layerFE->stealCompositionResult()};
+ if (compositionResult.lastClientCompositionFence) {
+ layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+ }
+ }
+
+ } else {
+ mCompositionEngine->present(refreshArgs);
+ moveSnapshotsFromCompositionArgs(refreshArgs, layers);
+
+ for (auto [layer, layerFE] : layers) {
+ CompositionResult compositionResult{layerFE->stealCompositionResult()};
+ for (auto& [releaseFence, layerStack] : compositionResult.releaseFences) {
+ Layer* clonedFrom = layer->getClonedFrom().get();
+ auto owningLayer = clonedFrom ? clonedFrom : layer;
+ owningLayer->onLayerDisplayed(std::move(releaseFence), layerStack);
+ }
+ if (compositionResult.lastClientCompositionFence) {
+ layer->setWasClientComposed(compositionResult.lastClientCompositionFence);
+ }
}
}
@@ -3038,10 +3151,9 @@
// but that should be okay since CompositorTiming has snapping logic.
const TimePoint compositeTime =
TimePoint::fromNs(mCompositionEngine->getLastFrameRefreshTimestamp());
- const Duration presentLatency =
- getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)
- ? Duration::zero()
- : mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime);
+ const Duration presentLatency = mHasReliablePresentFences
+ ? mPresentLatencyTracker.trackPendingFrame(compositeTime, pacesetterPresentFenceTime)
+ : Duration::zero();
const auto schedule = mScheduler->getVsyncSchedule();
const TimePoint vsyncDeadline = schedule->vsyncDeadlineAfter(presentTime);
@@ -3070,8 +3182,13 @@
auto optDisplay = layerStackToDisplay.get(layerStack);
if (optDisplay && !optDisplay->get()->isVirtual()) {
auto fence = getHwComposer().getPresentFence(optDisplay->get()->getPhysicalId());
- layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
- ui::INVALID_LAYER_STACK);
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ layer->prepareReleaseCallbacks(ftl::yield<FenceResult>(fence),
+ ui::INVALID_LAYER_STACK);
+ } else {
+ layer->onLayerDisplayed(ftl::yield<FenceResult>(fence).share(),
+ ui::INVALID_LAYER_STACK);
+ }
}
}
layer->releasePendingBuffer(presentTime.ns());
@@ -3145,7 +3262,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",
@@ -3204,7 +3322,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;
}
@@ -3461,12 +3579,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());
}
}
}
@@ -3474,36 +3625,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();
@@ -3522,7 +3657,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();
@@ -3542,7 +3678,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,
@@ -3570,43 +3722,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));
}
}));
}
@@ -3651,7 +3781,8 @@
if (const auto& physical = state.physical) {
const auto& mode = *physical->activeMode;
- display->setActiveMode(mode.getId(), mode.getVsyncRate(), mode.getVsyncRate());
+ mDisplayModeController.setActiveMode(physical->id, mode.getId(), mode.getVsyncRate(),
+ mode.getVsyncRate());
}
display->setLayerFilter(makeLayerFilterForDisplay(display->getId(), state.layerStack));
@@ -3737,7 +3868,8 @@
ftl::FakeGuard guard(kMainThreadContext);
// For hotplug reconnect, renew the registration since display modes have been reloaded.
- mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(),
+ mActiveDisplayId);
}
if (display->isVirtual()) {
@@ -3776,7 +3908,7 @@
if (display->isVirtual()) {
releaseVirtualDisplay(display->getVirtualId());
} else {
- mScheduler->unregisterDisplay(display->getPhysicalId());
+ mScheduler->unregisterDisplay(display->getPhysicalId(), mActiveDisplayId);
}
}
@@ -3824,11 +3956,15 @@
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()) {
- mScheduler->resetPhaseConfiguration(display->getActiveMode().fps);
+ const Fps refreshRate =
+ mDisplayModeController.getActiveMode(display->getPhysicalId()).fps;
+ mScheduler->resetPhaseConfiguration(refreshRate);
}
}
return;
@@ -3966,19 +4102,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());
}
});
@@ -4117,7 +4241,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())
@@ -4177,12 +4301,6 @@
if (!display) continue;
- if (ftl::FakeGuard guard(kMainThreadContext);
- !shouldApplyRefreshRateSelectorPolicy(*display)) {
- ALOGV("%s(%s): Skipped applying policy", __func__, to_string(displayId).c_str());
- continue;
- }
-
if (display->refreshRateSelector().isModeAllowed(request.mode)) {
setDesiredMode(std::move(request));
} else {
@@ -4354,6 +4472,12 @@
scheduleNotifyExpectedPresentHint(displayId);
}
+void SurfaceFlinger::onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) {
+ if (FlagManager::getInstance().commit_not_composited()) {
+ mFrameTimeline->onCommitNotComposited();
+ }
+}
+
void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) {
using namespace scheduler;
@@ -4377,7 +4501,7 @@
features |= Feature::kTracePredictedVsync;
}
if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) &&
- !getHwComposer().hasCapability(Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ mHasReliablePresentFences) {
features |= Feature::kPresentFences;
}
if (display->refreshRateSelector().kernelIdleTimerController()) {
@@ -4396,9 +4520,11 @@
getFactory(), activeRefreshRate, *mTimeStats);
// The pacesetter must be registered before EventThread creation below.
- mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector());
+ mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector(),
+ mActiveDisplayId);
if (FlagManager::getInstance().vrr_config()) {
- mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps);
+ mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps,
+ /*applyImmediately*/ true);
}
const auto configs = mScheduler->getVsyncConfiguration().getCurrentConfigs();
@@ -4821,6 +4947,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(),
@@ -4843,97 +4971,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;
}
@@ -5055,7 +5193,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);
}
@@ -5154,7 +5292,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});
@@ -5189,27 +5333,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);
+ }
}
}
}
@@ -5268,7 +5407,7 @@
}
mFrontEndDisplayInfosChanged = mTransactionFlags & eDisplayTransactionNeeded;
- if (mFrontEndDisplayInfosChanged && !mLegacyFrontEndEnabled) {
+ if (mFrontEndDisplayInfosChanged) {
processDisplayChangesLocked();
mFrontEndDisplayInfos.clear();
for (const auto& [_, display] : mDisplays) {
@@ -5477,11 +5616,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,
@@ -5529,10 +5663,7 @@
layer->setInputInfo(*s.windowInfoHandle->getInfo());
flags |= eTraversalNeeded;
}
- std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
- dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
-
if (const int32_t gameMode = s.metadata.getInt32(gui::METADATA_GAME_MODE, -1);
gameMode != -1) {
// The transaction will be received on the Task layer and needs to be applied to all
@@ -5622,7 +5753,7 @@
if (layer->setHdrMetadata(s.hdrMetadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eTrustedOverlayChanged) {
- if (layer->setTrustedOverlay(s.isTrustedOverlay)) {
+ if (layer->setTrustedOverlay(s.trustedOverlay == gui::TrustedOverlay::ENABLED)) {
flags |= eTraversalNeeded;
}
}
@@ -5674,8 +5805,7 @@
if (what & layer_state_t::eBufferChanged) {
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
} else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) {
@@ -5761,10 +5891,6 @@
if (what & layer_state_t::eProducerDisconnect) {
layer->onDisconnect();
}
- std::optional<nsecs_t> dequeueBufferTimestamp;
- if (what & layer_state_t::eMetadataChanged) {
- dequeueBufferTimestamp = s.metadata.getInt64(gui::METADATA_DEQUEUE_TIME);
- }
std::vector<sp<CallbackHandle>> callbackHandles;
if ((what & layer_state_t::eHasListenerCallbacksChanged) && (!filteredListeners.empty())) {
@@ -5811,8 +5937,7 @@
}
layer->setTransformHint(transformHint);
if (layer->setBuffer(composerState.externalTexture, *s.bufferData, postTime,
- desiredPresentTime, isAutoTimestamp, dequeueBufferTimestamp,
- frameTimelineInfo)) {
+ desiredPresentTime, isAutoTimestamp, frameTimelineInfo)) {
flags |= eTraversalNeeded;
}
mLayersWithQueuedFrames.emplace(layer);
@@ -5870,7 +5995,7 @@
return result;
}
- mirrorLayer->setClonedChild(mirrorFrom->createClone(mirrorLayer->getSequence()));
+ mirrorLayer->setClonedChild(mirrorFrom->createClone());
}
outResult.layerId = mirrorLayer->sequence;
@@ -5916,11 +6041,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;
}
@@ -6001,7 +6121,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);
@@ -6031,9 +6155,7 @@
std::vector<TransactionState> transactions;
transactions.emplace_back(state);
- if (mLegacyFrontEndEnabled) {
- applyTransactions(transactions, VsyncId{0});
- } else {
+ {
Mutex::Autolock lock(mStateLock);
applyAndCommitDisplayTransactionStatesLocked(transactions);
}
@@ -6049,8 +6171,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);
+ }
}
}
}
@@ -6209,6 +6334,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,
@@ -6261,9 +6387,9 @@
{"--frontend"s, mainThreadDumper(&SurfaceFlinger::dumpFrontEnd)},
{"--hdrinfo"s, dumper(&SurfaceFlinger::dumpHdrInfo)},
{"--hwclayers"s, mainThreadDumper(&SurfaceFlinger::dumpHwcLayersMinidump)},
- {"--latency"s, argsDumper(&SurfaceFlinger::dumpStatsLocked)},
- {"--latency-clear"s, argsDumper(&SurfaceFlinger::clearStatsLocked)},
- {"--list"s, dumper(&SurfaceFlinger::listLayersLocked)},
+ {"--latency"s, argsMainThreadDumper(&SurfaceFlinger::dumpStats)},
+ {"--latency-clear"s, argsMainThreadDumper(&SurfaceFlinger::clearStats)},
+ {"--list"s, mainThreadDumper(&SurfaceFlinger::listLayers)},
{"--planner"s, argsDumper(&SurfaceFlinger::dumpPlannerInfo)},
{"--scheduler"s, dumper(&SurfaceFlinger::dumpScheduler)},
{"--timestats"s, protoDumper(&SurfaceFlinger::dumpTimeStats)},
@@ -6296,28 +6422,29 @@
return doDump(fd, DumpArgs(), asProto);
}
-void SurfaceFlinger::listLayersLocked(std::string& result) const {
- mCurrentState.traverseInZOrder(
- [&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getDebugName()); });
+void SurfaceFlinger::listLayers(std::string& result) const {
+ for (const auto& layer : mLayerLifecycleManager.getLayers()) {
+ StringAppendF(&result, "%s\n", layer->getDebugString().c_str());
+ }
}
-void SurfaceFlinger::dumpStatsLocked(const DumpArgs& args, std::string& result) const {
+void SurfaceFlinger::dumpStats(const DumpArgs& args, std::string& result) const {
StringAppendF(&result, "%" PRId64 "\n", getVsyncPeriodFromHWC());
if (args.size() < 2) return;
const auto name = String8(args[1]);
- mCurrentState.traverseInZOrder([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (layer->getName() == name.c_str()) {
layer->dumpFrameStats(result);
}
});
}
-void SurfaceFlinger::clearStatsLocked(const DumpArgs& args, std::string&) {
+void SurfaceFlinger::clearStats(const DumpArgs& args, std::string&) {
const bool clearAll = args.size() < 2;
const auto name = clearAll ? String8() : String8(args[1]);
- mCurrentState.traverse([&](Layer* layer) {
+ traverseLegacyLayers([&](Layer* layer) {
if (clearAll || layer->getName() == name.c_str()) {
layer->clearFrameStats();
}
@@ -6474,18 +6601,19 @@
StringAppendF(&result, "DisplayColorSetting: %s\n",
decodeDisplayColorSetting(mDisplayColorSetting).c_str());
- // TODO: print out if wide-color mode is active or not
+ // TODO: print out if wide-color mode is active or not.
for (const auto& [id, display] : mPhysicalDisplays) {
StringAppendF(&result, "Display %s color modes:\n", to_string(id).c_str());
for (const auto mode : display.snapshot().colorModes()) {
- StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(), mode);
+ StringAppendF(&result, " %s (%d)\n", decodeColorMode(mode).c_str(),
+ fmt::underlying(mode));
}
if (const auto display = getDisplayDeviceLocked(id)) {
ui::ColorMode currentMode = display->getCompositionDisplay()->getState().colorMode;
StringAppendF(&result, " Current color mode: %s (%d)\n",
- decodeColorMode(currentMode).c_str(), currentMode);
+ decodeColorMode(currentMode).c_str(), fmt::underlying(currentMode));
}
}
result.append("\n");
@@ -6589,17 +6717,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());
@@ -6657,7 +6774,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) {
@@ -6706,17 +6827,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");
}
}
@@ -6813,24 +6935,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);
@@ -6844,10 +6965,6 @@
}
result.push_back('\n');
- if (mLegacyFrontEndEnabled) {
- dumpHwcLayersMinidumpLockedLegacy(result);
- }
-
{
DumpArgs plannerArgs;
plannerArgs.add(); // first argument is ignored
@@ -6953,8 +7070,8 @@
// Used by apps to hook Choreographer to SurfaceFlinger.
case CREATE_DISPLAY_EVENT_CONNECTION:
case CREATE_CONNECTION:
- case CREATE_DISPLAY:
- case DESTROY_DISPLAY:
+ case CREATE_VIRTUAL_DISPLAY:
+ case DESTROY_VIRTUAL_DISPLAY:
case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
case GET_PHYSICAL_DISPLAY_IDS:
case GET_PHYSICAL_DISPLAY_TOKEN:
@@ -7091,6 +7208,7 @@
Mutex::Autolock _l(mStateLock);
// daltonize
n = data.readInt32();
+ mDaltonizer.setLevel(data.readInt32());
switch (n % 10) {
case 1:
mDaltonizer.setType(ColorBlindnessType::Protanomaly);
@@ -7454,14 +7572,11 @@
auto future = mScheduler->schedule(
[&]() FTL_FAKE_GUARD(mStateLock) FTL_FAKE_GUARD(kMainThreadContext) {
n = data.readInt32();
- mHdrSdrRatioOverlay = n != 0;
- switch (n) {
- case 0:
- case 1:
- enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay);
- break;
- default:
- reply->writeBool(isHdrSdrRatioOverlayEnabled());
+ if (n == 0 || n == 1) {
+ mHdrSdrRatioOverlay = n != 0;
+ enableHdrSdrRatioOverlay(mHdrSdrRatioOverlay);
+ } else {
+ reply->writeBool(isHdrSdrRatioOverlayEnabled());
}
});
future.wait();
@@ -7588,9 +7703,10 @@
if (!display->isRefreshRateOverlayEnabled()) return;
const auto desiredModeIdOpt =
- display->getDesiredMode().transform([](const display::DisplayModeRequest& request) {
- return request.mode.modePtr->getId();
- });
+ mDisplayModeController.getDesiredMode(display->getPhysicalId())
+ .transform([](const display::DisplayModeRequest& request) {
+ return request.mode.modePtr->getId();
+ });
const bool timerExpired = mKernelIdleTimerEnabled && expired;
@@ -7600,14 +7716,29 @@
}));
}
+void SurfaceFlinger::vrrDisplayIdle(bool idle) {
+ // Update the overlay on the main thread to avoid race conditions with
+ // RefreshRateSelector::getActiveMode
+ static_cast<void>(mScheduler->schedule([=, this] {
+ const auto display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked());
+ if (!display) {
+ ALOGW("%s: default display is null", __func__);
+ return;
+ }
+ if (!display->isRefreshRateOverlayEnabled()) return;
+
+ display->onVrrIdle(idle);
+ mScheduler->scheduleFrame();
+ }));
+}
+
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.
@@ -7768,14 +7899,13 @@
namespace {
-ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace, const DisplayDevice* display,
+ui::Dataspace pickBestDataspace(ui::Dataspace requestedDataspace,
+ const compositionengine::impl::OutputCompositionState& state,
bool capturingHdrLayers, bool hintForSeamlessTransition) {
- if (requestedDataspace != ui::Dataspace::UNKNOWN || display == nullptr) {
+ if (requestedDataspace != ui::Dataspace::UNKNOWN) {
return requestedDataspace;
}
- const auto& state = display->getCompositionDisplay()->getState();
-
const auto dataspaceForColorMode = ui::pickDataspaceFor(state.colorMode);
// TODO: Enable once HDR screenshots are ready.
@@ -7806,17 +7936,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;
}
@@ -7829,6 +7961,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;
}
@@ -7845,32 +7978,25 @@
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 getLayerSnapshotsFn =
+ getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
- GetLayerSnapshotsFunction getLayerSnapshots;
- if (mLayerLifecycleManagerEnabled) {
- getLayerSnapshots =
- getLayerSnapshotsForScreenshots(layerStack, args.uid, std::move(excludeLayerIds));
- } else {
- auto traverseLayers = [this, args, excludeLayerIds,
- layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, args.uid, std::move(excludeLayerIds), visitor);
- };
- getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
- }
-
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, reqSize, args.pixelFormat,
- args.allowProtected, args.grayscale, captureListener);
+ ftl::Flags<RenderArea::Options> options;
+ if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (args.hintForSeamlessTransition)
+ options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+ args.sourceCrop, reqSize, args.dataspace,
+ displayWeak, options),
+ getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
}
void SurfaceFlinger::captureDisplay(DisplayId displayId, const CaptureArgs& args,
@@ -7883,6 +8009,7 @@
const auto display = getDisplayDeviceLocked(displayId);
if (!display) {
+ ALOGD("Unable to find display device for captureDisplay");
invokeScreenCaptureError(NAME_NOT_FOUND, captureListener);
return;
}
@@ -7900,27 +8027,14 @@
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,
- /*snapshotFilterFn=*/nullptr);
- } else {
- auto traverseLayers = [this, layerStack](const LayerVector::Visitor& visitor) {
- traverseLayersInLayerStack(layerStack, CaptureArgs::UNSET_UID, {}, visitor);
- };
- getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
- }
+ GetLayerSnapshotsFunction getLayerSnapshotsFn =
+ getLayerSnapshotsForScreenshots(layerStack, CaptureArgs::UNSET_UID,
+ /*snapshotFilterFn=*/nullptr);
if (captureListener == nullptr) {
ALOGE("capture screen must provide a capture listener callback");
@@ -7931,8 +8045,14 @@
constexpr bool kAllowProtected = false;
constexpr bool kGrayscale = false;
- captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, size, args.pixelFormat,
- kAllowProtected, kGrayscale, captureListener);
+ ftl::Flags<RenderArea::Options> options;
+ if (args.hintForSeamlessTransition)
+ options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<DisplayRenderAreaBuilder>,
+ Rect(), size, args.dataspace, displayWeak,
+ options),
+ getLayerSnapshotsFn, size, args.pixelFormat, kAllowProtected, kGrayscale,
+ captureListener);
}
ScreenCaptureResults SurfaceFlinger::captureLayersSync(const LayerCaptureArgs& args) {
@@ -7947,6 +8067,7 @@
status_t validate = validateScreenshotPermissions(args);
if (validate != OK) {
+ ALOGD("Permission denied to captureLayers");
invokeScreenCaptureError(validate, captureListener);
return;
}
@@ -7958,7 +8079,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;
}
@@ -7968,7 +8089,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;
}
@@ -7987,6 +8108,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;
}
@@ -7997,7 +8119,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;
}
@@ -8006,82 +8128,87 @@
// really small crop or frameScale
if (reqSize.width <= 0 || reqSize.height <= 0) {
- ALOGW("Failed to captureLayes: 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;
- if (args.childrenOnly) {
- parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
- : crop.toFloatRect();
- }
-
- getLayerSnapshots = getLayerSnapshotsForScreenshots(parent->sequence, args.uid,
- std::move(excludeLayerIds),
- args.childrenOnly, parentCrop);
- } else {
- auto traverseLayers = [parent, args, excludeLayerIds](const LayerVector::Visitor& visitor) {
- parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
- if (!layer->isVisible()) {
- return;
- } else if (args.childrenOnly && layer == parent.get()) {
- return;
- } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
- return;
- }
-
- auto p = sp<Layer>::fromExisting(layer);
- while (p != nullptr) {
- if (excludeLayerIds.count(p->sequence) != 0) {
- return;
- }
- p = p->getParent();
- }
-
- visitor(layer);
- });
- };
- getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ std::optional<FloatRect> parentCrop = std::nullopt;
+ if (args.childrenOnly) {
+ parentCrop = crop.isEmpty() ? FloatRect(0, 0, reqSize.width, reqSize.height)
+ : crop.toFloatRect();
}
+ GetLayerSnapshotsFunction getLayerSnapshotsFn =
+ getLayerSnapshotsForScreenshots(parent->sequence, args.uid, std::move(excludeLayerIds),
+ args.childrenOnly, parentCrop);
+
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);
+ ftl::Flags<RenderArea::Options> options;
+ if (args.captureSecureLayers) options |= RenderArea::Options::CAPTURE_SECURE_LAYERS;
+ if (args.hintForSeamlessTransition)
+ options |= RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION;
+ captureScreenCommon(RenderAreaBuilderVariant(std::in_place_type<LayerRenderAreaBuilder>, crop,
+ reqSize, dataspace, parent, args.childrenOnly,
+ options),
+ getLayerSnapshotsFn, reqSize, args.pixelFormat, args.allowProtected,
+ args.grayscale, captureListener);
}
-void SurfaceFlinger::captureScreenCommon(RenderAreaFuture renderAreaFuture,
- GetLayerSnapshotsFunction getLayerSnapshots,
+// Creates a Future release fence for a layer and keeps track of it in a list to
+// release the buffer when the Future is complete. Calls from composittion
+// involve needing to refresh the composition start time for stats.
+void SurfaceFlinger::attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE,
+ ui::LayerStack layerStack) {
+ ftl::Future<FenceResult> futureFence = layerFE->createReleaseFenceFuture();
+ Layer* clonedFrom = layer->getClonedFrom().get();
+ auto owningLayer = clonedFrom ? clonedFrom : layer;
+ owningLayer->prepareReleaseCallbacks(std::move(futureFence), layerStack);
+}
+
+// Loop over all visible layers to see whether there's any protected layer. A protected layer is
+// typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
+// A protected layer has no implication on whether it's secure, which is explicitly set by
+// application to avoid being screenshot or drawn via unsecure display.
+bool SurfaceFlinger::layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const {
+ bool protectedLayerFound = false;
+ for (auto& layerFE : layers) {
+ protectedLayerFound |=
+ (layerFE->mSnapshot->isVisible && layerFE->mSnapshot->hasProtectedContent);
+ if (protectedLayerFound) {
+ break;
+ }
+ }
+ return protectedLayerFound;
+}
+
+// Getting layer snapshots and display should take place on main thread.
+// Accessing display requires mStateLock, and contention for this lock
+// is reduced when grabbed from the main thread, thus also reducing
+// risk of deadlocks.
+std::optional<SurfaceFlinger::OutputCompositionState>
+SurfaceFlinger::getDisplayAndLayerSnapshotsFromMainThread(
+ RenderAreaBuilderVariant& renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
+ std::vector<sp<LayerFE>>& layerFEs) {
+ return mScheduler
+ ->schedule([=, this, &renderAreaBuilder, &layerFEs]() REQUIRES(kMainThreadContext) {
+ auto layers = getLayerSnapshotsFn();
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+ }
+ layerFEs = extractLayerFEs(layers);
+ return getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
+ })
+ .get();
+}
+
+void SurfaceFlinger::captureScreenCommon(RenderAreaBuilderVariant renderAreaBuilder,
+ GetLayerSnapshotsFunction getLayerSnapshotsFn,
ui::Size bufferSize, ui::PixelFormat reqPixelFormat,
bool allowProtected, bool grayscale,
const sp<IScreenCaptureListener>& captureListener) {
@@ -8095,95 +8222,223 @@
return;
}
- // Loop over all visible layers to see whether there's any protected layer. A protected layer is
- // typically a layer with DRM contents, or have the GRALLOC_USAGE_PROTECTED set on the buffer.
- // A protected layer has no implication on whether it's secure, which is explicitly set by
- // application to avoid being screenshot or drawn via unsecure display.
- const bool supportsProtected = getRenderEngine().supportsProtectedContent();
- bool hasProtectedLayer = false;
- if (allowProtected && supportsProtected) {
- hasProtectedLayer = mScheduler
- ->schedule([=]() {
- bool protectedLayerFound = false;
- auto layers = getLayerSnapshots();
- for (auto& [_, layerFe] : layers) {
- protectedLayerFound |=
- (layerFe->mSnapshot->isVisible &&
- layerFe->mSnapshot->hasProtectedContent);
- }
- return protectedLayerFound;
- })
- .get();
- }
- const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
- const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
- GRALLOC_USAGE_HW_TEXTURE |
- (isProtected ? GRALLOC_USAGE_PROTECTED
- : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
- sp<GraphicBuffer> buffer =
- getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
- static_cast<android_pixel_format>(reqPixelFormat),
- 1 /* layerCount */, usage, "screenshot");
+ if (FlagManager::getInstance().single_hop_screenshot() &&
+ FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+ std::vector<sp<LayerFE>> layerFEs;
+ auto displayState =
+ getDisplayAndLayerSnapshotsFromMainThread(renderAreaBuilder, getLayerSnapshotsFn,
+ layerFEs);
- const status_t bufferStatus = buffer->initCheck();
- if (bufferStatus != OK) {
- // Animations may end up being really janky, but don't crash here.
- // Otherwise an irreponsible process may cause an SF crash by allocating
- // too much.
- ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
- invokeScreenCaptureError(bufferStatus, captureListener);
- return;
+ const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+ bool hasProtectedLayer = false;
+ if (allowProtected && supportsProtected) {
+ hasProtectedLayer = layersHasProtectedLayer(layerFEs);
+ }
+ const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+ const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ (isProtected ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ sp<GraphicBuffer> buffer =
+ getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1 /* layerCount */, usage, "screenshot");
+
+ const status_t bufferStatus = buffer->initCheck();
+ if (bufferStatus != OK) {
+ // Animations may end up being really janky, but don't crash here.
+ // Otherwise an irreponsible process may cause an SF crash by allocating
+ // too much.
+ ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+ invokeScreenCaptureError(bufferStatus, captureListener);
+ return;
+ }
+ const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+ renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::Usage::
+ WRITEABLE);
+ auto futureFence =
+ captureScreenshot(renderAreaBuilder, texture, false /* regionSampling */, grayscale,
+ isProtected, captureListener, displayState, layerFEs);
+ futureFence.get();
+
+ } else {
+ const bool supportsProtected = getRenderEngine().supportsProtectedContent();
+ bool hasProtectedLayer = false;
+ if (allowProtected && supportsProtected) {
+ auto layers = mScheduler->schedule([=]() { return getLayerSnapshotsFn(); }).get();
+ hasProtectedLayer = layersHasProtectedLayer(extractLayerFEs(layers));
+ }
+ const bool isProtected = hasProtectedLayer && allowProtected && supportsProtected;
+ const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ (isProtected ? GRALLOC_USAGE_PROTECTED
+ : GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN);
+ sp<GraphicBuffer> buffer =
+ getFactory().createGraphicBuffer(bufferSize.getWidth(), bufferSize.getHeight(),
+ static_cast<android_pixel_format>(reqPixelFormat),
+ 1 /* layerCount */, usage, "screenshot");
+
+ const status_t bufferStatus = buffer->initCheck();
+ if (bufferStatus != OK) {
+ // Animations may end up being really janky, but don't crash here.
+ // Otherwise an irreponsible process may cause an SF crash by allocating
+ // too much.
+ ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus);
+ invokeScreenCaptureError(bufferStatus, captureListener);
+ return;
+ }
+ const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
+ renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
+ renderengine::impl::ExternalTexture::Usage::
+ WRITEABLE);
+ auto futureFence = captureScreenshotLegacy(renderAreaBuilder, getLayerSnapshotsFn, texture,
+ false /* regionSampling */, grayscale,
+ isProtected, captureListener);
+ futureFence.get();
}
- const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared<
- renderengine::impl::ExternalTexture>(buffer, getRenderEngine(),
- renderengine::impl::ExternalTexture::Usage::
- WRITEABLE);
- auto fence = captureScreenCommon(std::move(renderAreaFuture), getLayerSnapshots, texture,
- false /* regionSampling */, grayscale, isProtected,
- captureListener);
- fence.get();
}
-ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon(
- RenderAreaFuture renderAreaFuture, GetLayerSnapshotsFunction getLayerSnapshots,
+std::optional<SurfaceFlinger::OutputCompositionState>
+SurfaceFlinger::getDisplayStateFromRenderAreaBuilder(RenderAreaBuilderVariant& renderAreaBuilder) {
+ sp<const DisplayDevice> display = nullptr;
+ {
+ Mutex::Autolock lock(mStateLock);
+ if (auto* layerRenderAreaBuilder =
+ std::get_if<LayerRenderAreaBuilder>(&renderAreaBuilder)) {
+ // LayerSnapshotBuilder should only be accessed from the main thread.
+ const frontend::LayerSnapshot* snapshot =
+ mLayerSnapshotBuilder.getSnapshot(layerRenderAreaBuilder->layer->getSequence());
+ if (!snapshot) {
+ ALOGW("Couldn't find layer snapshot for %d",
+ layerRenderAreaBuilder->layer->getSequence());
+ } else {
+ layerRenderAreaBuilder->setLayerSnapshot(*snapshot);
+ display = findDisplay(
+ [layerStack = snapshot->outputFilter.layerStack](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ });
+ }
+ } else if (auto* displayRenderAreaBuilder =
+ std::get_if<DisplayRenderAreaBuilder>(&renderAreaBuilder)) {
+ display = displayRenderAreaBuilder->displayWeak.promote();
+ }
+
+ if (display == nullptr) {
+ display = getDefaultDisplayDeviceLocked();
+ }
+
+ if (display != nullptr) {
+ return std::optional{display->getCompositionDisplay()->getState()};
+ }
+ }
+ return std::nullopt;
+}
+
+std::vector<sp<LayerFE>> SurfaceFlinger::extractLayerFEs(
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const {
+ std::vector<sp<LayerFE>> layerFEs;
+ layerFEs.reserve(layers.size());
+ for (const auto& [_, layerFE] : layers) {
+ layerFEs.push_back(layerFE);
+ }
+ return layerFEs;
+}
+
+ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshot(
+ const RenderAreaBuilderVariant& renderAreaBuilder,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ std::optional<OutputCompositionState>& displayState, std::vector<sp<LayerFE>>& layerFEs) {
+ ATRACE_CALL();
+
+ ScreenCaptureResults captureResults;
+ 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) {
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }
+ return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+ }
+
+ // Empty vector needed to pass into renderScreenImpl for legacy path
+ std::vector<std::pair<Layer*, sp<android::LayerFE>>> layers;
+ ftl::SharedFuture<FenceResult> renderFuture =
+ renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale, isProtected,
+ captureResults, displayState, layers, layerFEs);
+
+ if (captureListener) {
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ captureResults.fenceResult = std::move(fenceResult);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
+ }
+ return renderFuture;
+}
+
+ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenshotLegacy(
+ RenderAreaBuilderVariant renderAreaBuilder, GetLayerSnapshotsFunction getLayerSnapshotsFn,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener) {
ATRACE_CALL();
- auto future = mScheduler->schedule(
- [=, this, renderAreaFuture = std::move(renderAreaFuture)]() FTL_FAKE_GUARD(
- kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
- ScreenCaptureResults captureResults;
- std::shared_ptr<RenderArea> renderArea = renderAreaFuture.get();
- if (!renderArea) {
- ALOGW("Skipping screen capture because of invalid render area.");
- if (captureListener) {
- captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ auto takeScreenshotFn = [=, this, renderAreaBuilder = std::move(renderAreaBuilder)]() REQUIRES(
+ kMainThreadContext) mutable -> ftl::SharedFuture<FenceResult> {
+ auto layers = getLayerSnapshotsFn();
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ for (auto& [layer, layerFE] : layers) {
+ attachReleaseFenceFutureToLayer(layer, layerFE.get(), ui::INVALID_LAYER_STACK);
+ }
+ }
+ auto displayState = getDisplayStateFromRenderAreaBuilder(renderAreaBuilder);
+
+ ScreenCaptureResults captureResults;
+ 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) {
+ captureResults.fenceResult = base::unexpected(NO_MEMORY);
+ captureListener->onScreenCaptureCompleted(captureResults);
+ }
+ return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
+ }
+
+ auto layerFEs = extractLayerFEs(layers);
+ ftl::SharedFuture<FenceResult> renderFuture =
+ renderScreenImpl(std::move(renderArea), buffer, regionSampling, grayscale,
+ isProtected, captureResults, displayState, layers, layerFEs);
+
+ if (captureListener) {
+ // Defer blocking on renderFuture back to the Binder thread.
+ return ftl::Future(std::move(renderFuture))
+ .then([captureListener, captureResults = std::move(captureResults)](
+ FenceResult fenceResult) mutable -> FenceResult {
+ captureResults.fenceResult = std::move(fenceResult);
captureListener->onScreenCaptureCompleted(captureResults);
- }
- return ftl::yield<FenceResult>(base::unexpected(NO_ERROR)).share();
- }
+ return base::unexpected(NO_ERROR);
+ })
+ .share();
+ }
+ return renderFuture;
+ };
- ftl::SharedFuture<FenceResult> renderFuture;
- renderArea->render([&]() FTL_FAKE_GUARD(kMainThreadContext) {
- renderFuture =
- renderScreenImpl(renderArea, getLayerSnapshots, buffer, regionSampling,
- grayscale, isProtected, captureResults);
- });
-
- if (captureListener) {
- // Defer blocking on renderFuture back to the Binder thread.
- return ftl::Future(std::move(renderFuture))
- .then([captureListener, captureResults = std::move(captureResults)](
- FenceResult fenceResult) mutable -> FenceResult {
- captureResults.fenceResult = std::move(fenceResult);
- captureListener->onScreenCaptureCompleted(captureResults);
- return base::unexpected(NO_ERROR);
- })
- .share();
- }
- return renderFuture;
- });
+ // TODO(b/294936197): Run takeScreenshotsFn() in a binder thread to reduce the number
+ // of calls on the main thread.
+ auto future =
+ mScheduler->schedule(FTL_FAKE_GUARD(kMainThreadContext, std::move(takeScreenshotFn)));
// Flatten nested futures.
auto chain = ftl::Future(std::move(future)).then([](ftl::SharedFuture<FenceResult> future) {
@@ -8194,14 +8449,14 @@
}
ftl::SharedFuture<FenceResult> SurfaceFlinger::renderScreenImpl(
- std::shared_ptr<const RenderArea> renderArea, GetLayerSnapshotsFunction getLayerSnapshots,
+ std::unique_ptr<const RenderArea> renderArea,
const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults& captureResults) {
+ bool grayscale, bool isProtected, ScreenCaptureResults& captureResults,
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers, std::vector<sp<LayerFE>>& layerFEs) {
ATRACE_CALL();
- auto layers = getLayerSnapshots();
-
- for (auto& [_, layerFE] : layers) {
+ for (auto& layerFE : layerFEs) {
frontend::LayerSnapshot* snapshot = layerFE->mSnapshot.get();
captureResults.capturedSecureLayers |= (snapshot->isVisible && snapshot->isSecure);
captureResults.capturedHdrLayers |= isHdrLayer(*snapshot);
@@ -8221,79 +8476,64 @@
captureResults.capturedDataspace = requestedDataspace;
- {
- Mutex::Autolock lock(mStateLock);
- const DisplayDevice* display = nullptr;
- if (parent) {
- display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
- return display.getLayerStack() == layerStack;
- }).get();
- }
+ const bool enableLocalTonemapping = FlagManager::getInstance().local_tonemap_screenshots() &&
+ !renderArea->getHintForSeamlessTransition();
- if (display == nullptr) {
- display = renderArea->getDisplayDevice().get();
- }
+ if (displayState) {
+ const auto& state = displayState.value();
+ captureResults.capturedDataspace =
+ pickBestDataspace(requestedDataspace, state, captureResults.capturedHdrLayers,
+ renderArea->getHintForSeamlessTransition());
+ sdrWhitePointNits = state.sdrWhitePointNits;
- if (display == nullptr) {
- display = getDefaultDisplayDeviceLocked().get();
- }
-
- if (display != nullptr) {
- const auto& state = display->getCompositionDisplay()->getState();
- captureResults.capturedDataspace =
- pickBestDataspace(requestedDataspace, display, captureResults.capturedHdrLayers,
- renderArea->getHintForSeamlessTransition());
- sdrWhitePointNits = state.sdrWhitePointNits;
-
- // TODO(b/298219334): Clean this up once we verify this doesn't break anything
- static constexpr bool kScreenshotsDontDim = true;
-
- if (kScreenshotsDontDim && !captureResults.capturedHdrLayers) {
- displayBrightnessNits = sdrWhitePointNits;
- } else {
- displayBrightnessNits = state.displayBrightnessNits;
- // Only clamp the display brightness if this is not a seamless transition. Otherwise
- // for seamless transitions it's important to match the current display state as the
- // buffer will be shown under these same conditions, and we want to avoid any
- // flickers
+ if (!captureResults.capturedHdrLayers) {
+ displayBrightnessNits = sdrWhitePointNits;
+ } else {
+ displayBrightnessNits = state.displayBrightnessNits;
+ if (!enableLocalTonemapping) {
+ // Only clamp the display brightness if this is not a seamless transition.
+ // Otherwise for seamless transitions it's important to match the current
+ // display state as the buffer will be shown under these same conditions, and we
+ // want to avoid any flickers
if (sdrWhitePointNits > 1.0f && !renderArea->getHintForSeamlessTransition()) {
- // Restrict the amount of HDR "headroom" in the screenshot to avoid over-dimming
- // the SDR portion. 2.0 chosen by experimentation
+ // Restrict the amount of HDR "headroom" in the screenshot to avoid
+ // over-dimming the SDR portion. 2.0 chosen by experimentation
constexpr float kMaxScreenshotHeadroom = 2.0f;
displayBrightnessNits = std::min(sdrWhitePointNits * kMaxScreenshotHeadroom,
displayBrightnessNits);
}
}
+ }
- // Screenshots leaving the device should be colorimetric
- if (requestedDataspace == ui::Dataspace::UNKNOWN &&
- renderArea->getHintForSeamlessTransition()) {
- renderIntent = state.renderIntent;
- }
+ // Screenshots leaving the device should be colorimetric
+ if (requestedDataspace == ui::Dataspace::UNKNOWN &&
+ renderArea->getHintForSeamlessTransition()) {
+ renderIntent = state.renderIntent;
}
}
captureResults.buffer = capturedBuffer->getBuffer();
ui::LayerStack layerStack{ui::DEFAULT_LAYER_STACK};
- if (!layers.empty()) {
- const sp<LayerFE>& layerFE = layers.back().second;
+ if (!layerFEs.empty()) {
+ const sp<LayerFE>& layerFE = layerFEs.back();
layerStack = layerFE->getCompositionState()->outputFilter.layerStack;
}
- auto copyLayerFEs = [&layers]() {
- std::vector<sp<compositionengine::LayerFE>> layerFEs;
- layerFEs.reserve(layers.size());
- for (const auto& [_, layerFE] : layers) {
- layerFEs.push_back(layerFE);
+ auto copyLayerFEs = [&layerFEs]() {
+ std::vector<sp<compositionengine::LayerFE>> ceLayerFEs;
+ ceLayerFEs.reserve(layerFEs.size());
+ for (const auto& layerFE : layerFEs) {
+ ceLayerFEs.push_back(layerFE);
}
- return layerFEs;
+ return ceLayerFEs;
};
auto present = [this, buffer = capturedBuffer, dataspace = captureResults.capturedDataspace,
sdrWhitePointNits, displayBrightnessNits, grayscale, isProtected,
layerFEs = copyLayerFEs(), layerStack, regionSampling,
- renderArea = std::move(renderArea), renderIntent]() -> FenceResult {
+ renderArea = std::move(renderArea), renderIntent,
+ enableLocalTonemapping]() -> FenceResult {
std::unique_ptr<compositionengine::CompositionEngine> compositionEngine =
mFactory.createCompositionEngine();
compositionEngine->setRenderEngine(mRenderEngine.get());
@@ -8302,7 +8542,11 @@
.renderIntent = renderIntent};
float targetBrightness = 1.0f;
- if (dataspace == ui::Dataspace::BT2020_HLG) {
+ if (enableLocalTonemapping) {
+ // Boost the whole scene so that SDR white is at 1.0 while still communicating the hdr
+ // sdr ratio via display brightness / sdrWhite nits.
+ targetBrightness = sdrWhitePointNits / displayBrightnessNits;
+ } else if (dataspace == ui::Dataspace::BT2020_HLG) {
const float maxBrightnessNits = displayBrightnessNits / sdrWhitePointNits * 203;
// With a low dimming ratio, don't fit the entire curve. Otherwise mixed content
// will appear way too bright.
@@ -8328,7 +8572,8 @@
.treat170mAsSrgb = mTreat170mAsSrgb,
.dimInGammaSpaceForEnhancedScreenshots =
dimInGammaSpaceForEnhancedScreenshots,
- .isProtected = isProtected});
+ .isProtected = isProtected,
+ .enableLocalTonemapping = enableLocalTonemapping});
const float colorSaturation = grayscale ? 0 : 1;
compositionengine::CompositionRefreshArgs refreshArgs{
@@ -8350,27 +8595,35 @@
//
// TODO(b/196334700) Once we use RenderEngineThreaded everywhere we can always defer the call
// to CompositionEngine::present.
- auto presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
- : ftl::yield(present()).share();
+ ftl::SharedFuture<FenceResult> presentFuture;
+ if (FlagManager::getInstance().single_hop_screenshot() &&
+ FlagManager::getInstance().ce_fence_promise() && mRenderEngine->isThreaded()) {
+ presentFuture = ftl::yield(present()).share();
+ } else {
+ presentFuture = mRenderEngine->isThreaded() ? ftl::defer(std::move(present)).share()
+ : ftl::yield(present()).share();
+ }
- for (auto& [layer, layerFE] : layers) {
- layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
- [layerFE = std::move(layerFE)](FenceResult) {
- if (FlagManager::getInstance()
- .screenshot_fence_preservation()) {
- const auto compositionResult =
- layerFE->stealCompositionResult();
- const auto& fences = compositionResult.releaseFences;
- // CompositionEngine may choose to cull layers that
- // aren't visible, so pass a non-fence.
- return fences.empty() ? Fence::NO_FENCE
- : fences.back().first.get();
- } else {
- return layerFE->stealCompositionResult()
- .releaseFences.back()
- .first.get();
- }
- });
+ if (!FlagManager::getInstance().ce_fence_promise()) {
+ for (auto& [layer, layerFE] : layers) {
+ layer->onLayerDisplayed(presentFuture, ui::INVALID_LAYER_STACK,
+ [layerFE = std::move(layerFE)](FenceResult) {
+ if (FlagManager::getInstance()
+ .screenshot_fence_preservation()) {
+ const auto compositionResult =
+ layerFE->stealCompositionResult();
+ const auto& fences = compositionResult.releaseFences;
+ // CompositionEngine may choose to cull layers that
+ // aren't visible, so pass a non-fence.
+ return fences.empty() ? Fence::NO_FENCE
+ : fences.back().first.get();
+ } else {
+ return layerFE->stealCompositionResult()
+ .releaseFences.back()
+ .first.get();
+ }
+ });
+ }
}
return presentFuture;
@@ -8478,35 +8731,11 @@
break;
}
- if (!shouldApplyRefreshRateSelectorPolicy(*display)) {
- ALOGV("%s(%s): Skipped applying policy", __func__, to_string(displayId).c_str());
- return NO_ERROR;
- }
-
return applyRefreshRateSelectorPolicy(displayId, selector);
}
-bool SurfaceFlinger::shouldApplyRefreshRateSelectorPolicy(const DisplayDevice& display) const {
- if (display.isPoweredOn() || mPhysicalDisplays.size() == 1) return true;
-
- LOG_ALWAYS_FATAL_IF(display.isVirtual());
- const auto displayId = display.getPhysicalId();
-
- // The display is powered off, and this is a multi-display device. If the display is the
- // inactive internal display of a dual-display foldable, then the policy will be applied
- // when it becomes active upon powering on.
- //
- // TODO(b/255635711): Remove this function (i.e. returning `false` as a special case) once
- // concurrent mode setting across multiple (potentially powered off) displays is supported.
- //
- return displayId == mActiveDisplayId ||
- !mPhysicalDisplays.get(displayId)
- .transform(&PhysicalDisplay::isInternal)
- .value_or(false);
-}
-
status_t SurfaceFlinger::applyRefreshRateSelectorPolicy(
- PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector, bool force) {
+ PhysicalDisplayId displayId, const scheduler::RefreshRateSelector& selector) {
const scheduler::RefreshRateSelector::Policy currentPolicy = selector.getCurrentPolicy();
ALOGV("Setting desired display mode specs: %s", currentPolicy.toString().c_str());
@@ -8537,7 +8766,7 @@
return INVALID_OPERATION;
}
- setDesiredMode({std::move(preferredMode), .emitEvent = true, .force = force});
+ setDesiredMode({std::move(preferredMode), .emitEvent = true});
// Update the frameRateOverride list as the display render rate might have changed
if (mScheduler->updateFrameRateOverrides(scheduler::GlobalSignals{}, preferredFps)) {
@@ -8591,8 +8820,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);
}
@@ -8729,22 +8963,29 @@
void SurfaceFlinger::enableRefreshRateOverlay(bool enable) {
bool setByHwc = getHwComposer().hasCapability(Capability::REFRESH_RATE_CHANGED_CALLBACK_DEBUG);
- for (const auto& [id, display] : mPhysicalDisplays) {
- if (display.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
+ for (const auto& [displayId, physical] : mPhysicalDisplays) {
+ if (physical.snapshot().connectionType() == ui::DisplayConnectionType::Internal ||
FlagManager::getInstance().refresh_rate_overlay_on_external_display()) {
- if (const auto device = getDisplayDeviceLocked(id)) {
- const auto enableOverlay = [&](const bool setByHwc) FTL_FAKE_GUARD(
- kMainThreadContext) {
- device->enableRefreshRateOverlay(enable, setByHwc, mRefreshRateOverlaySpinner,
- mRefreshRateOverlayRenderRate,
- mRefreshRateOverlayShowInMiddle);
+ if (const auto display = getDisplayDeviceLocked(displayId)) {
+ const auto enableOverlay = [&](bool setByHwc) FTL_FAKE_GUARD(kMainThreadContext) {
+ const auto activeMode = mDisplayModeController.getActiveMode(displayId);
+ const Fps refreshRate = activeMode.modePtr->getVsyncRate();
+ const Fps renderFps = activeMode.fps;
+
+ display->enableRefreshRateOverlay(enable, setByHwc, refreshRate, renderFps,
+ mRefreshRateOverlaySpinner,
+ mRefreshRateOverlayRenderRate,
+ mRefreshRateOverlayShowInMiddle);
};
+
enableOverlay(setByHwc);
if (setByHwc) {
const auto status =
- getHwComposer().setRefreshRateChangedCallbackDebugEnabled(id, enable);
+ getHwComposer().setRefreshRateChangedCallbackDebugEnabled(displayId,
+ enable);
if (status != NO_ERROR) {
- ALOGE("Error updating the refresh rate changed callback debug enabled");
+ ALOGE("Error %s refresh rate changed callback debug",
+ enable ? "enabling" : "disabling");
enableOverlay(/*setByHwc*/ false);
}
}
@@ -8890,19 +9131,14 @@
const DisplayDevice& activeDisplay) {
ATRACE_CALL();
- // For the first display activated during boot, there is no need to force setDesiredMode,
- // because DM is about to send its policy via setDesiredDisplayModeSpecs.
- bool forceApplyPolicy = false;
-
if (inactiveDisplayPtr) {
inactiveDisplayPtr->getCompositionDisplay()->setLayerCachingTexturePoolEnabled(false);
- forceApplyPolicy = true;
}
mActiveDisplayId = activeDisplay.getPhysicalId();
activeDisplay.getCompositionDisplay()->setLayerCachingTexturePoolEnabled(true);
- mScheduler->resetPhaseConfiguration(activeDisplay.getActiveMode().fps);
+ mScheduler->resetPhaseConfiguration(mDisplayModeController.getActiveMode(mActiveDisplayId).fps);
// TODO(b/255635711): Check for pending mode changes on other displays.
mScheduler->setModeChangePending(false);
@@ -8913,12 +9149,11 @@
mActiveDisplayTransformHint = activeDisplay.getTransformHint();
sActiveDisplayRotationFlags = ui::Transform::toRotationFlags(activeDisplay.getOrientation());
- // The policy of the new active/pacesetter display may have changed while it was inactive. In
- // that case, its preferred mode has not been propagated to HWC (via setDesiredMode). In either
- // case, the Scheduler's cachedModeChangedParams must be initialized to the newly active mode,
- // and the kernel idle timer of the newly active display must be toggled.
- applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector(),
- forceApplyPolicy);
+ // Whether or not the policy of the new active/pacesetter display changed while it was inactive
+ // (in which case its preferred mode has already been propagated to HWC via setDesiredMode), the
+ // Scheduler's cachedModeChangedParams must be initialized to the newly active mode, and the
+ // kernel idle timer of the newly active display must be toggled.
+ applyRefreshRateSelectorPolicy(mActiveDisplayId, activeDisplay.refreshRateSelector());
}
status_t SurfaceFlinger::addWindowInfosListener(const sp<IWindowInfosListener>& windowInfosListener,
@@ -8936,6 +9171,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;
}
@@ -9063,7 +9300,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
@@ -9123,7 +9360,7 @@
snapshots[i] = std::move(layerFE->mSnapshot);
}
}
- if (mLegacyFrontEndEnabled && !mLayerLifecycleManagerEnabled) {
+ if (!mLayerLifecycleManagerEnabled) {
for (auto [layer, layerFE] : layers) {
layer->updateLayerSnapshot(std::move(layerFE->mSnapshot));
}
@@ -9136,7 +9373,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) {
@@ -9159,7 +9397,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 &&
@@ -9197,11 +9435,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;
}
@@ -9223,7 +9462,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));
});
@@ -9236,7 +9475,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);
@@ -9278,7 +9518,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,
@@ -9458,35 +9698,35 @@
}
}
-binder::Status SurfaceComposerAIDL::createDisplay(const std::string& displayName, bool secure,
- float requestedRefreshRate,
- sp<IBinder>* outDisplay) {
+binder::Status SurfaceComposerAIDL::createVirtualDisplay(const std::string& displayName,
+ bool isSecure, const std::string& uniqueId,
+ float requestedRefreshRate,
+ sp<IBinder>* outDisplay) {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
- String8 displayName8 = String8::format("%s", displayName.c_str());
- *outDisplay = mFlinger->createDisplay(displayName8, secure, requestedRefreshRate);
+ *outDisplay =
+ mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId, requestedRefreshRate);
return binder::Status::ok();
}
-binder::Status SurfaceComposerAIDL::destroyDisplay(const sp<IBinder>& display) {
+binder::Status SurfaceComposerAIDL::destroyVirtualDisplay(const sp<IBinder>& displayToken) {
status_t status = checkAccessPermission();
if (status != OK) {
return binderStatusFromStatusT(status);
}
- mFlinger->destroyDisplay(display);
- return binder::Status::ok();
+ return binder::Status::fromStatusT(mFlinger->destroyVirtualDisplay(displayToken));
}
binder::Status SurfaceComposerAIDL::getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) {
std::vector<PhysicalDisplayId> physicalDisplayIds = mFlinger->getPhysicalDisplayIds();
std::vector<int64_t> displayIds;
displayIds.reserve(physicalDisplayIds.size());
- for (auto item : physicalDisplayIds) {
- displayIds.push_back(static_cast<int64_t>(item.value));
+ for (const auto id : physicalDisplayIds) {
+ displayIds.push_back(static_cast<int64_t>(id.value));
}
- *outDisplayIds = displayIds;
+ *outDisplayIds = std::move(displayIds);
return binder::Status::ok();
}
@@ -9789,6 +10029,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);
@@ -9834,22 +10075,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;
@@ -10261,6 +10486,11 @@
return gui::getSchedulingPolicy(outPolicy);
}
+binder::Status SurfaceComposerAIDL::notifyShutdown() {
+ TransactionTraceWriter::getInstance().invoke("systemShutdown_", /* overwrite= */ false);
+ return ::android::binder::Status::ok();
+}
+
status_t SurfaceComposerAIDL::checkAccessPermission(bool usePermissionCache) {
if (!mFlinger->callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index afc7647..a3534b5 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"
@@ -89,6 +89,7 @@
#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
#include "TransactionState.h"
+#include "Utils/OnceFuture.h"
#include <atomic>
#include <cstdint>
@@ -191,6 +192,9 @@
Always,
};
+struct DisplayRenderAreaBuilder;
+struct LayerRenderAreaBuilder;
+
using DisplayColorSetting = compositionengine::OutputColorSetting;
class SurfaceFlinger : public BnSurfaceComposer,
@@ -382,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&)>;
@@ -508,10 +512,7 @@
return lockedDumper(std::bind(dump, this, _1, _2, _3));
}
- template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
- Dumper mainThreadDumper(F dump) {
- using namespace std::placeholders;
- Dumper dumper = std::bind(dump, this, _3);
+ Dumper mainThreadDumperImpl(Dumper dumper) {
return [this, dumper](const DumpArgs& args, bool asProto, std::string& result) -> void {
mScheduler
->schedule(
@@ -521,21 +522,35 @@
};
}
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper mainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ return mainThreadDumperImpl(std::bind(dump, this, _3));
+ }
+
+ template <typename F, std::enable_if_t<std::is_member_function_pointer_v<F>>* = nullptr>
+ Dumper argsMainThreadDumper(F dump) {
+ using namespace std::placeholders;
+ return mainThreadDumperImpl(std::bind(dump, this, _1, _3));
+ }
+
// Maximum allowed number of display frames that can be set through backdoor
static const int MAX_ALLOWED_DISPLAY_FRAMES = 2048;
static const size_t MAX_LAYERS = 4096;
- // Implements IBinder.
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
- status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
- bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
+ static bool callingThreadHasUnscopedSurfaceFlingerAccess(bool usePermissionCache = true)
EXCLUDES(mStateLock);
- // Implements ISurfaceComposer
- sp<IBinder> createDisplay(const String8& displayName, bool secure,
- float requestedRefreshRate = 0.0f);
- void destroyDisplay(const sp<IBinder>& displayToken);
+ // IBinder overrides:
+ status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
+ status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); }
+
+ // ISurfaceComposer implementation:
+ sp<IBinder> createVirtualDisplay(const std::string& displayName, bool isSecure,
+ const std::string& uniqueId,
+ float requestedRefreshRate = 0.0f);
+ status_t destroyVirtualDisplay(const sp<IBinder>& displayToken);
std::vector<PhysicalDisplayId> getPhysicalDisplayIds() const EXCLUDES(mStateLock) {
Mutex::Autolock lock(mStateLock);
return getPhysicalDisplayIdsLocked();
@@ -550,7 +565,7 @@
bool hasListenerCallbacks, const std::vector<ListenerCallbacks>& listenerCallbacks,
uint64_t transactionId, const std::vector<uint64_t>& mergedTransactionIds) override;
void bootFinished();
- virtual status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
+ status_t getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const;
sp<IDisplayEventConnection> createDisplayEventConnection(
gui::ISurfaceComposer::VsyncSource vsyncSource =
gui::ISurfaceComposer::VsyncSource::eVsyncSourceApp,
@@ -589,7 +604,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;
@@ -655,7 +669,7 @@
void updateHdcpLevels(hal::HWDisplayId hwcDisplayId, int32_t connectedLevel, int32_t maxLevel);
- // Implements IBinder::DeathRecipient.
+ // IBinder::DeathRecipient overrides:
void binderDied(const wp<IBinder>& who) override;
// HWC2::ComposerCallback overrides:
@@ -687,6 +701,9 @@
void onChoreographerAttached() override;
void onExpectedPresentTimePosted(TimePoint expectedPresentTime, ftl::NonNull<DisplayModePtr>,
Fps renderRate) override;
+ void onCommitNotComposited(PhysicalDisplayId pacesetterDisplayId) override
+ REQUIRES(kMainThreadContext);
+ void vrrDisplayIdle(bool idle) override;
// ICEPowerCallback overrides:
void notifyCpuLoadUp() override;
@@ -699,7 +716,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,
@@ -721,12 +738,12 @@
status_t setActiveModeFromBackdoor(const sp<display::DisplayToken>&, DisplayModeId, Fps minFps,
Fps maxFps);
- void initiateDisplayModeChanges() REQUIRES(mStateLock, kMainThreadContext);
- void finalizeDisplayModeChange(DisplayDevice&) REQUIRES(mStateLock, kMainThreadContext);
+ void initiateDisplayModeChanges() REQUIRES(kMainThreadContext) REQUIRES(mStateLock);
+ void finalizeDisplayModeChange(PhysicalDisplayId) REQUIRES(kMainThreadContext)
+ REQUIRES(mStateLock);
- // TODO(b/241285191): Replace DisplayDevice with DisplayModeRequest, and move to Scheduler.
- void dropModeRequest(const sp<DisplayDevice>&) REQUIRES(mStateLock);
- void applyActiveMode(const sp<DisplayDevice>&) REQUIRES(mStateLock);
+ void dropModeRequest(PhysicalDisplayId) REQUIRES(kMainThreadContext);
+ void applyActiveMode(PhysicalDisplayId) REQUIRES(kMainThreadContext);
// Called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode)
@@ -741,13 +758,9 @@
const sp<DisplayDevice>&, const scheduler::RefreshRateSelector::PolicyVariant&)
EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
- bool shouldApplyRefreshRateSelectorPolicy(const DisplayDevice&) const
- REQUIRES(mStateLock, kMainThreadContext);
-
// TODO(b/241285191): Look up RefreshRateSelector on Scheduler to remove redundant parameter.
status_t applyRefreshRateSelectorPolicy(PhysicalDisplayId,
- const scheduler::RefreshRateSelector&,
- bool force = false)
+ const scheduler::RefreshRateSelector&)
REQUIRES(mStateLock, kMainThreadContext);
void commitTransactionsLegacy() EXCLUDES(mStateLock) REQUIRES(kMainThreadContext);
@@ -762,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);
@@ -795,7 +811,7 @@
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);
@@ -805,8 +821,8 @@
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);
@@ -823,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.
@@ -839,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);
@@ -872,21 +888,51 @@
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
- // Boot animation, on/off animations and screen capture
- void startBootAnim();
+ // Creates a promise for a future release fence for a layer. This allows for
+ // the layer to keep track of when its buffer can be released.
+ void attachReleaseFenceFutureToLayer(Layer* layer, LayerFE* layerFE, ui::LayerStack layerStack);
- void captureScreenCommon(RenderAreaFuture, GetLayerSnapshotsFunction, ui::Size bufferSize,
- ui::PixelFormat, bool allowProtected, bool grayscale,
- const sp<IScreenCaptureListener>&);
- ftl::SharedFuture<FenceResult> captureScreenCommon(
- RenderAreaFuture, GetLayerSnapshotsFunction,
+ // Checks if a protected layer exists in a list of layers.
+ bool layersHasProtectedLayer(const std::vector<sp<LayerFE>>& layers) const;
+
+ using OutputCompositionState = compositionengine::impl::OutputCompositionState;
+
+ std::optional<OutputCompositionState> getDisplayAndLayerSnapshotsFromMainThread(
+ RenderAreaBuilderVariant& renderAreaBuilder,
+ GetLayerSnapshotsFunction getLayerSnapshotsFn, std::vector<sp<LayerFE>>& layerFEs);
+
+ void captureScreenCommon(RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
+ ui::Size bufferSize, ui::PixelFormat, bool allowProtected,
+ bool grayscale, const sp<IScreenCaptureListener>&);
+
+ std::optional<OutputCompositionState> getDisplayStateFromRenderAreaBuilder(
+ RenderAreaBuilderVariant& renderAreaBuilder) REQUIRES(kMainThreadContext);
+
+ // Legacy layer raw pointer is not safe to access outside the main thread.
+ // Creates a new vector consisting only of LayerFEs, which can be safely
+ // accessed outside the main thread.
+ std::vector<sp<LayerFE>> extractLayerFEs(
+ const std::vector<std::pair<Layer*, sp<LayerFE>>>& layers) const;
+
+ ftl::SharedFuture<FenceResult> captureScreenshot(
+ const RenderAreaBuilderVariant& renderAreaBuilder,
+ const std::shared_ptr<renderengine::ExternalTexture>& buffer, bool regionSampling,
+ bool grayscale, bool isProtected, const sp<IScreenCaptureListener>& captureListener,
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<sp<LayerFE>>& layerFEs);
+
+ ftl::SharedFuture<FenceResult> captureScreenshotLegacy(
+ RenderAreaBuilderVariant, GetLayerSnapshotsFunction,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
bool grayscale, bool isProtected, const sp<IScreenCaptureListener>&);
+
ftl::SharedFuture<FenceResult> renderScreenImpl(
- std::shared_ptr<const RenderArea>, GetLayerSnapshotsFunction,
+ std::unique_ptr<const RenderArea>,
const std::shared_ptr<renderengine::ExternalTexture>&, bool regionSampling,
- bool grayscale, bool isProtected, ScreenCaptureResults&) EXCLUDES(mStateLock)
- REQUIRES(kMainThreadContext);
+ bool grayscale, bool isProtected, ScreenCaptureResults&,
+ std::optional<OutputCompositionState>& displayState,
+ std::vector<std::pair<Layer*, sp<LayerFE>>>& layers,
+ std::vector<sp<LayerFE>>& layerFEs);
// If the uid provided is not UNSET_UID, the traverse will skip any layers that don't have a
// matching ownerUid
@@ -962,8 +1008,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) {
@@ -1032,10 +1077,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,
@@ -1051,8 +1099,7 @@
const DisplayDeviceState& drawingState)
REQUIRES(mStateLock, kMainThreadContext);
- void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&)
- REQUIRES(mStateLock);
+ void dispatchDisplayModeChangeEvent(PhysicalDisplayId, const scheduler::FrameRateMode&);
/*
* VSYNC
@@ -1112,9 +1159,10 @@
void dumpHwcLayersMinidumpLockedLegacy(std::string& result) const REQUIRES(mStateLock);
void appendSfConfigString(std::string& result) const;
- void listLayersLocked(std::string& result) const;
- void dumpStatsLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
- void clearStatsLocked(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);
@@ -1132,7 +1180,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;
@@ -1180,12 +1229,19 @@
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();
- sp<StartPropertySetThread> mStartPropertySetThread;
+
surfaceflinger::Factory& mFactory;
pid_t mPid;
- std::future<void> mRenderEnginePrimeCacheFuture;
+
+ // TODO: b/328459745 - Encapsulate in a SystemProperties object.
+ utils::OnceFuture mInitBootPropsFuture;
+
+ utils::OnceFuture mRenderEnginePrimeCacheFuture;
// mStateLock has conventions related to the current thread, because only
// the main thread should modify variables protected by mStateLock.
@@ -1221,6 +1277,7 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
bool mIsUserBuild = true;
+ bool mHasReliablePresentFences = false;
// Can only accessed from the main thread, these members
// don't need synchronization
@@ -1282,9 +1339,9 @@
display::PhysicalDisplays mPhysicalDisplays GUARDED_BY(mStateLock);
// The inner or outer display for foldables, assuming they have mutually exclusive power states.
- // Atomic because writes from onActiveDisplayChangedLocked are not always under mStateLock, but
- // reads from ISchedulerCallback::requestDisplayModes may happen concurrently.
- std::atomic<PhysicalDisplayId> mActiveDisplayId GUARDED_BY(mStateLock);
+ std::atomic<PhysicalDisplayId> mActiveDisplayId;
+
+ display::DisplayModeController mDisplayModeController;
struct {
DisplayIdGenerator<GpuVirtualDisplayId> gpu;
@@ -1449,25 +1506,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
@@ -1511,7 +1571,7 @@
class SurfaceComposerAIDL : public gui::BnSurfaceComposer {
public:
- SurfaceComposerAIDL(sp<SurfaceFlinger> sf) : mFlinger(std::move(sf)) {}
+ explicit SurfaceComposerAIDL(sp<SurfaceFlinger> sf) : mFlinger(std::move(sf)) {}
binder::Status bootFinished() override;
binder::Status createDisplayEventConnection(
@@ -1519,9 +1579,10 @@
const sp<IBinder>& layerHandle,
sp<gui::IDisplayEventConnection>* outConnection) override;
binder::Status createConnection(sp<gui::ISurfaceComposerClient>* outClient) override;
- binder::Status createDisplay(const std::string& displayName, bool secure,
- float requestedRefreshRate, sp<IBinder>* outDisplay) override;
- binder::Status destroyDisplay(const sp<IBinder>& display) override;
+ binder::Status createVirtualDisplay(const std::string& displayName, bool isSecure,
+ const std::string& uniqueId, float requestedRefreshRate,
+ sp<IBinder>* outDisplay) override;
+ binder::Status destroyVirtualDisplay(const sp<IBinder>& displayToken) override;
binder::Status getPhysicalDisplayIds(std::vector<int64_t>* outDisplayIds) override;
binder::Status getPhysicalDisplayToken(int64_t displayId, sp<IBinder>* outDisplay) override;
binder::Status setPowerMode(const sp<IBinder>& display, int mode) override;
@@ -1569,7 +1630,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;
@@ -1633,6 +1693,7 @@
binder::Status getStalledTransactionInfo(
int pid, std::optional<gui::StalledTransactionInfo>* outInfo) override;
binder::Status getSchedulingPolicy(gui::SchedulingPolicy* outPolicy) override;
+ binder::Status notifyShutdown() override;
private:
static const constexpr bool kUsePermissionCache = true;
@@ -1643,7 +1704,7 @@
gui::DynamicDisplayInfo*& outInfo);
private:
- sp<SurfaceFlinger> mFlinger;
+ const sp<SurfaceFlinger> mFlinger;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 7e6894d..b1d8ba9 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -26,7 +26,6 @@
#include "FrameTracer/FrameTracer.h"
#include "Layer.h"
#include "NativeWindowSurface.h"
-#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
#include "SurfaceFlingerProperties.h"
@@ -53,11 +52,6 @@
}
}
-sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
- bool timestampPropertyValue) {
- return sp<StartPropertySetThread>::make(timestampPropertyValue);
-}
-
sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) {
return sp<DisplayDevice>::make(creationArgs);
}
@@ -91,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 2c6de0e..7ebf10f 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -29,7 +29,6 @@
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) override;
- sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
@@ -42,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 f310c4a..c7d1fa0 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -39,7 +39,6 @@
class IGraphicBufferProducer;
class Layer;
class LayerFE;
-class StartPropertySetThread;
class SurfaceFlinger;
class TimeStats;
@@ -71,8 +70,6 @@
virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) = 0;
- virtual sp<StartPropertySetThread> createStartPropertySetThread(
- bool timestampPropertyValue) = 0;
virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) = 0;
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount,
@@ -88,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 3752d5e..b189598 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -247,7 +247,8 @@
proto.set_auto_refresh(layer.autoRefresh);
}
if (layer.what & layer_state_t::eTrustedOverlayChanged) {
- proto.set_is_trusted_overlay(layer.isTrustedOverlay);
+ proto.set_is_trusted_overlay(layer.trustedOverlay == gui::TrustedOverlay::ENABLED);
+ // TODO(b/339701674) update protos
}
if (layer.what & layer_state_t::eBufferCropChanged) {
LayerProtoHelper::writeToProto(layer.bufferCrop, proto.mutable_buffer_crop());
@@ -435,6 +436,7 @@
layer.bufferData->flags = ftl::Flags<BufferData::BufferDataChange>(bufferProto.flags());
layer.bufferData->cachedBuffer.id = bufferProto.cached_buffer_id();
layer.bufferData->acquireFence = Fence::NO_FENCE;
+ layer.bufferData->dequeueTime = -1;
}
if (proto.what() & layer_state_t::eApiChanged) {
@@ -515,7 +517,8 @@
layer.autoRefresh = proto.auto_refresh();
}
if (proto.what() & layer_state_t::eTrustedOverlayChanged) {
- layer.isTrustedOverlay = proto.is_trusted_overlay();
+ layer.trustedOverlay = proto.is_trusted_overlay() ? gui::TrustedOverlay::ENABLED
+ : gui::TrustedOverlay::UNSET;
}
if (proto.what() & layer_state_t::eBufferCropChanged) {
LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop);
@@ -566,7 +569,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 +591,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/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index 7b5298c..222ae30 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -30,6 +30,7 @@
#include <cinttypes>
#include <binder/IInterface.h>
+#include <common/FlagManager.h>
#include <utils/RefBase.h>
namespace android {
@@ -128,9 +129,17 @@
sp<IBinder> surfaceControl = handle->surfaceControl.promote();
if (surfaceControl) {
sp<Fence> prevFence = nullptr;
- for (const auto& future : handle->previousReleaseFences) {
- mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
+
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ for (auto& future : handle->previousReleaseFences) {
+ mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
+ }
+ } else {
+ for (const auto& future : handle->previousSharedReleaseFences) {
+ mergeFence(handle->name.c_str(), future.get().value_or(Fence::NO_FENCE), prevFence);
+ }
}
+
handle->previousReleaseFence = prevFence;
handle->previousReleaseFences.clear();
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index 245398f..cb7150b 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -46,7 +46,8 @@
bool releasePreviousBuffer = false;
std::string name;
sp<Fence> previousReleaseFence;
- std::vector<ftl::SharedFuture<FenceResult>> previousReleaseFences;
+ std::vector<ftl::Future<FenceResult>> previousReleaseFences;
+ std::vector<ftl::SharedFuture<FenceResult>> previousSharedReleaseFences;
std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence = -1;
nsecs_t latchTime = -1;
std::optional<uint32_t> transformHint = std::nullopt;
diff --git a/services/surfaceflinger/TransactionState.h b/services/surfaceflinger/TransactionState.h
index 31cd2d7..e5d6481 100644
--- a/services/surfaceflinger/TransactionState.h
+++ b/services/surfaceflinger/TransactionState.h
@@ -23,6 +23,7 @@
#include "FrontEnd/LayerCreationArgs.h"
#include "renderengine/ExternalTexture.h"
+#include <common/FlagManager.h>
#include <gui/LayerState.h>
#include <system/window.h>
@@ -86,7 +87,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);
@@ -108,9 +109,22 @@
for (const auto& state : states) {
const bool frameRateChanged = state.state.what & layer_state_t::eFrameRateChanged;
- if (!frameRateChanged ||
- state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
- return true;
+ if (FlagManager::getInstance().vrr_bugfix_24q4()) {
+ const bool frameRateIsNoVote = frameRateChanged &&
+ state.state.frameRateCompatibility == ANATIVEWINDOW_FRAME_RATE_NO_VOTE;
+ const bool frameRateCategoryChanged =
+ state.state.what & layer_state_t::eFrameRateCategoryChanged;
+ const bool frameRateCategoryIsNoPreference = frameRateCategoryChanged &&
+ state.state.frameRateCategory ==
+ ANATIVEWINDOW_FRAME_RATE_CATEGORY_NO_PREFERENCE;
+ if (!frameRateIsNoVote && !frameRateCategoryIsNoPreference) {
+ return true;
+ }
+ } else {
+ if (!frameRateChanged ||
+ state.state.frameRateCompatibility != ANATIVEWINDOW_FRAME_RATE_NO_VOTE) {
+ return true;
+ }
}
}
diff --git a/services/surfaceflinger/Utils/OnceFuture.h b/services/surfaceflinger/Utils/OnceFuture.h
new file mode 100644
index 0000000..412038c
--- /dev/null
+++ b/services/surfaceflinger/Utils/OnceFuture.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <future>
+#include <mutex>
+
+#include <android-base/thread_annotations.h>
+
+namespace android::utils {
+
+// Allows a thread to `wait` for a future produced by a different thread. The future is returned by
+// the first call to a function `F` that multiple threads may `callOnce`. If no `callOnce` happens,
+// then `wait` does nothing. Otherwise, it blocks on the future, then destroys it, which resets the
+// `OnceFuture`.
+class OnceFuture {
+public:
+ template <typename F>
+ void callOnce(F f) {
+ std::lock_guard lock(mMutex);
+ if (!mFuture.valid()) {
+ mFuture = f();
+ }
+ }
+
+ void wait() {
+ std::lock_guard lock(mMutex);
+ if (mFuture.valid()) {
+ mFuture.wait();
+ mFuture = {};
+ }
+ }
+
+private:
+ std::mutex mMutex;
+ std::future<void> mFuture GUARDED_BY(mMutex);
+};
+
+} // namespace android::utils
diff --git a/services/surfaceflinger/common/Android.bp b/services/surfaceflinger/common/Android.bp
index f2ff00b..bcf1886 100644
--- a/services/surfaceflinger/common/Android.bp
+++ b/services/surfaceflinger/common/Android.bp
@@ -17,6 +17,7 @@
shared_libs: [
"libSurfaceFlingerProp",
"server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
],
static_libs: [
"librenderengine_includes",
@@ -35,6 +36,9 @@
],
static_libs: [
"libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
+ "android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
@@ -45,5 +49,38 @@
],
static_libs: [
"libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
+ "android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common",
+ "libsurfaceflingerflags",
+ "android.os.flags-aconfig-cc",
+ "android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
+ ],
+}
+
+cc_defaults {
+ name: "libsurfaceflinger_common_test_deps",
+ shared_libs: [
+ "server_configurable_flags",
+ "libaconfig_storage_read_api_cc",
+ ],
+ static_libs: [
+ "libsurfaceflinger_common_test",
+ "libsurfaceflingerflags_test",
+ "android.os.flags-aconfig-cc-test",
+ "android.server.display.flags-aconfig-cc",
+ "libguiflags_no_apex",
],
}
diff --git a/services/surfaceflinger/common/FlagManager.cpp b/services/surfaceflinger/common/FlagManager.cpp
index b7f06a9..2e3273c 100644
--- a/services/surfaceflinger/common/FlagManager.cpp
+++ b/services/surfaceflinger/common/FlagManager.cpp
@@ -26,7 +26,10 @@
#include <server_configurable_flags/get_flags.h>
#include <cinttypes>
+#include <android_os.h>
+#include <com_android_graphics_libgui_flags.h>
#include <com_android_graphics_surfaceflinger_flags.h>
+#include <com_android_server_display_feature_flags.h>
namespace android {
using namespace com::android::graphics::surfaceflinger;
@@ -109,10 +112,13 @@
/// Trunk stable server flags ///
DUMP_SERVER_FLAG(refresh_rate_overlay_on_external_display);
+ DUMP_SERVER_FLAG(adpf_gpu_sf);
+ DUMP_SERVER_FLAG(adpf_use_fmq_channel);
/// Trunk stable readonly flags ///
DUMP_READ_ONLY_FLAG(connected_display);
DUMP_READ_ONLY_FLAG(enable_small_area_detection);
+ DUMP_READ_ONLY_FLAG(frame_rate_category_mrr);
DUMP_READ_ONLY_FLAG(misc1);
DUMP_READ_ONLY_FLAG(vrr_config);
DUMP_READ_ONLY_FLAG(hotplug2);
@@ -129,9 +135,25 @@
DUMP_READ_ONLY_FLAG(screenshot_fence_preservation);
DUMP_READ_ONLY_FLAG(vulkan_renderengine);
DUMP_READ_ONLY_FLAG(renderable_buffer_usage);
+ DUMP_READ_ONLY_FLAG(vrr_bugfix_24q4);
DUMP_READ_ONLY_FLAG(restore_blur_step);
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);
+ DUMP_READ_ONLY_FLAG(commit_not_composited);
+ DUMP_READ_ONLY_FLAG(local_tonemap_screenshots);
+ DUMP_READ_ONLY_FLAG(override_trusted_overlay);
+ DUMP_READ_ONLY_FLAG(flush_buffer_slots_to_uncache);
+ DUMP_READ_ONLY_FLAG(force_compile_graphite_renderengine);
+ DUMP_READ_ONLY_FLAG(single_hop_screenshot);
+ DUMP_READ_ONLY_FLAG(trace_frame_rate_override);
+
#undef DUMP_READ_ONLY_FLAG
#undef DUMP_SERVER_FLAG
#undef DUMP_FLAG_INTERVAL
@@ -158,7 +180,7 @@
return getServerConfigurableFlag(serverFlagName); \
}
-#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted) \
+#define FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, checkForBootCompleted, owner) \
bool FlagManager::name() const { \
if (checkForBootCompleted) { \
LOG_ALWAYS_FATAL_IF(!mBootCompleted, \
@@ -166,21 +188,27 @@
__func__); \
} \
static const std::optional<bool> debugOverride = getBoolProperty(syspropOverride); \
- static const bool value = getFlagValue([] { return flags::name(); }, debugOverride); \
+ static const bool value = getFlagValue([] { return owner ::name(); }, debugOverride); \
if (mUnitTestMode) { \
/* \
* When testing, we don't want to rely on the cached `value` or the debugOverride. \
*/ \
- return flags::name(); \
+ return owner ::name(); \
} \
return value; \
}
#define FLAG_MANAGER_SERVER_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, true, flags)
#define FLAG_MANAGER_READ_ONLY_FLAG(name, syspropOverride) \
- FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false)
+ FLAG_MANAGER_FLAG_INTERNAL(name, syspropOverride, false, flags)
+
+#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, "", "")
@@ -192,6 +220,7 @@
/// Trunk stable readonly flags ///
FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "")
FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "")
+FLAG_MANAGER_READ_ONLY_FLAG(frame_rate_category_mrr, "debug.sf.frame_rate_category_mrr")
FLAG_MANAGER_READ_ONLY_FLAG(misc1, "")
FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config")
FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "")
@@ -205,15 +234,39 @@
FLAG_MANAGER_READ_ONLY_FLAG(display_protected, "")
FLAG_MANAGER_READ_ONLY_FLAG(fp16_client_target, "debug.sf.fp16_client_target")
FLAG_MANAGER_READ_ONLY_FLAG(game_default_frame_rate, "")
-FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "")
+FLAG_MANAGER_READ_ONLY_FLAG(enable_layer_command_batching, "debug.sf.enable_layer_command_batching")
FLAG_MANAGER_READ_ONLY_FLAG(screenshot_fence_preservation, "debug.sf.screenshot_fence_preservation")
FLAG_MANAGER_READ_ONLY_FLAG(vulkan_renderengine, "debug.renderengine.vulkan")
FLAG_MANAGER_READ_ONLY_FLAG(renderable_buffer_usage, "")
FLAG_MANAGER_READ_ONLY_FLAG(restore_blur_step, "debug.renderengine.restore_blur_step")
FLAG_MANAGER_READ_ONLY_FLAG(dont_skip_on_early_ro, "")
FLAG_MANAGER_READ_ONLY_FLAG(protected_if_client, "")
+FLAG_MANAGER_READ_ONLY_FLAG(vrr_bugfix_24q4, "");
+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, "");
+FLAG_MANAGER_READ_ONLY_FLAG(commit_not_composited, "");
+FLAG_MANAGER_READ_ONLY_FLAG(local_tonemap_screenshots, "debug.sf.local_tonemap_screenshots");
+FLAG_MANAGER_READ_ONLY_FLAG(override_trusted_overlay, "");
+FLAG_MANAGER_READ_ONLY_FLAG(flush_buffer_slots_to_uncache, "");
+FLAG_MANAGER_READ_ONLY_FLAG(force_compile_graphite_renderengine, "");
+FLAG_MANAGER_READ_ONLY_FLAG(single_hop_screenshot, "");
/// Trunk stable server flags ///
FLAG_MANAGER_SERVER_FLAG(refresh_rate_overlay_on_external_display, "")
+FLAG_MANAGER_SERVER_FLAG(adpf_gpu_sf, "")
+
+/// 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)
+FLAG_MANAGER_READ_ONLY_FLAG_IMPORTED(trace_frame_rate_override, "",
+ com::android::graphics::libgui::flags);
} // namespace android
diff --git a/services/surfaceflinger/common/include/common/FlagManager.h b/services/surfaceflinger/common/include/common/FlagManager.h
index 241c814..ab7a474 100644
--- a/services/surfaceflinger/common/include/common/FlagManager.h
+++ b/services/surfaceflinger/common/include/common/FlagManager.h
@@ -49,9 +49,13 @@
/// Trunk stable server flags ///
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;
+ bool frame_rate_category_mrr() const;
bool enable_small_area_detection() const;
bool misc1() const;
bool vrr_config() const;
@@ -68,10 +72,25 @@
bool enable_layer_command_batching() const;
bool screenshot_fence_preservation() const;
bool vulkan_renderengine() const;
+ bool vrr_bugfix_24q4() const;
bool renderable_buffer_usage() const;
bool restore_blur_step() const;
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;
+ bool commit_not_composited() const;
+ bool local_tonemap_screenshots() const;
+ bool override_trusted_overlay() const;
+ bool flush_buffer_slots_to_uncache() const;
+ bool force_compile_graphite_renderengine() const;
+ bool single_hop_screenshot() const;
+ bool trace_frame_rate_override() 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 550c70d..5317cbb 100644
--- a/services/surfaceflinger/common/include/common/test/FlagUtils.h
+++ b/services/surfaceflinger/common/include/common/test/FlagUtils.h
@@ -18,7 +18,14 @@
#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 {
class TestFlagSetter {
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 5174fa7..56bca7f 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -161,7 +161,7 @@
flag {
name: "graphite_renderengine"
namespace: "core_graphics"
- description: "Use Skia's Graphite Vulkan backend in RenderEngine."
+ description: "Compile AND enable Skia's Graphite Vulkan backend in RenderEngine. See also: force_compile_graphite_renderengine."
bug: "293371537"
is_fixed_read_only: true
}
@@ -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 5451752..f4d4ee9 100644
--- a/services/surfaceflinger/surfaceflinger_flags_new.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags_new.aconfig
@@ -4,10 +4,136 @@
container: "system"
flag {
- name: "dont_skip_on_early_ro2"
+ name: "adpf_gpu_sf"
+ namespace: "game"
+ description: "Guards use of the sending ADPF GPU duration hint and load hints from SurfaceFlinger to Power HAL"
+ bug: "284324521"
+} # adpf_gpu_sf
+
+flag {
+ name: "ce_fence_promise"
+ namespace: "window_surfaces"
+ description: "Moves logic for buffer release fences into LayerFE"
+ bug: "294936197"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # ce_fence_promise
+
+ flag {
+ name: "commit_not_composited"
+ namespace: "core_graphics"
+ description: "mark frames as non janky if the transaction resulted in no composition"
+ bug: "340633280"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # commit_not_composited
+
+ 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
+ }
+} # detached_mirror
+
+flag {
+ name: "flush_buffer_slots_to_uncache"
+ namespace: "core_graphics"
+ description: "Flush DisplayCommands for disabled displays in order to uncache requested buffers."
+ bug: "330806421"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # flush_buffer_slots_to_uncache
+
+flag {
+ name: "force_compile_graphite_renderengine"
+ namespace: "core_graphics"
+ description: "Compile Skia's Graphite Vulkan backend in RenderEngine, but do NOT enable it, unless graphite_renderengine is also set. It can also be enabled with the debug.renderengine.graphite system property for testing. In contrast, the graphite_renderengine flag both compiles AND enables Graphite in RenderEngine."
+ bug: "293371537"
+ is_fixed_read_only: true
+} # force_compile_graphite_renderengine
+
+flag {
+ name: "frame_rate_category_mrr"
+ namespace: "core_graphics"
+ description: "Enable to use frame rate category and newer frame rate votes such as GTE in SurfaceFlinger scheduler, to guard dVRR changes from MRR devices"
+ bug: "330224639"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # 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
+
+flag {
+ name: "local_tonemap_screenshots"
+ namespace: "core_graphics"
+ description: "Enables local tonemapping when capturing screenshots"
+ bug: "329464641"
+ is_fixed_read_only: true
+} # local_tonemap_screenshots
+
+flag {
+ name: "single_hop_screenshot"
+ namespace: "window_surfaces"
+ description: "Only access SF main thread once during a screenshot"
+ bug: "285553970"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+ } # single_hop_screenshot
+
+ flag {
+ name: "override_trusted_overlay"
+ namespace: "window_surfaces"
+ description: "Allow child to disable trusted overlay set by a parent layer"
+ bug: "339701674"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # override_trusted_overlay
+
+flag {
+ name: "vrr_bugfix_24q4"
+ namespace: "core_graphics"
+ description: "bug fixes for VRR"
+ bug: "331513837"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+} # vrr_bugfix_24q4
# IMPORTANT - please keep alphabetize to reduce merge conflicts
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index dab0a3f..38fc977 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -27,6 +27,7 @@
defaults: [
"android.hardware.graphics.common-ndk_shared",
"surfaceflinger_defaults",
+ "libsurfaceflinger_common_test_deps",
],
test_suites: ["device-tests"],
srcs: [
@@ -37,10 +38,10 @@
"DereferenceSurfaceControl_test.cpp",
"DisplayConfigs_test.cpp",
"DisplayEventReceiver_test.cpp",
+ "Dumpsys_test.cpp",
"EffectLayer_test.cpp",
"HdrSdrRatioOverlay_test.cpp",
"InvalidHandles_test.cpp",
- "LayerBorder_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
"LayerState_test.cpp",
@@ -66,7 +67,7 @@
static_libs: [
"liblayers_proto",
"android.hardware.graphics.composer@2.1",
- "libsurfaceflingerflags",
+ "libsurfaceflinger_common",
],
shared_libs: [
"android.hardware.graphics.common@1.2",
@@ -82,6 +83,7 @@
"libprotobuf-cpp-full",
"libui",
"libutils",
+ "server_configurable_flags",
],
header_libs: [
"libnativewindow_headers",
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 822ac4d..ebe11fb 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,13 +35,12 @@
namespace android {
using Transaction = SurfaceComposerClient::Transaction;
-using gui::LayerDebugInfo;
using gui::aidl_utils::statusTFromBinderStatus;
using ui::ColorMode;
namespace {
-const String8 DISPLAY_NAME("Credentials Display Test");
-const String8 SURFACE_NAME("Test Surface Name");
+const std::string kDisplayName("Credentials Display Test");
+const String8 kSurfaceName("Test Surface Name");
} // namespace
/**
@@ -101,7 +99,7 @@
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(mDisplay, &mode));
// Background surface
- mBGSurfaceControl = mComposerClient->createSurface(SURFACE_NAME, mode.resolution.getWidth(),
+ mBGSurfaceControl = mComposerClient->createSurface(kSurfaceName, mode.resolution.getWidth(),
mode.resolution.getHeight(),
PIXEL_FORMAT_RGBA_8888, 0);
ASSERT_TRUE(mBGSurfaceControl != nullptr);
@@ -234,14 +232,14 @@
TEST_F(CredentialsTest, CreateDisplayTest) {
// Only graphics and system processes can create a secure display.
std::function<bool()> condition = [=]() {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
+ sp<IBinder> testDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName, true);
return testDisplay.get() != nullptr;
};
// Check with root.
{
UIDFaker f(AID_ROOT);
- ASSERT_FALSE(condition());
+ ASSERT_TRUE(condition());
}
// Check as a Graphics user.
@@ -269,7 +267,7 @@
}
condition = [=]() {
- sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, false);
+ sp<IBinder> testDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName, false);
return testDisplay.get() != nullptr;
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges(condition, true, false));
@@ -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/Dumpsys_test.cpp b/services/surfaceflinger/tests/Dumpsys_test.cpp
new file mode 100644
index 0000000..c3914e5
--- /dev/null
+++ b/services/surfaceflinger/tests/Dumpsys_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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/native_window.h>
+#include <gtest/gtest.h>
+#include <gui/SurfaceComposerClient.h>
+#include "android-base/stringprintf.h"
+#include "utils/Errors.h"
+
+namespace android {
+
+namespace {
+status_t runShellCommand(const std::string& cmd, std::string& result) {
+ FILE* pipe = popen(cmd.c_str(), "r");
+ if (!pipe) {
+ return UNKNOWN_ERROR;
+ }
+
+ char buffer[1024];
+ while (fgets(buffer, sizeof(buffer), pipe) != NULL) {
+ result += buffer;
+ }
+
+ pclose(pipe);
+ return OK;
+}
+} // namespace
+
+using android::hardware::graphics::common::V1_1::BufferUsage;
+
+class WaitForCompletedCallback {
+public:
+ WaitForCompletedCallback() = default;
+ ~WaitForCompletedCallback() = default;
+
+ static void transactionCompletedCallback(void* callbackContext, nsecs_t /* latchTime */,
+ const sp<Fence>& /* presentFence */,
+ const std::vector<SurfaceControlStats>& /* stats */) {
+ ASSERT_NE(callbackContext, nullptr) << "failed to get callback context";
+ WaitForCompletedCallback* context = static_cast<WaitForCompletedCallback*>(callbackContext);
+ context->notify();
+ }
+
+ void wait() {
+ std::unique_lock lock(mMutex);
+ cv.wait(lock, [this] { return mCallbackReceived; });
+ }
+
+ void notify() {
+ std::unique_lock lock(mMutex);
+ mCallbackReceived = true;
+ cv.notify_one();
+ }
+
+private:
+ std::mutex mMutex;
+ std::condition_variable cv;
+ bool mCallbackReceived = false;
+};
+
+TEST(Dumpsys, listLayers) {
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+ ASSERT_EQ(NO_ERROR, client->initCheck());
+ auto newLayer =
+ client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0);
+ std::string layersAsString;
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --list", layersAsString));
+ EXPECT_NE(strstr(layersAsString.c_str(), ""), nullptr);
+}
+
+TEST(Dumpsys, stats) {
+ sp<SurfaceComposerClient> client = sp<SurfaceComposerClient>::make();
+ ASSERT_EQ(NO_ERROR, client->initCheck());
+ auto newLayer =
+ client->createSurface(String8("MY_TEST_LAYER"), 100, 100, PIXEL_FORMAT_RGBA_8888, 0);
+ uint64_t usageFlags = BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN |
+ BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE;
+
+ sp<GraphicBuffer> buffer =
+ sp<GraphicBuffer>::make(1u, 1u, PIXEL_FORMAT_RGBA_8888, 1u, usageFlags, "test");
+
+ WaitForCompletedCallback callback;
+ SurfaceComposerClient::Transaction()
+ .setBuffer(newLayer, buffer)
+ .addTransactionCompletedCallback(WaitForCompletedCallback::transactionCompletedCallback,
+ &callback)
+ .apply();
+ callback.wait();
+ std::string stats;
+ std::string layerName = base::StringPrintf("MY_TEST_LAYER#%d", newLayer->getLayerId());
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency " + layerName, stats));
+ EXPECT_NE(std::string(""), stats);
+ EXPECT_EQ(OK, runShellCommand("dumpsys SurfaceFlinger --latency-clear " + layerName, stats));
+}
+
+} // namespace android
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/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 79886bd..b4496d3 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -1295,4 +1295,74 @@
}
}
+TEST_F(LayerCallbackTest, OccludedLayerHasReleaseCallback) {
+ sp<SurfaceControl> layer1, layer2;
+ ASSERT_NO_FATAL_FAILURE(layer1 = createLayerWithBuffer());
+ ASSERT_NO_FATAL_FAILURE(layer2 = createLayerWithBuffer());
+
+ Transaction transaction1, transaction2;
+ CallbackHelper callback1a, callback1b, callback2a, callback2b;
+ int err = fillTransaction(transaction1, &callback1a, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2a, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ ui::Size bufferSize = getBufferSize();
+
+ // Occlude layer1 with layer2
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ transaction1.apply();
+ transaction2.apply();
+
+ ExpectedResult expected1a, expected1b, expected2a, expected2b;
+ expected1a.addSurface(ExpectedResult::Transaction::PRESENTED, {layer1},
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+
+ expected2a.addSurface(ExpectedResult::Transaction::PRESENTED, {layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::NOT_RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1a, expected1a, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2a, expected2a, true));
+
+ // Submit new buffers so previous buffers can be released
+ err = fillTransaction(transaction1, &callback1b, layer1);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+ err = fillTransaction(transaction2, &callback2b, layer2);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ TransactionUtils::setFrame(transaction1, layer1,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ TransactionUtils::setFrame(transaction2, layer2,
+ Rect(0, 0, bufferSize.width, bufferSize.height), Rect(0, 0, 32, 32));
+ transaction1.apply();
+ transaction2.apply();
+
+ expected1b.addSurface(ExpectedResult::Transaction::PRESENTED, {layer1},
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+
+ expected2b.addSurface(ExpectedResult::Transaction::PRESENTED, {layer2},
+ ExpectedResult::Buffer::ACQUIRED,
+ ExpectedResult::PreviousBuffer::RELEASED);
+
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback1b, expected1b, true));
+ EXPECT_NO_FATAL_FAILURE(waitForCallback(callback2b, expected2b, true));
+}
} // namespace android
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/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 0ea0824..d97d433 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -19,6 +19,7 @@
#pragma clang diagnostic ignored "-Wconversion"
#include <android-base/properties.h>
+#include <common/FlagManager.h>
#include <private/android_filesystem_config.h>
#include "LayerTransactionTest.h"
#include "utils/TransactionUtils.h"
@@ -78,6 +79,10 @@
.show(mirrorLayer)
.apply();
+ if (FlagManager::getInstance().detached_mirror()) {
+ Transaction().setPosition(mirrorLayer, 550, 550).apply();
+ }
+
{
SCOPED_TRACE("Initial Mirror");
auto shot = screenshot();
@@ -172,6 +177,9 @@
.show(mirrorLayer)
.apply();
+ if (FlagManager::getInstance().detached_mirror()) {
+ Transaction().setPosition(mirrorLayer, 550, 550).apply();
+ }
{
SCOPED_TRACE("Initial Mirror BufferQueueLayer");
auto shot = screenshot();
@@ -263,6 +271,9 @@
.setLayer(mirrorLayer, INT32_MAX - 1)
.apply();
+ if (FlagManager::getInstance().detached_mirror()) {
+ Transaction().setPosition(mirrorLayer, 550, 550).apply();
+ }
{
SCOPED_TRACE("Offscreen Mirror");
auto shot = screenshot();
@@ -313,8 +324,15 @@
ASSERT_NE(mirrorLayer, nullptr);
}
+ sp<SurfaceControl> mirrorParent =
+ createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState);
+
// Show the mirror layer, but don't reparent to a layer on screen.
- Transaction().show(mirrorLayer).apply();
+ Transaction().reparent(mirrorLayer, mirrorParent).show(mirrorLayer).apply();
+
+ if (FlagManager::getInstance().detached_mirror()) {
+ Transaction().setPosition(mirrorLayer, 50, 50).apply();
+ }
{
SCOPED_TRACE("Offscreen Mirror");
@@ -331,7 +349,7 @@
SCOPED_TRACE("Capture Mirror");
// Capture just the mirror layer and child.
LayerCaptureArgs captureArgs;
- captureArgs.layerHandle = mirrorLayer->getHandle();
+ captureArgs.layerHandle = mirrorParent->getHandle();
captureArgs.sourceCrop = childBounds;
std::unique_ptr<ScreenCapture> shot;
ScreenCapture::captureLayers(&shot, captureArgs);
diff --git a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
index 15ff696..56cf13d 100644
--- a/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
+++ b/services/surfaceflinger/tests/MultiDisplayLayerBounds_test.cpp
@@ -18,9 +18,12 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wconversion"
+#include <common/FlagManager.h>
#include <ui/DisplayState.h>
#include "LayerTransactionTest.h"
+#include "gui/SurfaceComposerClient.h"
+#include "ui/DisplayId.h"
namespace android {
@@ -37,7 +40,8 @@
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
ASSERT_FALSE(ids.empty());
- mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ mMainDisplayId = ids.front();
+ mMainDisplay = SurfaceComposerClient::getPhysicalDisplayToken(mMainDisplayId);
SurfaceComposerClient::getDisplayState(mMainDisplay, &mMainDisplayState);
SurfaceComposerClient::getActiveDisplayMode(mMainDisplay, &mMainDisplayMode);
@@ -49,14 +53,15 @@
}
virtual void TearDown() {
- SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::destroyVirtualDisplay(mVirtualDisplay));
LayerTransactionTest::TearDown();
mColorLayer = 0;
}
void createDisplay(const ui::Size& layerStackSize, ui::LayerStack layerStack) {
+ static const std::string kDisplayName("VirtualDisplay");
mVirtualDisplay =
- SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName, false /*isSecure*/);
asTransaction([&](Transaction& t) {
t.setDisplaySurface(mVirtualDisplay, mProducer);
t.setDisplayLayerStack(mVirtualDisplay, layerStack);
@@ -85,6 +90,7 @@
ui::DisplayState mMainDisplayState;
ui::DisplayMode mMainDisplayMode;
sp<IBinder> mMainDisplay;
+ PhysicalDisplayId mMainDisplayId;
sp<IBinder> mVirtualDisplay;
sp<IGraphicBufferProducer> mProducer;
sp<SurfaceControl> mColorLayer;
@@ -119,6 +125,9 @@
createDisplay(mMainDisplayState.layerStackSpaceRect, ui::DEFAULT_LAYER_STACK);
createColorLayer(ui::DEFAULT_LAYER_STACK);
+ sp<SurfaceControl> mirrorSc =
+ SurfaceComposerClient::getDefault()->mirrorDisplay(mMainDisplayId);
+
asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
// Verify color layer renders correctly on main display and it is mirrored on the
@@ -133,6 +142,37 @@
sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
}
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerWithPromisedFenceInMirroredVirtualDisplay) {
+ // Create a display and use a unique layerstack ID for mirrorDisplay() so
+ // the contents of the main display are mirrored on to the virtual display.
+
+ // A unique layerstack ID must be used because sharing the same layerFE
+ // with more than one display is unsupported. A unique layerstack ensures
+ // that a different layerFE is used between displays.
+ constexpr ui::LayerStack layerStack{77687666}; // ASCII for MDLB (MultiDisplayLayerBounds)
+ createDisplay(mMainDisplayState.layerStackSpaceRect, layerStack);
+ createColorLayer(ui::DEFAULT_LAYER_STACK);
+
+ sp<SurfaceControl> mirrorSc =
+ SurfaceComposerClient::getDefault()->mirrorDisplay(mMainDisplayId);
+
+ asTransaction([&](Transaction& t) {
+ t.setPosition(mColorLayer, 10, 10);
+ t.setLayerStack(mirrorSc, layerStack);
+ });
+
+ // Verify color layer renders correctly on main display and it is mirrored on the
+ // virtual display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
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/TransactionTestHarnesses.h b/services/surfaceflinger/tests/TransactionTestHarnesses.h
index 797a64c..af3cb9a 100644
--- a/services/surfaceflinger/tests/TransactionTestHarnesses.h
+++ b/services/surfaceflinger/tests/TransactionTestHarnesses.h
@@ -16,9 +16,11 @@
#ifndef ANDROID_TRANSACTION_TEST_HARNESSES
#define ANDROID_TRANSACTION_TEST_HARNESSES
+#include <common/FlagManager.h>
#include <ui/DisplayState.h>
#include "LayerTransactionTest.h"
+#include "ui/LayerStack.h"
namespace android {
@@ -36,9 +38,10 @@
case RenderPath::VIRTUAL_DISPLAY:
const auto ids = SurfaceComposerClient::getPhysicalDisplayIds();
+ const PhysicalDisplayId displayId = ids.front();
const auto displayToken = ids.empty()
? nullptr
- : SurfaceComposerClient::getPhysicalDisplayToken(ids.front());
+ : SurfaceComposerClient::getPhysicalDisplayToken(displayId);
ui::DisplayState displayState;
SurfaceComposerClient::getDisplayState(displayToken, &displayState);
@@ -63,14 +66,25 @@
sp<BufferListener> listener = sp<BufferListener>::make(this);
itemConsumer->setFrameAvailableListener(listener);
- vDisplay = SurfaceComposerClient::createDisplay(String8("VirtualDisplay"),
- false /*secure*/);
+ static const std::string kDisplayName("VirtualDisplay");
+ vDisplay = SurfaceComposerClient::createVirtualDisplay(kDisplayName,
+ false /*isSecure*/);
+
+ constexpr ui::LayerStack layerStack{
+ 848472}; // ASCII for TTH (TransactionTestHarnesses)
+ sp<SurfaceControl> mirrorSc =
+ SurfaceComposerClient::getDefault()->mirrorDisplay(displayId);
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(vDisplay, producer);
- t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
t.setDisplayProjection(vDisplay, displayState.orientation,
Rect(displayState.layerStackSpaceRect), Rect(resolution));
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ t.setDisplayLayerStack(vDisplay, layerStack);
+ t.setLayerStack(mirrorSc, layerStack);
+ } else {
+ t.setDisplayLayerStack(vDisplay, ui::DEFAULT_LAYER_STACK);
+ }
t.apply();
SurfaceComposerClient::Transaction().apply(true);
@@ -85,7 +99,16 @@
constexpr bool kContainsHdr = false;
auto sc = std::make_unique<ScreenCapture>(item.mGraphicBuffer, kContainsHdr);
itemConsumer->releaseBuffer(item);
- SurfaceComposerClient::destroyDisplay(vDisplay);
+
+ // Possible race condition with destroying virtual displays, in which
+ // CompositionEngine::present may attempt to be called on the same
+ // display multiple times. The layerStack is set to invalid here so
+ // that the display is ignored if that scenario occurs.
+ if (FlagManager::getInstance().ce_fence_promise()) {
+ t.setLayerStack(mirrorSc, ui::INVALID_LAYER_STACK);
+ t.apply(true);
+ }
+ SurfaceComposerClient::destroyVirtualDisplay(vDisplay);
return sc;
}
}
diff --git a/services/surfaceflinger/tests/VirtualDisplay_test.cpp b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
index f31f582..cd66dd2 100644
--- a/services/surfaceflinger/tests/VirtualDisplay_test.cpp
+++ b/services/surfaceflinger/tests/VirtualDisplay_test.cpp
@@ -41,14 +41,15 @@
};
TEST_F(VirtualDisplayTest, VirtualDisplayDestroyedSurfaceReuse) {
+ static const std::string kDisplayName("VirtualDisplay");
sp<IBinder> virtualDisplay =
- SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ SurfaceComposerClient::createVirtualDisplay(kDisplayName, false /*isSecure*/);
SurfaceComposerClient::Transaction t;
t.setDisplaySurface(virtualDisplay, mProducer);
t.apply(true);
- SurfaceComposerClient::destroyDisplay(virtualDisplay);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::destroyVirtualDisplay(virtualDisplay));
virtualDisplay.clear();
// Sync here to ensure the display was completely destroyed in SF
t.apply(true);
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index f529f7c..98d5754 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -29,7 +29,7 @@
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockHWC2.cpp",
"mock/DisplayHardware/MockIPower.cpp",
- "mock/DisplayHardware/MockIPowerHintSession.cpp",
+ "mock/DisplayHardware/MockPowerHintSessionWrapper.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
@@ -66,12 +66,13 @@
"BackgroundExecutorTest.cpp",
"CommitTest.cpp",
"CompositionTest.cpp",
+ "DaltonizerTest.cpp",
"DisplayIdGeneratorTest.cpp",
"DisplayTransactionTest.cpp",
"DisplayDevice_GetBestColorModeTest.cpp",
- "DisplayDevice_InitiateModeChange.cpp",
"DisplayDevice_SetDisplayBrightnessTest.cpp",
"DisplayDevice_SetProjectionTest.cpp",
+ "DisplayModeControllerTest.cpp",
"EventThreadTest.cpp",
"FlagManagerTest.cpp",
"FpsReporterTest.cpp",
@@ -149,6 +150,7 @@
"android.hardware.graphics.composer3-ndk_static",
"android.hardware.power-ndk_static",
"librenderengine_deps",
+ "libsurfaceflinger_common_test_deps",
],
static_libs: [
"android.hardware.common-V2-ndk",
@@ -173,13 +175,11 @@
"librenderengine_mocks",
"libscheduler",
"libserviceutils",
- "libsurfaceflinger_common_test",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
"libtonemap",
"perfetto_trace_protos",
- "libsurfaceflingerflags_test",
],
shared_libs: [
"android.hardware.configstore-utils",
@@ -208,7 +208,6 @@
"libsync",
"libui",
"libutils",
- "server_configurable_flags",
],
header_libs: [
"android.hardware.graphics.composer3-command-buffer",
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..cdd77fe 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -70,6 +70,7 @@
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
+using namespace ftl::flag_operators;
constexpr hal::HWDisplayId HWC_DISPLAY = FakeHwcDisplayInjector::DEFAULT_HWC_DISPLAY_ID;
constexpr hal::HWLayerId HWC_LAYER = 5000;
@@ -197,15 +198,19 @@
const Rect sourceCrop(0, 0, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT);
constexpr bool regionSampling = false;
- auto renderArea = DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
- ui::Dataspace::V0_SRGB, true, true);
+ auto renderArea =
+ DisplayRenderArea::create(mDisplay, sourceCrop, sourceCrop.getSize(),
+ ui::Dataspace::V0_SRGB,
+ RenderArea::Options::CAPTURE_SECURE_LAYERS |
+ RenderArea::Options::HINT_FOR_SEAMLESS_TRANSITION);
auto traverseLayers = [this](const LayerVector::Visitor& visitor) {
return mFlinger.traverseLayersInLayerStack(mDisplay->getLayerStack(),
CaptureArgs::UNSET_UID, {}, visitor);
};
- auto getLayerSnapshots = RenderArea::fromTraverseLayersLambda(traverseLayers);
+ // TODO: Use SurfaceFlinger::getLayerSnapshotsForScreenshots instead of this legacy function
+ auto getLayerSnapshotsFn = RenderArea::fromTraverseLayersLambda(traverseLayers);
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
@@ -215,7 +220,7 @@
HAL_PIXEL_FORMAT_RGBA_8888, 1,
usage);
- auto future = mFlinger.renderScreenImpl(std::move(renderArea), getLayerSnapshots,
+ auto future = mFlinger.renderScreenImpl(mDisplay, std::move(renderArea), getLayerSnapshotsFn,
mCaptureScreenBuffer, regionSampling);
ASSERT_TRUE(future.valid());
const auto fenceResult = future.get();
@@ -282,7 +287,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/DaltonizerTest.cpp b/services/surfaceflinger/tests/unittests/DaltonizerTest.cpp
new file mode 100644
index 0000000..9f632a1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DaltonizerTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright 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.
+ */
+
+#include <gtest/gtest.h>
+#include <math/mat4.h>
+#include <cmath>
+#include "Effects/Daltonizer.h"
+
+namespace android {
+
+class DaltonizerTest {
+private:
+ Daltonizer& mDaltonizer;
+
+public:
+ DaltonizerTest(Daltonizer& daltonizer) : mDaltonizer(daltonizer) {}
+
+ bool isDirty() const { return mDaltonizer.mDirty; }
+
+ float getLevel() const { return mDaltonizer.mLevel; }
+
+ ColorBlindnessType getType() const { return mDaltonizer.mType; }
+};
+
+constexpr float TOLERANCE = 0.01f;
+
+static bool isIdentityMatrix(mat4& matrix) {
+ for (size_t i = 0; i < 4; ++i) {
+ for (size_t j = 0; j < 4; ++j) {
+ if (i == j) {
+ // Check diagonal elements
+ if (std::fabs(matrix[i][j] - 1.0f) > TOLERANCE) {
+ return false;
+ }
+ } else {
+ // Check off-diagonal elements
+ if (std::fabs(matrix[i][j]) > TOLERANCE) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// Test Suite Name : DaltonizerTest, Test name: ConstructionDefaultValues
+TEST(DaltonizerTest, ConstructionDefaultValues) {
+ Daltonizer daltonizer;
+ DaltonizerTest test(daltonizer);
+
+ EXPECT_EQ(test.getLevel(), 0.7f);
+ ASSERT_TRUE(test.isDirty());
+ EXPECT_EQ(test.getType(), ColorBlindnessType::None);
+ mat4 matrix = daltonizer();
+ ASSERT_TRUE(isIdentityMatrix(matrix));
+}
+
+TEST(DaltonizerTest, NotDirtyAfterColorMatrixReturned) {
+ Daltonizer daltonizer;
+
+ mat4 matrix = daltonizer();
+ DaltonizerTest test(daltonizer);
+
+ ASSERT_FALSE(test.isDirty());
+ ASSERT_TRUE(isIdentityMatrix(matrix));
+}
+
+TEST(DaltonizerTest, LevelOutOfRangeTooLowIgnored) {
+ Daltonizer daltonizer;
+ // Get matrix to reset isDirty == false.
+ mat4 matrix = daltonizer();
+
+ daltonizer.setLevel(-1);
+ DaltonizerTest test(daltonizer);
+
+ EXPECT_EQ(test.getLevel(), 0.7f);
+ ASSERT_FALSE(test.isDirty());
+}
+
+TEST(DaltonizerTest, LevelOutOfRangeTooHighIgnored) {
+ Daltonizer daltonizer;
+ // Get matrix to reset isDirty == false.
+ mat4 matrix = daltonizer();
+
+ daltonizer.setLevel(11);
+ DaltonizerTest test(daltonizer);
+
+ EXPECT_EQ(test.getLevel(), 0.7f);
+ ASSERT_FALSE(test.isDirty());
+}
+
+TEST(DaltonizerTest, ColorCorrectionMatrixNonIdentical) {
+ Daltonizer daltonizer;
+ daltonizer.setType(ColorBlindnessType::Protanomaly);
+ daltonizer.setMode(ColorBlindnessMode::Correction);
+
+ mat4 matrix = daltonizer();
+
+ ASSERT_FALSE(isIdentityMatrix(matrix));
+}
+
+TEST(DaltonizerTest, LevelZeroColorMatrixEqIdentityMatrix) {
+ Daltonizer daltonizer;
+ daltonizer.setType(ColorBlindnessType::Protanomaly);
+ daltonizer.setMode(ColorBlindnessMode::Correction);
+ daltonizer.setLevel(0);
+
+ mat4 matrix = daltonizer();
+
+ ASSERT_TRUE(isIdentityMatrix(matrix));
+}
+
+} /* namespace android */
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
deleted file mode 100644
index c463a92..0000000
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "LibSurfaceFlingerUnittests"
-
-#include "DisplayTransactionTestHelpers.h"
-#include "mock/MockFrameRateMode.h"
-
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-
-#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
- ASSERT_TRUE(requestOpt); \
- EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
- EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
-
-namespace android {
-namespace {
-
-using FakeDisplayDeviceInjector = TestableSurfaceFlinger::FakeDisplayDeviceInjector;
-using DisplayModeRequest = display::DisplayModeRequest;
-
-class InitiateModeChangeTest : public DisplayTransactionTest {
-public:
- using Action = DisplayDevice::DesiredModeAction;
- void SetUp() override {
- injectFakeBufferQueueFactory();
- injectFakeNativeWindowSurfaceFactory();
-
- PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
- PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
- PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
- PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
- PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
-
- mFlinger.onComposerHalHotplugEvent(PrimaryDisplayVariant::HWC_DISPLAY_ID,
- DisplayHotplugEvent::CONNECTED);
- mFlinger.configureAndCommit();
-
- mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
- .setDisplayModes(makeModes(kMode60, kMode90, kMode120), kModeId60)
- .inject();
- }
-
-protected:
- sp<DisplayDevice> mDisplay;
-
- static constexpr DisplayModeId kModeId60{0};
- static constexpr DisplayModeId kModeId90{1};
- static constexpr DisplayModeId kModeId120{2};
-
- static inline const ftl::NonNull<DisplayModePtr> kMode60 =
- ftl::as_non_null(createDisplayMode(kModeId60, 60_Hz));
- static inline const ftl::NonNull<DisplayModePtr> kMode90 =
- ftl::as_non_null(createDisplayMode(kModeId90, 90_Hz));
- static inline const ftl::NonNull<DisplayModePtr> kMode120 =
- ftl::as_non_null(createDisplayMode(kModeId120, 120_Hz));
-
- static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false};
- static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true};
- static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false};
- static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true};
-};
-
-TEST_F(InitiateModeChangeTest, setDesiredModeToActiveMode) {
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode60)));
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, setDesiredMode) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, clearDesiredMode) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_TRUE(mDisplay->getDesiredMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateModeChange) REQUIRES(kMainThreadContext) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- const hal::VsyncPeriodChangeConstraints constraints{
- .desiredTimeNanos = systemTime(),
- .seamlessRequired = false,
- };
- hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateRenderRateSwitch) {
- EXPECT_EQ(Action::InitiateRenderRateSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode30)));
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-TEST_F(InitiateModeChangeTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
- EXPECT_EQ(Action::InitiateDisplayModeSwitch,
- mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode90)));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getDesiredMode());
-
- const hal::VsyncPeriodChangeConstraints constraints{
- .desiredTimeNanos = systemTime(),
- .seamlessRequired = false,
- };
- hal::VsyncPeriodChangeTimeline timeline;
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- EXPECT_EQ(Action::None, mDisplay->setDesiredMode(DisplayModeRequest(kDesiredMode120)));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getDesiredMode());
-
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDisplay->getPendingMode());
-
- EXPECT_TRUE(mDisplay->initiateModeChange(*mDisplay->getDesiredMode(), constraints, timeline));
- EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDisplay->getPendingMode());
-
- mDisplay->clearDesiredMode();
- EXPECT_FALSE(mDisplay->getDesiredMode());
-}
-
-} // namespace
-} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
new file mode 100644
index 0000000..d971150
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/DisplayModeControllerTest.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "Display/DisplayModeController.h"
+#include "Display/DisplaySnapshot.h"
+#include "DisplayHardware/HWComposer.h"
+#include "DisplayIdentificationTestHelpers.h"
+#include "FpsOps.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockDisplayMode.h"
+#include "mock/MockFrameRateMode.h"
+
+#include <ftl/fake_guard.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#define EXPECT_DISPLAY_MODE_REQUEST(expected, requestOpt) \
+ ASSERT_TRUE(requestOpt); \
+ EXPECT_FRAME_RATE_MODE(expected.mode.modePtr, expected.mode.fps, requestOpt->mode); \
+ EXPECT_EQ(expected.emitEvent, requestOpt->emitEvent)
+
+namespace android::display {
+namespace {
+
+namespace hal = android::hardware::graphics::composer::hal;
+
+using testing::_;
+using testing::DoAll;
+using testing::Return;
+using testing::SetArgPointee;
+
+class DisplayModeControllerTest : public testing::Test {
+public:
+ using Action = DisplayModeController::DesiredModeAction;
+
+ void SetUp() override {
+ mDmc.setHwComposer(mComposer.get());
+ mDmc.setActiveModeListener(
+ [this](PhysicalDisplayId displayId, Fps vsyncRate, Fps renderFps) {
+ mActiveModeListener.Call(displayId, vsyncRate, renderFps);
+ });
+
+ constexpr uint8_t kPort = 111;
+ EXPECT_CALL(*mComposerHal, getDisplayIdentificationData(kHwcDisplayId, _, _))
+ .WillOnce(DoAll(SetArgPointee<1>(kPort), SetArgPointee<2>(getInternalEdid()),
+ Return(hal::Error::NONE)));
+
+ EXPECT_CALL(*mComposerHal, setClientTargetSlotCount(kHwcDisplayId));
+ EXPECT_CALL(*mComposerHal,
+ setVsyncEnabled(kHwcDisplayId, hal::IComposerClient::Vsync::DISABLE));
+ EXPECT_CALL(*mComposerHal, onHotplugConnect(kHwcDisplayId));
+
+ const auto infoOpt = mComposer->onHotplug(kHwcDisplayId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(infoOpt);
+
+ mDisplayId = infoOpt->id;
+ mDisplaySnapshotOpt.emplace(mDisplayId, ui::DisplayConnectionType::Internal,
+ makeModes(kMode60, kMode90, kMode120), ui::ColorModes{},
+ std::nullopt);
+
+ ftl::FakeGuard guard(kMainThreadContext);
+ mDmc.registerDisplay(*mDisplaySnapshotOpt, kModeId60,
+ scheduler::RefreshRateSelector::Config{});
+ }
+
+protected:
+ hal::VsyncPeriodChangeConstraints expectModeSet(const DisplayModeRequest& request,
+ hal::VsyncPeriodChangeTimeline& timeline,
+ bool subsequent = false) {
+ EXPECT_CALL(*mComposerHal,
+ isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+ .WillOnce(Return(true));
+
+ if (!subsequent) {
+ EXPECT_CALL(*mComposerHal, getDisplayConnectionType(kHwcDisplayId, _))
+ .WillOnce(DoAll(SetArgPointee<1>(
+ hal::IComposerClient::DisplayConnectionType::INTERNAL),
+ Return(hal::V2_4::Error::NONE)));
+ }
+
+ const hal::VsyncPeriodChangeConstraints constraints{
+ .desiredTimeNanos = systemTime(),
+ .seamlessRequired = false,
+ };
+
+ const hal::HWConfigId hwcModeId = request.mode.modePtr->getHwcId();
+
+ EXPECT_CALL(*mComposerHal,
+ setActiveConfigWithConstraints(kHwcDisplayId, hwcModeId, constraints, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(hal::V2_4::Error::NONE)));
+
+ return constraints;
+ }
+
+ static constexpr hal::HWDisplayId kHwcDisplayId = 1234;
+
+ Hwc2::mock::Composer* mComposerHal = new testing::StrictMock<Hwc2::mock::Composer>();
+ const std::unique_ptr<HWComposer> mComposer{
+ std::make_unique<impl::HWComposer>(std::unique_ptr<Hwc2::Composer>(mComposerHal))};
+
+ testing::MockFunction<void(PhysicalDisplayId, Fps, Fps)> mActiveModeListener;
+
+ DisplayModeController mDmc;
+
+ PhysicalDisplayId mDisplayId;
+ std::optional<DisplaySnapshot> mDisplaySnapshotOpt;
+
+ static constexpr DisplayModeId kModeId60{0};
+ static constexpr DisplayModeId kModeId90{1};
+ static constexpr DisplayModeId kModeId120{2};
+
+ static inline const ftl::NonNull<DisplayModePtr> kMode60 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId60, 60_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode90 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId90, 90_Hz));
+ static inline const ftl::NonNull<DisplayModePtr> kMode120 =
+ ftl::as_non_null(mock::createDisplayMode(kModeId120, 120_Hz));
+
+ static inline const DisplayModeRequest kDesiredMode30{{30_Hz, kMode60}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode60{{60_Hz, kMode60}, .emitEvent = true};
+ static inline const DisplayModeRequest kDesiredMode90{{90_Hz, kMode90}, .emitEvent = false};
+ static inline const DisplayModeRequest kDesiredMode120{{120_Hz, kMode120}, .emitEvent = true};
+};
+
+TEST_F(DisplayModeControllerTest, setDesiredModeToActiveMode) {
+ EXPECT_CALL(mActiveModeListener, Call(_, _, _)).Times(0);
+
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode60)));
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, setDesiredMode) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+
+ // No action since a mode switch has already been initiated.
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode120)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, clearDesiredMode) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_TRUE(mDmc.getDesiredMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateModeChange) REQUIRES(kMainThreadContext) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+ auto modeRequest = kDesiredMode90;
+
+ hal::VsyncPeriodChangeTimeline timeline;
+ const auto constraints = expectModeSet(modeRequest, timeline);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateRenderRateSwitch) {
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 30_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateRenderRateSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode30)));
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+TEST_F(DisplayModeControllerTest, initiateDisplayModeSwitch) FTL_FAKE_GUARD(kMainThreadContext) {
+ // Called because setDesiredMode resets the render rate to the active refresh rate.
+ EXPECT_CALL(mActiveModeListener, Call(mDisplayId, 60_Hz, 60_Hz)).Times(1);
+
+ EXPECT_EQ(Action::InitiateDisplayModeSwitch,
+ mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode90)));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getDesiredMode(mDisplayId));
+ auto modeRequest = kDesiredMode90;
+
+ hal::VsyncPeriodChangeTimeline timeline;
+ auto constraints = expectModeSet(modeRequest, timeline);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+
+ // No action since a mode switch has already been initiated.
+ EXPECT_EQ(Action::None, mDmc.setDesiredMode(mDisplayId, DisplayModeRequest(kDesiredMode120)));
+
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode90, mDmc.getPendingMode(mDisplayId));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getDesiredMode(mDisplayId));
+ modeRequest = kDesiredMode120;
+
+ constexpr bool kSubsequent = true;
+ constraints = expectModeSet(modeRequest, timeline, kSubsequent);
+
+ EXPECT_TRUE(mDmc.initiateModeChange(mDisplayId, std::move(modeRequest), constraints, timeline));
+ EXPECT_DISPLAY_MODE_REQUEST(kDesiredMode120, mDmc.getPendingMode(mDisplayId));
+
+ mDmc.clearDesiredMode(mDisplayId);
+ EXPECT_FALSE(mDmc.getDesiredMode(mDisplayId));
+}
+
+} // namespace
+} // namespace android::display
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/FlagManagerTest.cpp b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
index 0adf0b6..51b5f40 100644
--- a/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FlagManagerTest.cpp
@@ -85,7 +85,7 @@
EXPECT_EQ(false, mFlagManager.test_flag());
}
-TEST_F(FlagManagerTest, creashesIfQueriedBeforeBoot) {
+TEST_F(FlagManagerTest, crashesIfQueriedBeforeBoot) {
mFlagManager.markBootIncomplete();
EXPECT_DEATH(FlagManager::getInstance()
.refresh_rate_overlay_on_external_display(), "");
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index ddc3967..dac9265 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -341,6 +341,57 @@
EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt);
}
+TEST_F(FrameTimelineTest, displayFrameSkippedComposition) {
+ // Layer specific increment
+ EXPECT_CALL(*mTimeStats, incrementJankyFrames(_)).Times(1);
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({10, 20, 30});
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 26, 30});
+ FrameTimelineInfo ftInfo;
+ ftInfo.vsyncId = surfaceFrameToken1;
+ ftInfo.inputEventId = sInputEventId;
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo, sPidOne, sUidOne, sLayerIdTwo,
+ sLayerNameTwo, sLayerNameTwo,
+ /*isBuffer*/ true, sGameMode);
+
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->onCommitNotComposited();
+
+ EXPECT_EQ(surfaceFrame1->getActuals().presentTime, 30);
+ ASSERT_NE(surfaceFrame1->getJankType(), std::nullopt);
+ EXPECT_EQ(*surfaceFrame1->getJankType(), JankType::None);
+ ASSERT_NE(surfaceFrame1->getJankSeverityType(), std::nullopt);
+ EXPECT_EQ(*surfaceFrame1->getJankSeverityType(), JankSeverityType::None);
+
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_11);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(26, presentFence1);
+
+ auto displayFrame = getDisplayFrame(0);
+ auto& presentedSurfaceFrame2 = getSurfaceFrame(0, 0);
+ presentFence1->signalForTest(42);
+
+ // Fences haven't been flushed yet, so it should be 0
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 0);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 0);
+
+ addEmptyDisplayFrame();
+
+ // Fences have flushed, so the present timestamps should be updated
+ EXPECT_EQ(displayFrame->getActuals().presentTime, 42);
+ EXPECT_EQ(presentedSurfaceFrame2.getActuals().presentTime, 42);
+ EXPECT_NE(surfaceFrame2->getJankType(), std::nullopt);
+ EXPECT_NE(surfaceFrame2->getJankSeverityType(), std::nullopt);
+}
+
TEST_F(FrameTimelineTest, displayFramesSlidingWindowMovesAfterLimit) {
// Insert kMaxDisplayFrames' count of DisplayFrames to fill the deque
int frameTimeFactor = 0;
@@ -1081,6 +1132,72 @@
EXPECT_EQ(received.cookie(), source.cookie());
}
+TEST_F(FrameTimelineTest, traceDisplayFrameNoSkipped) {
+ // setup 2 display frames
+ // DF 1: [22, 30] -> [0, 11]
+ // DF 2: [82, 90] -> SF [5, 16]
+ auto tracingSession = getTracingSessionForTest();
+ tracingSession->StartBlocking();
+ int64_t sfToken1 = mTokenManager->generateTokenForPredictions({22, 30, 40});
+ int64_t sfToken2 = mTokenManager->generateTokenForPredictions({82, 90, 100});
+ int64_t surfaceFrameToken1 = mTokenManager->generateTokenForPredictions({0, 11, 25});
+ int64_t surfaceFrameToken2 = mTokenManager->generateTokenForPredictions({5, 16, 30});
+ auto presentFence1 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+
+ int64_t traceCookie = snoopCurrentTraceCookie();
+
+ // set up 1st display frame
+ FrameTimelineInfo ftInfo1;
+ ftInfo1.vsyncId = surfaceFrameToken1;
+ ftInfo1.inputEventId = sInputEventId;
+ auto surfaceFrame1 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo1, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+ surfaceFrame1->setAcquireFenceTime(11);
+ mFrameTimeline->setSfWakeUp(sfToken1, 22, RR_11, RR_30);
+ surfaceFrame1->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame1);
+ mFrameTimeline->setSfPresent(30, presentFence1);
+ presentFence1->signalForTest(40);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ auto presentFence2 = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE);
+ FrameTimelineInfo ftInfo2;
+ ftInfo2.vsyncId = surfaceFrameToken2;
+ ftInfo2.inputEventId = sInputEventId;
+ auto surfaceFrame2 =
+ mFrameTimeline->createSurfaceFrameForToken(ftInfo2, sPidOne, sUidOne, sLayerIdOne,
+ sLayerNameOne, sLayerNameOne,
+ /*isBuffer*/ true, sGameMode);
+
+ // set up 2nd display frame
+ surfaceFrame2->setAcquireFenceTime(16);
+ mFrameTimeline->setSfWakeUp(sfToken2, 82, RR_11, RR_30);
+ surfaceFrame2->setPresentState(SurfaceFrame::PresentState::Presented);
+ mFrameTimeline->addSurfaceFrame(surfaceFrame2);
+ mFrameTimeline->setSfPresent(90, presentFence2);
+ presentFence2->signalForTest(100);
+
+ // the token of skipped Display Frame
+ auto protoSkippedActualDisplayFrameStart =
+ createProtoActualDisplayFrameStart(traceCookie + 9, 0, kSurfaceFlingerPid,
+ FrameTimelineEvent::PRESENT_DROPPED, true, false,
+ FrameTimelineEvent::JANK_DROPPED,
+ FrameTimelineEvent::SEVERITY_NONE,
+ FrameTimelineEvent::PREDICTION_VALID);
+ auto protoSkippedActualDisplayFrameEnd = createProtoFrameEnd(traceCookie + 9);
+
+ // Trigger a flush by finalizing the next DisplayFrame
+ addEmptyDisplayFrame();
+ flushTrace();
+ tracingSession->StopBlocking();
+
+ auto packets = readFrameTimelinePacketsBlocking(tracingSession.get());
+ // 8 Valid Display Frames + 8 Valid Surface Frames + no Skipped Display Frames
+ EXPECT_EQ(packets.size(), 16u);
+}
+
TEST_F(FrameTimelineTest, traceDisplayFrameSkipped) {
SET_FLAG_FOR_TEST(com::android::graphics::surfaceflinger::flags::add_sf_skipped_frames_to_trace,
true);
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
index 2b333f4..b79bdb4 100644
--- a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.cpp
@@ -777,4 +777,28 @@
EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expected);
}
+// (b/343901186)
+TEST_F(LayerHierarchyTest, cleanUpDanglingMirrorLayer) {
+ LayerHierarchyBuilder hierarchyBuilder;
+ hierarchyBuilder.update(mLifecycleManager);
+ mirrorLayer(/*layer*/ 14, /*parent*/ 1, /*layerToMirror*/ 2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+
+ std::vector<uint32_t> expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14, 2, 2};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+
+ // destroy layer handle
+ reparentLayer(2, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(2);
+ UPDATE_AND_VERIFY(hierarchyBuilder);
+ expectedTraversalPath = {1, 11, 111, 12, 121, 122, 1221, 13, 14};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ EXPECT_EQ(getTraversalPathInZOrder(hierarchyBuilder.getHierarchy()), expectedTraversalPath);
+ expectedTraversalPath = {};
+ EXPECT_EQ(getTraversalPath(hierarchyBuilder.getOffscreenHierarchy()), expectedTraversalPath);
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h b/services/surfaceflinger/tests/unittests/LayerHierarchyTest.h
index 67e6249..8b3303c 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;
@@ -465,14 +494,14 @@
mLifecycleManager.applyTransactions(transactions);
}
- void setTrustedOverlay(uint32_t id, bool isTrustedOverlay) {
+ void setTrustedOverlay(uint32_t id, gui::TrustedOverlay trustedOverlay) {
std::vector<TransactionState> transactions;
transactions.emplace_back();
transactions.back().states.push_back({});
transactions.back().states.front().state.what = layer_state_t::eTrustedOverlayChanged;
transactions.back().states.front().layerId = id;
- transactions.back().states.front().state.isTrustedOverlay = isTrustedOverlay;
+ transactions.back().states.front().state.trustedOverlay = trustedOverlay;
mLifecycleManager.applyTransactions(transactions);
}
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
index 110f324..a61fa1e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp
@@ -274,6 +274,8 @@
}
TEST_F(LayerHistoryIntegrationTest, oneLayerExplicitCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
createLegacyAndFrontedEndLayer(1);
setFrameRateCategory(1, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH);
@@ -1001,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 9b8ff42..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,7 +551,122 @@
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);
+
+ auto layer = createLayer();
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
+ .WillRepeatedly(
+ Return(Layer::FrameRate(73.4_Hz, Layer::FrameRateCompatibility::Default,
+ Seamlessness::OnlySeamless, FrameRateCategory::High)));
+
+ // Set default to Min so it is obvious that the vote reset triggered.
+ setDefaultLayerVote(layer.get(), LayerHistory::LayerVoteType::Min);
+
+ 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;
+ }
+
+ // There is only 1 LayerRequirement due to the disabled flag frame_rate_category_mrr.
+ ASSERT_EQ(1, summarizeLayerHistory(time).size());
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, summarizeLayerHistory(time)[0].vote);
+ EXPECT_EQ(0_Hz, summarizeLayerHistory(time)[0].desiredRefreshRate);
+ EXPECT_EQ(FrameRateCategory::Default, summarizeLayerHistory(time)[0].frameRateCategory);
+}
+
TEST_F(LayerHistoryTest, oneLayerExplicitCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree())
@@ -574,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());
@@ -588,6 +706,8 @@
// This test case should be the same as oneLayerNoVote except instead of layer vote is NoVote,
// the category is NoPreference.
TEST_F(LayerHistoryTest, oneLayerCategoryNoPreference) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree())
@@ -609,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());
@@ -617,6 +737,8 @@
}
TEST_F(LayerHistoryTest, oneLayerExplicitVoteWithCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree())
@@ -648,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());
@@ -1053,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/LayerLifecycleManagerTest.cpp b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
index aecfcba..bc15dec 100644
--- a/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerLifecycleManagerTest.cpp
@@ -461,8 +461,8 @@
HAL_PIXEL_FORMAT_RGBA_8888,
GRALLOC_USAGE_PROTECTED /*usage*/));
EXPECT_EQ(mLifecycleManager.getGlobalChanges().get(),
- ftl::Flags<RequestedLayerState::Changes>(RequestedLayerState::Changes::Buffer |
- RequestedLayerState::Changes::Content)
+ ftl::Flags<RequestedLayerState::Changes>(
+ RequestedLayerState::Changes::Buffer | RequestedLayerState::Changes::Content)
.get());
mLifecycleManager.commitChanges();
@@ -493,10 +493,10 @@
HAL_PIXEL_FORMAT_RGB_888,
GRALLOC_USAGE_PROTECTED /*usage*/));
EXPECT_EQ(mLifecycleManager.getGlobalChanges().get(),
- ftl::Flags<RequestedLayerState::Changes>(RequestedLayerState::Changes::Buffer |
- RequestedLayerState::Changes::Content |
- RequestedLayerState::Changes::VisibleRegion |
- RequestedLayerState::Changes::Visibility)
+ ftl::Flags<RequestedLayerState::Changes>(
+ RequestedLayerState::Changes::Buffer | RequestedLayerState::Changes::Content |
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Visibility)
.get());
mLifecycleManager.commitChanges();
}
@@ -538,7 +538,8 @@
ftl::Flags<RequestedLayerState::Changes>(
RequestedLayerState::Changes::Content |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::VisibleRegion)
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Input)
.string());
EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(startingAlpha));
mLifecycleManager.commitChanges();
@@ -551,7 +552,8 @@
ftl::Flags<RequestedLayerState::Changes>(
RequestedLayerState::Changes::Content |
RequestedLayerState::Changes::AffectsChildren |
- RequestedLayerState::Changes::VisibleRegion)
+ RequestedLayerState::Changes::VisibleRegion |
+ RequestedLayerState::Changes::Input)
.string());
EXPECT_EQ(mLifecycleManager.getChangedLayers()[0]->color.a, static_cast<half>(endingAlpha));
mLifecycleManager.commitChanges();
@@ -560,4 +562,73 @@
ftl::Flags<RequestedLayerState::Changes>().string());
}
+TEST_F(LayerLifecycleManagerTest, layerSecureChangesSetsVisibilityChangeFlag) {
+ // add a default buffer and make the layer secure
+ setFlags(1, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure);
+ setBuffer(1,
+ std::make_shared<renderengine::mock::
+ FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 1ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_READ_NEVER /*usage*/));
+
+ mLifecycleManager.commitChanges();
+
+ // set new buffer but layer secure doesn't change
+ setBuffer(1,
+ std::make_shared<renderengine::mock::
+ FakeExternalTexture>(1U /*width*/, 1U /*height*/,
+ 2ULL /* bufferId */,
+ HAL_PIXEL_FORMAT_RGBA_8888,
+ GRALLOC_USAGE_SW_READ_NEVER /*usage*/));
+ EXPECT_EQ(mLifecycleManager.getGlobalChanges().get(),
+ ftl::Flags<RequestedLayerState::Changes>(
+ RequestedLayerState::Changes::Buffer | RequestedLayerState::Changes::Content)
+ .get());
+ mLifecycleManager.commitChanges();
+
+ // change layer flags and confirm visibility flag is set
+ setFlags(1, layer_state_t::eLayerSecure, 0);
+ EXPECT_TRUE(
+ mLifecycleManager.getGlobalChanges().test(RequestedLayerState::Changes::Visibility));
+ mLifecycleManager.commitChanges();
+}
+
+TEST_F(LayerLifecycleManagerTest, isSimpleBufferUpdate) {
+ auto layer = rootLayer(1);
+
+ // no buffer changes
+ EXPECT_FALSE(layer->isSimpleBufferUpdate({}));
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::eBufferChanged;
+ EXPECT_TRUE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::eReparent | layer_state_t::eBufferChanged;
+ EXPECT_FALSE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer_state_t state;
+ state.what = layer_state_t::ePositionChanged | layer_state_t::eBufferChanged;
+ state.x = 9;
+ state.y = 10;
+ EXPECT_FALSE(layer->isSimpleBufferUpdate(state));
+ }
+
+ {
+ layer->x = 9;
+ layer->y = 10;
+ layer_state_t state;
+ state.what = layer_state_t::ePositionChanged | layer_state_t::eBufferChanged;
+ state.x = 9;
+ state.y = 10;
+ EXPECT_TRUE(layer->isSimpleBufferUpdate(state));
+ }
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 3baa48d..8b9ac93 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -17,6 +17,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <common/test/FlagUtils.h>
#include <renderengine/mock/FakeExternalTexture.h>
#include "FrontEnd/LayerHierarchy.h"
@@ -26,6 +27,8 @@
#include "LayerHierarchyTest.h"
#include "ui/GraphicTypes.h"
+#include <com_android_graphics_surfaceflinger_flags.h>
+
#define UPDATE_AND_VERIFY(BUILDER, ...) \
({ \
SCOPED_TRACE(""); \
@@ -42,6 +45,7 @@
using ftl::Flags;
using namespace ftl::flag_operators;
+using namespace com::android::graphics::surfaceflinger;
// To run test:
/**
@@ -209,6 +213,17 @@
UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
}
+TEST_F(LayerSnapshotTest, offscreenLayerSnapshotIsInvisible) {
+ EXPECT_EQ(getSnapshot(111)->isVisible, true);
+
+ reparentLayer(11, UNASSIGNED_LAYER_ID);
+ destroyLayerHandle(11);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 12, 121, 122, 1221, 13, 2});
+
+ EXPECT_EQ(getSnapshot(111)->isVisible, false);
+ EXPECT_TRUE(getSnapshot(111)->changes.test(RequestedLayerState::Changes::Visibility));
+}
+
// relative tests
TEST_F(LayerSnapshotTest, RelativeParentCanHideChild) {
reparentRelativeLayer(13, 11);
@@ -250,7 +265,8 @@
TEST_F(LayerSnapshotTest, FastPathClearsPreviousChangeStates) {
setColor(11, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_EQ(getSnapshot(11)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(11)->changes,
+ RequestedLayerState::Changes::Content);
EXPECT_EQ(getSnapshot(11)->clientChanges, layer_state_t::eColorChanged);
EXPECT_EQ(getSnapshot(1)->changes.get(), 0u);
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
@@ -260,7 +276,8 @@
TEST_F(LayerSnapshotTest, FastPathSetsChangeFlagToContent) {
setColor(1, {1._hf, 0._hf, 0._hf});
UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
- EXPECT_EQ(getSnapshot(1)->changes, RequestedLayerState::Changes::Content);
+ EXPECT_EQ(getSnapshot(1)->changes,
+ RequestedLayerState::Changes::Content);
EXPECT_EQ(getSnapshot(1)->clientChanges, layer_state_t::eColorChanged);
}
@@ -668,6 +685,8 @@
// This test is similar to "frameRate" test case but checks that the setFrameRateCategory API
// interaction also works correctly with the setFrameRate API within SF frontend.
TEST_F(LayerSnapshotTest, frameRateWithCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
// ROOT
// ├── 1
// │ ├── 11 (frame rate set to 244.f)
@@ -864,6 +883,8 @@
}
TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) {
+ SET_FLAG_FOR_TEST(flags::frame_rate_category_mrr, true);
+
// ROOT
// ├── 1
// │ ├── 11
@@ -1142,7 +1163,7 @@
TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) {
hideLayer(1);
- setTrustedOverlay(1, true);
+ setTrustedOverlay(1, gui::TrustedOverlay::ENABLED);
Region touch{Rect{0, 0, 1000, 1000}};
setTouchableRegion(1, touch);
@@ -1151,6 +1172,16 @@
gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
}
+TEST_F(LayerSnapshotTest, alphaChangesPropagateToInput) {
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(1, touch);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+
+ setAlpha(1, 0.5f);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER);
+ EXPECT_EQ(getSnapshot(1)->inputInfo.alpha, 0.5f);
+}
+
TEST_F(LayerSnapshotTest, isFrontBuffered) {
setBuffer(1,
std::make_shared<renderengine::mock::FakeExternalTexture>(
@@ -1190,6 +1221,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);
@@ -1258,4 +1325,114 @@
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);
+}
+
+TEST_F(LayerSnapshotTest, overrideParentTrustedOverlayState) {
+ SET_FLAG_FOR_TEST(flags::override_trusted_overlay, true);
+ hideLayer(1);
+ setTrustedOverlay(1, gui::TrustedOverlay::ENABLED);
+
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(1, touch);
+ setTouchableRegion(11, touch);
+ setTouchableRegion(111, touch);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+
+ // disable trusted overlay and override parent state
+ setTrustedOverlay(11, gui::TrustedOverlay::DISABLED);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_FALSE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_FALSE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+
+ // unset state and go back to default behavior of inheriting
+ // state
+ setTrustedOverlay(11, gui::TrustedOverlay::UNSET);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+}
+
+TEST_F(LayerSnapshotTest, doNotOverrideParentTrustedOverlayState) {
+ SET_FLAG_FOR_TEST(flags::override_trusted_overlay, false);
+ hideLayer(1);
+ setTrustedOverlay(1, gui::TrustedOverlay::ENABLED);
+
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegion(1, touch);
+ setTouchableRegion(11, touch);
+ setTouchableRegion(111, touch);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+
+ // disable trusted overlay but flag is disabled so this behaves
+ // as UNSET
+ setTrustedOverlay(11, gui::TrustedOverlay::DISABLED);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+
+ // unset state and go back to default behavior of inheriting
+ // state
+ setTrustedOverlay(11, gui::TrustedOverlay::UNSET);
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {2});
+ EXPECT_TRUE(getSnapshot(1)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(11)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+ EXPECT_TRUE(getSnapshot(111)->inputInfo.inputConfig.test(
+ gui::WindowInfo::InputConfig::TRUSTED_OVERLAY));
+}
+
} // namespace android::surfaceflinger::frontend
diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
index 9c66a97..e74f643 100644
--- a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp
@@ -18,7 +18,11 @@
#define LOG_TAG "PowerAdvisorTest"
#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>
#include <powermanager/PowerHalWrapper.h>
@@ -26,8 +30,8 @@
#include <chrono>
#include <future>
#include "TestableSurfaceFlinger.h"
-#include "mock/DisplayHardware/MockIPowerHintSession.h"
#include "mock/DisplayHardware/MockPowerHalController.h"
+#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
using namespace android;
using namespace android::Hwc2::mock;
@@ -41,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);
@@ -49,12 +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<MockIPowerHintSession> mMockPowerHintSession;
+ 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() {
@@ -62,31 +92,72 @@
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>>();
mMockPowerHalController =
reinterpret_cast<MockPowerHalController*>(mPowerAdvisor->mPowerHal.get());
ON_CALL(*mMockPowerHalController, getHintSessionPreferredRate)
- .WillByDefault(Return(HalResult<int64_t>::fromStatus(binder::Status::ok(), 16000)));
+ .WillByDefault(Return(
+ 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 = ndk::SharedRefBase::make<NiceMock<MockIPowerHintSession>>();
+ mMockPowerHintSession = std::make_shared<NiceMock<MockPowerHintSessionWrapper>>();
if (returnValidSession) {
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(
- Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), mMockPowerHintSession)));
+ ON_CALL(*mMockPowerHalController, createHintSessionWithConfig)
+ .WillByDefault(DoAll(SetArgPointee<5>(aidl::android::hardware::power::SessionConfig{
+ .id = mSessionId}),
+ Return(HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+ fromStatus(binder::Status::ok(),
+ mMockPowerHintSession))));
} else {
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(binder::Status::ok(), nullptr)));
+ ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] {
+ return HalResult<
+ std::shared_ptr<PowerHintSessionWrapper>>::fromStatus(ndk::ScopedAStatus::ok(),
+ nullptr);
+ });
}
mPowerAdvisor->enablePowerHintSession(true);
mPowerAdvisor->startPowerHintSession({1, 2, 3});
ON_CALL(*mMockPowerHintSession, updateTargetWorkDuration)
- .WillByDefault(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillByDefault(Return(testing::ByMove(HalResult<void>::ok())));
}
void PowerAdvisorTest::setExpectedTiming(Duration totalFrameTargetDuration,
@@ -109,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);
@@ -148,7 +344,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
mPowerAdvisor->setDisplays(displayIds);
@@ -188,7 +384,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -231,7 +427,7 @@
reportActualWorkDuration(ElementsAre(
Field(&WorkDuration::durationNanos, Eq(expectedDuration.ns())))))
.Times(1)
- .WillOnce(Return(testing::ByMove(ndk::ScopedAStatus::ok())));
+ .WillOnce(Return(testing::ByMove(HalResult<void>::ok())));
fakeBasicFrameTiming(startTime, vsyncPeriod);
setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod);
@@ -282,13 +478,6 @@
mPowerAdvisor->reportActualWorkDuration();
}
-TEST_F(PowerAdvisorTest, hintSessionOnlyCreatedOnce) {
- EXPECT_CALL(*mMockPowerHalController, createHintSession(_, _, _, _)).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
@@ -328,17 +517,17 @@
ON_CALL(*mMockPowerHintSession, sendHint).WillByDefault([&letSendHintFinish] {
letSendHintFinish.get_future().wait();
- return ndk::ScopedAStatus::fromExceptionCode(-127);
+ return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127));
});
ON_CALL(*mMockPowerHintSession, reportActualWorkDuration).WillByDefault([] {
- return ndk::ScopedAStatus::fromExceptionCode(-127);
+ return HalResult<void>::fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127));
});
- ON_CALL(*mMockPowerHalController, createHintSession)
- .WillByDefault(Return(
- HalResult<std::shared_ptr<IPowerHintSession>>::
- fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr)));
+ ON_CALL(*mMockPowerHalController, createHintSessionWithConfig).WillByDefault([] {
+ return HalResult<std::shared_ptr<PowerHintSessionWrapper>>::
+ fromStatus(ndk::ScopedAStatus::fromExceptionCode(-127), nullptr);
+ });
// First background call, to notice the session is down
auto firstHint = std::async(std::launch::async, [this] {
@@ -370,5 +559,284 @@
EXPECT_EQ(sessionExists(), false);
}
+TEST_F(PowerAdvisorTest, legacyHintSessionCreationStillWorks) {
+ 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);
+ 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
} // namespace android::Hwc2::impl
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
index 0a6e305..cf9a7d3 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -103,8 +103,9 @@
auto& mutableGetRankedRefreshRatesCache() { return mGetRankedFrameRatesCache; }
auto getRankedFrameRates(const std::vector<LayerRequirement>& layers,
- GlobalSignals signals = {}) const {
- const auto result = RefreshRateSelector::getRankedFrameRates(layers, signals);
+ GlobalSignals signals = {}, Fps pacesetterFps = {}) const {
+ const auto result =
+ RefreshRateSelector::getRankedFrameRates(layers, signals, pacesetterFps);
EXPECT_TRUE(std::is_sorted(result.ranking.begin(), result.ranking.end(),
ScoredFrameRate::DescendingScore{}));
@@ -114,8 +115,8 @@
auto getRankedRefreshRatesAsPair(const std::vector<LayerRequirement>& layers,
GlobalSignals signals) const {
- const auto [ranking, consideredSignals] = getRankedFrameRates(layers, signals);
- return std::make_pair(ranking, consideredSignals);
+ const auto result = getRankedFrameRates(layers, signals);
+ return std::make_pair(result.ranking, result.consideredSignals);
}
FrameRateMode getBestFrameRateMode(const std::vector<LayerRequirement>& layers = {},
@@ -259,6 +260,50 @@
config.enableFrameRateOverride = GetParam();
return TestableRefreshRateSelector(modes, activeModeId, config);
}
+
+ template <class T>
+ void testFrameRateCategoryWithMultipleLayers(const std::initializer_list<T>& testCases,
+ const TestableRefreshRateSelector& selector) {
+ std::vector<LayerRequirement> layers;
+ for (auto testCase : testCases) {
+ ALOGI("**** %s: Testing desiredFrameRate=%s, frameRateCategory=%s", __func__,
+ to_string(testCase.desiredFrameRate).c_str(),
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.desiredFrameRate.isValid()) {
+ std::stringstream ss;
+ ss << to_string(testCase.desiredFrameRate)
+ << ftl::enum_string(testCase.frameRateCategory) << "ExplicitDefault";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitDefault,
+ .desiredRefreshRate = testCase.desiredFrameRate,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers).modePtr->getPeakFps())
+ << "Did not get expected frame rate for frameRate="
+ << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ EXPECT_EQ(testCase.expectedModeId,
+ selector.getBestFrameRateMode(layers).modePtr->getId())
+ << "Did not get expected DisplayModeId for modeId="
+ << ftl::to_underlying(testCase.expectedModeId)
+ << " frameRate=" << to_string(testCase.desiredFrameRate)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+ }
};
RefreshRateSelectorTest::RefreshRateSelectorTest() {
@@ -1343,7 +1388,7 @@
TEST_P(RefreshRateSelectorTest, powerOnImminentConsidered) {
auto selector = createSelector(kModes_60_90, kModeId60);
- auto [refreshRates, signals] = selector.getRankedFrameRates({}, {});
+ auto [refreshRates, signals, _] = selector.getRankedFrameRates({}, {});
EXPECT_FALSE(signals.powerOnImminent);
auto expectedRefreshRates = []() -> std::vector<FrameRateMode> {
@@ -1427,10 +1472,32 @@
}
}
+TEST_P(RefreshRateSelectorTest, pacesetterConsidered) {
+ auto selector = createSelector(kModes_60_90, kModeId60);
+ constexpr RefreshRateSelector::GlobalSignals kNoSignals;
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}};
+ layers[0].vote = LayerVoteType::Min;
+
+ // The pacesetterFps takes precedence over the LayerRequirement.
+ {
+ const auto result = selector.getRankedFrameRates(layers, {}, 90_Hz);
+ EXPECT_EQ(kMode90, result.ranking.front().frameRateMode.modePtr);
+ EXPECT_EQ(kNoSignals, result.consideredSignals);
+ }
+
+ // The pacesetterFps takes precedence over GlobalSignals.
+ {
+ const auto result = selector.getRankedFrameRates(layers, {.touch = true}, 60_Hz);
+ EXPECT_EQ(kMode60, result.ranking.front().frameRateMode.modePtr);
+ EXPECT_EQ(kNoSignals, result.consideredSignals);
+ }
+}
+
TEST_P(RefreshRateSelectorTest, touchConsidered) {
auto selector = createSelector(kModes_60_90, kModeId60);
- auto [_, signals] = selector.getRankedFrameRates({}, {});
+ auto signals = selector.getRankedFrameRates({}, {}).consideredSignals;
EXPECT_FALSE(signals.touch);
std::tie(std::ignore, signals) = selector.getRankedRefreshRatesAsPair({}, {.touch = true});
@@ -1496,7 +1563,7 @@
{0_Hz, FrameRateCategory::High, 90_Hz},
{0_Hz, FrameRateCategory::Normal, 60_Hz},
{0_Hz, FrameRateCategory::Low, 30_Hz},
- {0_Hz, FrameRateCategory::NoPreference, 30_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 60_Hz},
// Cases that have both desired frame rate and frame rate category requirements.
{24_Hz, FrameRateCategory::High, 120_Hz},
@@ -1542,6 +1609,98 @@
}
}
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategoryMultiLayers_30_60_90_120) {
+ auto selector = createSelector(makeModes(kMode30, kMode60, kMode90, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ DisplayModeId expectedModeId = kModeId90;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::Normal, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {0_Hz, FrameRateCategory::Normal, 60_Hz, kModeId60},
+ {0_Hz, FrameRateCategory::High, 90_Hz},
+ {0_Hz, FrameRateCategory::NoPreference, 90_Hz},
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {30_Hz, FrameRateCategory::High, 90_Hz},
+ {24_Hz, FrameRateCategory::High, 120_Hz, kModeId120},
+ {12_Hz, FrameRateCategory::Normal, 120_Hz, kModeId120},
+ {30_Hz, FrameRateCategory::NoPreference, 120_Hz, kModeId120},
+
+ },
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz, kModeId120},
+ {30_Hz, FrameRateCategory::Default, 120_Hz, kModeId120},
+ {120_Hz, FrameRateCategory::Default, 120_Hz, kModeId120},
+ },
+ selector);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategoryMultiLayers_60_120) {
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
+
+ struct Case {
+ // Params
+ Fps desiredFrameRate = 0_Hz;
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ DisplayModeId expectedModeId = kModeId120;
+ };
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{0_Hz, FrameRateCategory::High, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::Normal, 120_Hz},
+ {0_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(std::initializer_list<
+ Case>{{24_Hz, FrameRateCategory::High, 120_Hz},
+ {30_Hz, FrameRateCategory::High, 120_Hz},
+ {12_Hz, FrameRateCategory::Normal,
+ 120_Hz},
+ {30_Hz, FrameRateCategory::NoPreference,
+ 120_Hz}},
+ selector);
+
+ testFrameRateCategoryWithMultipleLayers(
+ std::initializer_list<Case>{
+ {24_Hz, FrameRateCategory::Default, 120_Hz},
+ {30_Hz, FrameRateCategory::Default, 120_Hz},
+ {120_Hz, FrameRateCategory::Default, 120_Hz},
+ },
+ selector);
+}
+
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_120) {
auto selector = createSelector(makeModes(kMode60, kMode120), kModeId60);
@@ -1665,6 +1824,7 @@
lr1.frameRateCategory = FrameRateCategory::HighHint;
lr1.name = "ExplicitCategory HighHint";
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
lr2.desiredRefreshRate = 30_Hz;
lr2.name = "30Hz ExplicitExactOrMultiple";
actualRankedFrameRates = selector.getRankedFrameRates(layers);
@@ -1691,6 +1851,269 @@
EXPECT_EQ(kModeId30, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
}
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::HighHint;
+ lr1.name = "ExplicitCategory HighHint";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers);
+ // Gets touch boost
+ EXPECT_EQ(120_Hz, actualRankedFrameRates.ranking.front().frameRateMode.fps);
+ EXPECT_EQ(kModeId120, actualRankedFrameRates.ranking.front().frameRateMode.modePtr->getId());
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_TouchBoost) {
+ auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60);
+
+ std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::NoVote;
+ lr2.name = "NoVote";
+ auto actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ // No touch boost, for example a game that uses setFrameRate(30, default compatibility).
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitDefault;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitDefault";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::HighHint;
+ lr2.name = "ExplicitCategory HighHint";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitCategory;
+ lr2.frameRateCategory = FrameRateCategory::Low;
+ lr2.name = "ExplicitCategory Low";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr2.frameRateCategory = FrameRateCategory::Default;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExactOrMultiple";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitExact;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitExact";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ if (selector.supportsAppFrameRateOverrideByContent()) {
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+ } else {
+ EXPECT_FRAME_RATE_MODE(kMode30, 30_Hz,
+ actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+ }
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Min;
+ lr2.name = "Min";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Max;
+ lr2.name = "Max";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "30Hz Heuristic";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode120, 120_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_TRUE(actualRankedFrameRates.consideredSignals.touch);
+
+ lr1.vote = LayerVoteType::ExplicitCategory;
+ lr1.frameRateCategory = FrameRateCategory::Normal;
+ lr1.name = "ExplicitCategory Normal";
+ lr2.vote = LayerVoteType::ExplicitGte;
+ lr2.desiredRefreshRate = 30_Hz;
+ lr2.name = "30Hz ExplicitGte";
+ actualRankedFrameRates = selector.getRankedFrameRates(layers, {.touch = true});
+ EXPECT_FRAME_RATE_MODE(kMode60, 60_Hz, actualRankedFrameRates.ranking.front().frameRateMode);
+ EXPECT_FALSE(actualRankedFrameRates.consideredSignals.touch);
+}
+
+TEST_P(RefreshRateSelectorTest,
+ getBestFrameRateMode_withFrameRateCategory_idleTimer_60_120_nonVrr) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, false);
+ using KernelIdleTimerAction = RefreshRateSelector::KernelIdleTimerAction;
+ struct LayerArg {
+ // Params
+ FrameRateCategory frameRateCategory = FrameRateCategory::Default;
+ LayerVoteType voteType = LayerVoteType::ExplicitDefault;
+
+ // Expected result
+ Fps expectedFrameRate = 0_Hz;
+ DisplayModeId expectedModeId = kModeId60;
+ };
+
+ const auto runTest = [&](const TestableRefreshRateSelector& selector,
+ const std::initializer_list<LayerArg>& layerArgs,
+ const RefreshRateSelector::GlobalSignals& signals) {
+ std::vector<LayerRequirement> layers;
+ for (auto testCase : layerArgs) {
+ ALOGI("**** %s: Testing frameRateCategory=%s", __func__,
+ ftl::enum_string(testCase.frameRateCategory).c_str());
+
+ if (testCase.frameRateCategory != FrameRateCategory::Default) {
+ std::stringstream ss;
+ ss << "ExplicitCategory (" << ftl::enum_string(testCase.frameRateCategory) << ")";
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = LayerVoteType::ExplicitCategory,
+ .frameRateCategory = testCase.frameRateCategory,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ if (testCase.voteType != LayerVoteType::ExplicitDefault) {
+ std::stringstream ss;
+ ss << ftl::enum_string(testCase.voteType);
+ LayerRequirement layer = {.name = ss.str(),
+ .vote = testCase.voteType,
+ .weight = 1.f};
+ layers.push_back(layer);
+ }
+
+ EXPECT_EQ(testCase.expectedFrameRate,
+ selector.getBestFrameRateMode(layers, signals).modePtr->getPeakFps())
+ << "Did not get expected frame rate for"
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ EXPECT_EQ(testCase.expectedModeId,
+ selector.getBestFrameRateMode(layers, signals).modePtr->getId())
+ << "Did not get expected DisplayModeId for modeId="
+ << ftl::to_underlying(testCase.expectedModeId)
+ << " category=" << ftl::enum_string(testCase.frameRateCategory);
+ }
+ };
+
+ {
+ // IdleTimer not configured
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120);
+ ASSERT_EQ(0ms, selector.getIdleTimerTimeout());
+
+ runTest(selector,
+ std::initializer_list<LayerArg>{
+ // Rate does not change due to NoPreference.
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ {.voteType = LayerVoteType::NoVote,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ },
+ {.idle = false});
+ }
+
+ // IdleTimer configured
+ constexpr std::chrono::milliseconds kIdleTimerTimeoutMs = 10ms;
+ auto selector = createSelector(makeModes(kMode60, kMode120), kModeId120,
+ Config{
+ .legacyIdleTimerTimeout = kIdleTimerTimeoutMs,
+ });
+ ASSERT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+ ASSERT_EQ(kIdleTimerTimeoutMs, selector.getIdleTimerTimeout());
+ runTest(selector,
+ std::initializer_list<LayerArg>{
+ // Rate won't change immediately and will stay 120 due to NoPreference, as
+ // idle timer did not timeout yet.
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ {.voteType = LayerVoteType::NoVote,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 120_Hz,
+ .expectedModeId = kModeId120},
+ },
+ {.idle = false});
+
+ // Idle timer is triggered using GlobalSignals.
+ ASSERT_EQ(KernelIdleTimerAction::TurnOn, selector.getIdleTimerAction());
+ ASSERT_EQ(kIdleTimerTimeoutMs, selector.getIdleTimerTimeout());
+ runTest(selector,
+ std::initializer_list<LayerArg>{
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 60_Hz,
+ .expectedModeId = kModeId60},
+ {.voteType = LayerVoteType::NoVote,
+ .expectedFrameRate = 60_Hz,
+ .expectedModeId = kModeId60},
+ {.frameRateCategory = FrameRateCategory::NoPreference,
+ .expectedFrameRate = 60_Hz,
+ .expectedModeId = kModeId60},
+ },
+ {.idle = true});
}
TEST_P(RefreshRateSelectorTest,
@@ -1716,8 +2139,7 @@
const std::initializer_list<Case> testCases = {
// These layers may switch modes because smoothSwitchOnly=false.
{FrameRateCategory::Default, false, 120_Hz, kModeId120},
- // TODO(b/266481656): Once this bug is fixed, NoPreference should be a lower frame rate.
- {FrameRateCategory::NoPreference, false, 60_Hz, kModeId60},
+ {FrameRateCategory::NoPreference, false, 120_Hz, kModeId120},
{FrameRateCategory::Low, false, 30_Hz, kModeId60},
{FrameRateCategory::Normal, false, 60_Hz, kModeId60},
{FrameRateCategory::High, false, 120_Hz, kModeId120},
@@ -1725,8 +2147,8 @@
// These layers cannot change mode due to smoothSwitchOnly, and will definitely use
// active mode (120Hz).
{FrameRateCategory::NoPreference, true, 120_Hz, kModeId120},
- {FrameRateCategory::Low, true, 120_Hz, kModeId120},
- {FrameRateCategory::Normal, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Low, true, 40_Hz, kModeId120},
+ {FrameRateCategory::Normal, true, 120_Hz, kModeId120},
{FrameRateCategory::High, true, 120_Hz, kModeId120},
};
@@ -1964,7 +2386,7 @@
lr.name = "60Hz ExplicitDefault";
lr.focused = true;
- const auto [rankedFrameRate, signals] =
+ const auto [rankedFrameRate, signals, _] =
selector.getRankedFrameRates(layers, {.touch = true, .idle = true});
EXPECT_EQ(rankedFrameRate.begin()->frameRateMode.modePtr, kMode60);
@@ -2188,7 +2610,7 @@
EXPECT_EQ(SetPolicyResult::Changed,
selector.setDisplayManagerPolicy({kModeId90, {k90, k90}, {k60_90, k60_90}}));
- const auto [ranking, signals] = selector.getRankedFrameRates({}, {});
+ const auto [ranking, signals, _] = selector.getRankedFrameRates({}, {});
EXPECT_EQ(ranking.front().frameRateMode.modePtr, kMode90);
EXPECT_FALSE(signals.touch);
@@ -2572,7 +2994,7 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
- const auto [ranking, signals] =
+ const auto [ranking, signals, _] =
selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
@@ -2722,16 +3144,17 @@
auto selector = createSelector(kModes_30_60_72_90_120, kModeId60);
using GlobalSignals = RefreshRateSelector::GlobalSignals;
- const auto args = std::make_pair(std::vector<LayerRequirement>{},
- GlobalSignals{.touch = true, .idle = true});
-
const RefreshRateSelector::RankedFrameRates result = {{RefreshRateSelector::ScoredFrameRate{
{90_Hz, kMode90}}},
GlobalSignals{.touch = true}};
- selector.mutableGetRankedRefreshRatesCache() = {args, result};
+ selector.mutableGetRankedRefreshRatesCache() = {.layers = std::vector<LayerRequirement>{},
+ .signals = GlobalSignals{.touch = true,
+ .idle = true},
+ .result = result};
- EXPECT_EQ(result, selector.getRankedFrameRates(args.first, args.second));
+ const auto& cache = *selector.mutableGetRankedRefreshRatesCache();
+ EXPECT_EQ(result, selector.getRankedFrameRates(cache.layers, cache.signals));
}
TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_WritesCache) {
@@ -2739,15 +3162,18 @@
EXPECT_FALSE(selector.mutableGetRankedRefreshRatesCache());
- std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
- RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+ const std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 0.5f}};
+ const RefreshRateSelector::GlobalSignals globalSignals{.touch = true, .idle = true};
+ const Fps pacesetterFps = 60_Hz;
- const auto result = selector.getRankedFrameRates(layers, globalSignals);
+ const auto result = selector.getRankedFrameRates(layers, globalSignals, pacesetterFps);
const auto& cache = selector.mutableGetRankedRefreshRatesCache();
ASSERT_TRUE(cache);
- EXPECT_EQ(cache->arguments, std::make_pair(layers, globalSignals));
+ EXPECT_EQ(cache->layers, layers);
+ EXPECT_EQ(cache->signals, globalSignals);
+ EXPECT_EQ(cache->pacesetterFps, pacesetterFps);
EXPECT_EQ(cache->result, result);
}
@@ -3674,7 +4100,7 @@
layers[0].vote = voteType;
layers[0].desiredRefreshRate = 90_Hz;
- const auto [ranking, signals] =
+ const auto [ranking, signals, _] =
selector.getRankedFrameRates(layers, {.touch = touchActive, .idle = true});
// Refresh rate will be chosen by either touch state or idle state.
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 049b092..fc54a8b 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -57,6 +57,11 @@
using LayerHierarchyBuilder = surfaceflinger::frontend::LayerHierarchyBuilder;
using RequestedLayerState = surfaceflinger::frontend::RequestedLayerState;
+class ZeroClock : public Clock {
+public:
+ nsecs_t now() const override { return 0; }
+};
+
class SchedulerTest : public testing::Test {
protected:
class MockEventThreadConnection : public android::EventThreadConnection {
@@ -338,12 +343,18 @@
}
TEST_F(SchedulerTest, chooseDisplayModesMultipleDisplays) {
+ constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
- kDisplay1Mode60->getId()));
+ kDisplay1Mode60->getId()),
+ kActiveDisplayId);
mScheduler->registerDisplay(kDisplayId2,
std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
+ kDisplay2Mode60->getId()),
+ kActiveDisplayId);
+
+ mScheduler->setDisplayPowerMode(kDisplayId1, hal::PowerMode::ON);
+ mScheduler->setDisplayPowerMode(kDisplayId2, hal::PowerMode::ON);
using DisplayModeChoice = TestableScheduler::DisplayModeChoice;
TestableScheduler::DisplayModeChoiceMap expectedChoices;
@@ -357,7 +368,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{60_Hz,
kDisplay2Mode60},
- globalSignals);
+ GlobalSignals{});
std::vector<RefreshRateSelector::LayerRequirement> layers = {{.weight = 1.f},
{.weight = 1.f}};
@@ -376,7 +387,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz,
kDisplay2Mode120},
- globalSignals);
+ GlobalSignals{});
mScheduler->setTouchStateAndIdleTimerPolicy(globalSignals);
@@ -395,7 +406,7 @@
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz,
kDisplay2Mode120},
- globalSignals);
+ GlobalSignals{});
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
@@ -403,10 +414,11 @@
{
// The kDisplayId3 does not support 120Hz, The pacesetter display rate is chosen to be 120
// Hz. In this case only the display kDisplayId3 choose 60Hz as it does not support 120Hz.
- mScheduler
- ->registerDisplay(kDisplayId3,
- std::make_shared<RefreshRateSelector>(kDisplay3Modes,
- kDisplay3Mode60->getId()));
+ mScheduler->registerDisplay(kDisplayId3,
+ std::make_shared<RefreshRateSelector>(kDisplay3Modes,
+ kDisplay3Mode60->getId()),
+ kActiveDisplayId);
+ mScheduler->setDisplayPowerMode(kDisplayId3, hal::PowerMode::ON);
const GlobalSignals globalSignals = {.touch = true};
mScheduler->replaceTouchTimer(10);
@@ -417,10 +429,10 @@
DisplayModeChoice>(kDisplayId1, FrameRateMode{120_Hz, kDisplay1Mode120},
globalSignals)(kDisplayId2,
FrameRateMode{120_Hz, kDisplay2Mode120},
- globalSignals)(kDisplayId3,
- FrameRateMode{60_Hz,
- kDisplay3Mode60},
- globalSignals);
+ GlobalSignals{})(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ GlobalSignals{});
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
@@ -435,12 +447,12 @@
expectedChoices = ftl::init::map<
const PhysicalDisplayId&,
DisplayModeChoice>(kDisplayId1, FrameRateMode{60_Hz, kDisplay1Mode60},
- globalSignals)(kDisplayId2,
- FrameRateMode{60_Hz, kDisplay2Mode60},
- globalSignals)(kDisplayId3,
- FrameRateMode{60_Hz,
- kDisplay3Mode60},
- globalSignals);
+ GlobalSignals{})(kDisplayId2,
+ FrameRateMode{60_Hz, kDisplay2Mode60},
+ GlobalSignals{})(kDisplayId3,
+ FrameRateMode{60_Hz,
+ kDisplay3Mode60},
+ globalSignals);
const auto actualChoices = mScheduler->chooseDisplayModes();
EXPECT_EQ(expectedChoices, actualChoices);
@@ -448,12 +460,15 @@
}
TEST_F(SchedulerTest, onFrameSignalMultipleDisplays) {
+ constexpr PhysicalDisplayId kActiveDisplayId = kDisplayId1;
mScheduler->registerDisplay(kDisplayId1,
std::make_shared<RefreshRateSelector>(kDisplay1Modes,
- kDisplay1Mode60->getId()));
+ kDisplay1Mode60->getId()),
+ kActiveDisplayId);
mScheduler->registerDisplay(kDisplayId2,
std::make_shared<RefreshRateSelector>(kDisplay2Modes,
- kDisplay2Mode60->getId()));
+ kDisplay2Mode60->getId()),
+ kActiveDisplayId);
using VsyncIds = std::vector<std::pair<PhysicalDisplayId, VsyncId>>;
@@ -564,7 +579,7 @@
hal::VrrConfig{.minFrameIntervalNs = static_cast<int32_t>(
frameRate.getPeriodNsecs())}));
std::shared_ptr<VSyncPredictor> vrrTracker =
- std::make_shared<VSyncPredictor>(std::make_unique<SystemClock>(), kMode, kHistorySize,
+ std::make_shared<VSyncPredictor>(std::make_unique<ZeroClock>(), kMode, kHistorySize,
kMinimumSamplesForPrediction,
kOutlierTolerancePercent);
std::shared_ptr<RefreshRateSelector> vrrSelectorPtr =
@@ -576,9 +591,10 @@
mFlinger.getTimeStats(),
mSchedulerCallback};
- scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, vrrTracker);
+ scheduler.registerDisplay(kMode->getPhysicalDisplayId(), vrrSelectorPtr, std::nullopt,
+ vrrTracker);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
- scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false);
vrrTracker->addVsyncTimestamp(0);
// Set 1000 as vsync seq #0
vrrTracker->nextAnticipatedVSyncTimeFrom(700);
@@ -591,23 +607,21 @@
TimePoint::fromNs(2000)));
// Not crossing the min frame period
+ vrrTracker->onFrameBegin(TimePoint::fromNs(2000), TimePoint::fromNs(1500));
EXPECT_EQ(Fps::fromPeriodNsecs(1000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
TimePoint::fromNs(2500)));
// Change render rate
frameRate = Fps::fromPeriodNsecs(2000);
vrrSelectorPtr->setActiveMode(kMode->getId(), frameRate);
- scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate);
-
- // Set 2000 as vsync seq #0
- vrrTracker->nextAnticipatedVSyncTimeFrom(1700);
+ scheduler.setRenderRate(kMode->getPhysicalDisplayId(), frameRate, /*applyImmediately*/ false);
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(2000)));
+ TimePoint::fromNs(5500)));
EXPECT_EQ(Fps::fromPeriodNsecs(2000),
scheduler.getNextFrameInterval(kMode->getPhysicalDisplayId(),
- TimePoint::fromNs(4000)));
+ TimePoint::fromNs(7500)));
}
TEST_F(SchedulerTest, resyncAllToHardwareVsync) FTL_FAKE_GUARD(kMainThreadContext) {
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_ColorMatrixTest.cpp
index f127213..ff7612e 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,13 +46,15 @@
}
TEST_F(ColorMatrixTest, colorMatrixChangedAfterDisplayTransaction) {
+ mFlinger.enableLayerLifecycleManager();
EXPECT_COLOR_MATRIX_CHANGED(true, true);
mFlinger.mutableTransactionFlags() |= eTransactionNeeded;
mFlinger.commitAndComposite();
EXPECT_COLOR_MATRIX_CHANGED(false, false);
- mFlinger.createDisplay(String8("Test Display"), false);
+ static const std::string kDisplayName("Test Display");
+ mFlinger.createVirtualDisplay(kDisplayName, false /*isSecure=*/);
mFlinger.commit();
EXPECT_COLOR_MATRIX_CHANGED(false, true);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 28162f4..e5f2a91 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -27,7 +27,7 @@
class CreateDisplayTest : public DisplayTransactionTest {
public:
- void createDisplayWithRequestedRefreshRate(const String8& name, uint64_t displayId,
+ void createDisplayWithRequestedRefreshRate(const std::string& name, uint64_t displayId,
float pacesetterDisplayRefreshRate,
float requestedRefreshRate,
float expectedAdjustedRefreshRate) {
@@ -37,7 +37,7 @@
// --------------------------------------------------------------------
// Invocation
- sp<IBinder> displayToken = mFlinger.createDisplay(name, false, requestedRefreshRate);
+ sp<IBinder> displayToken = mFlinger.createVirtualDisplay(name, false, requestedRefreshRate);
// --------------------------------------------------------------------
// Postconditions
@@ -73,7 +73,7 @@
};
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForNonsecureDisplay) {
- const String8 name("virtual.test");
+ static const std::string name("virtual.test");
// --------------------------------------------------------------------
// Call Expectations
@@ -81,7 +81,7 @@
// --------------------------------------------------------------------
// Invocation
- sp<IBinder> displayToken = mFlinger.createDisplay(name, false);
+ sp<IBinder> displayToken = mFlinger.createVirtualDisplay(name, false);
// --------------------------------------------------------------------
// Postconditions
@@ -101,7 +101,7 @@
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
- const String8 name("virtual.test");
+ static const std::string kDisplayName("virtual.test");
// --------------------------------------------------------------------
// Call Expectations
@@ -112,7 +112,7 @@
// Set the calling identity to graphics so captureDisplay with secure is allowed.
IPCThreadState::self()->restoreCallingIdentity(static_cast<int64_t>(AID_GRAPHICS) << 32 |
AID_GRAPHICS);
- sp<IBinder> displayToken = mFlinger.createDisplay(name, true);
+ sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, true);
IPCThreadState::self()->restoreCallingIdentity(oldId);
// --------------------------------------------------------------------
@@ -123,7 +123,37 @@
const auto& display = getCurrentDisplayState(displayToken);
EXPECT_TRUE(display.isVirtual());
EXPECT_TRUE(display.isSecure);
- EXPECT_EQ(name.c_str(), display.displayName);
+ EXPECT_EQ(kDisplayName.c_str(), display.displayName);
+
+ // --------------------------------------------------------------------
+ // Cleanup conditions
+
+ // Creating the display commits a display transaction.
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
+}
+
+TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForUniqueId) {
+ static const std::string kDisplayName("virtual.test");
+ static const std::string kUniqueId = "virtual:package:id";
+
+ // --------------------------------------------------------------------
+ // Call Expectations
+
+ // --------------------------------------------------------------------
+ // Invocation
+
+ sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false, kUniqueId);
+
+ // --------------------------------------------------------------------
+ // Postconditions
+
+ // The display should have been added to the current state
+ ASSERT_TRUE(hasCurrentDisplayState(displayToken));
+ const auto& display = getCurrentDisplayState(displayToken);
+ EXPECT_TRUE(display.isVirtual());
+ EXPECT_FALSE(display.isSecure);
+ EXPECT_EQ(display.uniqueId, "virtual:package:id");
+ EXPECT_EQ(kDisplayName.c_str(), display.displayName);
// --------------------------------------------------------------------
// Cleanup conditions
@@ -134,78 +164,78 @@
// Requesting 0 tells SF not to do anything, i.e., default to refresh as physical displays
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRate0) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 60.f;
- const float kRequestedRefreshRate = 0.f;
- const float kExpectedAdjustedRefreshRate = 0.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 60.f;
+ constexpr float kRequestedRefreshRate = 0.f;
+ constexpr float kExpectedAdjustedRefreshRate = 0.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting negative refresh rate, will be ignored, same as requesting 0
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNegative) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 60.f;
- const float kRequestedRefreshRate = -60.f;
- const float kExpectedAdjustedRefreshRate = 0.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 60.f;
+ constexpr float kRequestedRefreshRate = -60.f;
+ constexpr float kExpectedAdjustedRefreshRate = 0.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting a higher refresh rate than the pacesetter
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateHigh) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 60.f;
- const float kRequestedRefreshRate = 90.f;
- const float kExpectedAdjustedRefreshRate = 60.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 60.f;
+ constexpr float kRequestedRefreshRate = 90.f;
+ constexpr float kExpectedAdjustedRefreshRate = 60.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting the same refresh rate as the pacesetter
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateSame) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 60.f;
- const float kRequestedRefreshRate = 60.f;
- const float kExpectedAdjustedRefreshRate = 60.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 60.f;
+ constexpr float kRequestedRefreshRate = 60.f;
+ constexpr float kExpectedAdjustedRefreshRate = 60.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting a divisor (30) of the pacesetter (60) should be honored
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateDivisor) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 60.f;
- const float kRequestedRefreshRate = 30.f;
- const float kExpectedAdjustedRefreshRate = 30.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 60.f;
+ constexpr float kRequestedRefreshRate = 30.f;
+ constexpr float kExpectedAdjustedRefreshRate = 30.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting a non divisor (45) of the pacesetter (120) should round up to a divisor (60)
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisor) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 120.f;
- const float kRequestedRefreshRate = 45.f;
- const float kExpectedAdjustedRefreshRate = 60.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 120.f;
+ constexpr float kRequestedRefreshRate = 45.f;
+ constexpr float kExpectedAdjustedRefreshRate = 60.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
// Requesting a non divisor (75) of the pacesetter (120) should round up to pacesetter (120)
TEST_F(CreateDisplayTest, createDisplayWithRequestedRefreshRateNoneDivisorMax) {
- const String8 displayName("virtual.test");
- const uint64_t displayId = 123ull;
- const float kPacesetterDisplayRefreshRate = 120.f;
- const float kRequestedRefreshRate = 75.f;
- const float kExpectedAdjustedRefreshRate = 120.f;
- createDisplayWithRequestedRefreshRate(displayName, displayId, kPacesetterDisplayRefreshRate,
+ static const std::string kDisplayName("virtual.test");
+ constexpr uint64_t kDisplayId = 123ull;
+ constexpr float kPacesetterDisplayRefreshRate = 120.f;
+ constexpr float kRequestedRefreshRate = 75.f;
+ constexpr float kExpectedAdjustedRefreshRate = 120.f;
+ createDisplayWithRequestedRefreshRate(kDisplayName, kDisplayId, kPacesetterDisplayRefreshRate,
kRequestedRefreshRate, kExpectedAdjustedRefreshRate);
}
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index 93a3811..f8ad8e1 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -43,18 +43,18 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.destroyDisplay(existing.token());
+ EXPECT_EQ(NO_ERROR, mFlinger.destroyVirtualDisplay(existing.token()));
// --------------------------------------------------------------------
// Postconditions
- // The display should have been removed from the current state
+ // The display should have been removed from the current state.
EXPECT_FALSE(hasCurrentDisplayState(existing.token()));
- // Ths display should still exist in the drawing state
+ // Ths display should still exist in the drawing state.
EXPECT_TRUE(hasDrawingDisplayState(existing.token()));
- // The display transaction needed flasg should be set
+ // The display transaction needed flags should be set.
EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
}
@@ -67,7 +67,7 @@
// --------------------------------------------------------------------
// Invocation
- mFlinger.destroyDisplay(displayToken);
+ EXPECT_EQ(NAME_NOT_FOUND, mFlinger.destroyVirtualDisplay(displayToken));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
index 15a6db6..0c3e875 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -76,6 +76,7 @@
mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
.setRefreshRateSelector(std::move(selectorPtr))
.inject(std::move(vsyncController), std::move(vsyncTracker));
+ mDisplayId = mDisplay->getPhysicalId();
// isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
// will call setActiveConfig instead of setActiveConfigWithConstraints.
@@ -112,7 +113,11 @@
protected:
void setupScheduler(std::shared_ptr<scheduler::RefreshRateSelector>);
+ auto& dmc() { return mFlinger.mutableDisplayModeController(); }
+
sp<DisplayDevice> mDisplay, mOuterDisplay;
+ PhysicalDisplayId mDisplayId;
+
mock::EventThread* mAppEventThread;
static constexpr DisplayModeId kModeId60{0};
@@ -167,17 +172,17 @@
TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90, false, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
@@ -187,8 +192,8 @@
Mock::VerifyAndClearExpectations(mComposer);
- EXPECT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that the next commit will complete the mode change and send
// a onModeChanged event to the framework.
@@ -198,23 +203,23 @@
mFlinger.commit();
Mock::VerifyAndClearExpectations(mAppEventThread);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, changeRefreshRateOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90, true, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
@@ -226,8 +231,8 @@
mFlinger.commit();
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90);
}
TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
@@ -236,8 +241,8 @@
// Test that if we call setDesiredDisplayModeSpecs while a previous mode change
// is still being processed the later call will be respected.
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
@@ -252,46 +257,45 @@
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId120, false, 0, 180));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId120);
mFlinger.commit();
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId120);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId120);
mFlinger.commit();
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId120);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId120);
}
TEST_F(DisplayModeSwitchingTest, changeResolutionOnActiveDisplayWithoutRefreshRequired) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
mFlinger.onActiveDisplayChanged(nullptr, *mDisplay);
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
mock::createDisplayModeSpecs(kModeId90_4K, false, 0, 120));
- ASSERT_TRUE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getDesiredMode()->mode.modePtr->getId(), kModeId90_4K);
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId60);
+ ASSERT_TRUE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getDesiredMode(mDisplayId)->mode.modePtr->getId(), kModeId90_4K);
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId60);
// Verify that next commit will call setActiveConfigWithConstraints in HWC
// and complete the mode change.
const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
EXPECT_SET_ACTIVE_CONFIG(PrimaryDisplayVariant::HWC_DISPLAY_ID, kModeId90_4K);
- EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
+ EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplayId, true));
- // Misc expecations. We don't need to enforce these method calls, but since the helper methods
- // already set expectations we should add new ones here, otherwise the test will fail.
+ // Override expectations set up by PrimaryDisplayVariant.
EXPECT_CALL(*mConsumer,
setDefaultBufferSize(static_cast<uint32_t>(kResolution4K.getWidth()),
static_cast<uint32_t>(kResolution4K.getHeight())))
@@ -304,30 +308,29 @@
injectFakeNativeWindowSurfaceFactory();
PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
- const auto displayToken = mDisplay->getDisplayToken().promote();
-
mFlinger.commit();
- // The DisplayDevice will be destroyed and recreated,
- // so we need to update with the new instance.
- mDisplay = mFlinger.getDisplay(displayToken);
-
- EXPECT_FALSE(mDisplay->getDesiredMode());
- EXPECT_EQ(mDisplay->getActiveMode().modePtr->getId(), kModeId90_4K);
+ EXPECT_FALSE(dmc().getDesiredMode(mDisplayId));
+ EXPECT_EQ(dmc().getActiveMode(mDisplayId).modePtr->getId(), kModeId90_4K);
}
MATCHER_P2(ModeSwitchingTo, flinger, modeId, "") {
- if (!arg->getDesiredMode()) {
+ const auto displayId = arg->getPhysicalId();
+ auto& dmc = flinger->mutableDisplayModeController();
+
+ if (!dmc.getDesiredMode(displayId)) {
*result_listener << "No desired mode";
return false;
}
- if (arg->getDesiredMode()->mode.modePtr->getId() != modeId) {
+ if (dmc.getDesiredMode(displayId)->mode.modePtr->getId() != modeId) {
*result_listener << "Unexpected desired mode " << ftl::to_underlying(modeId);
return false;
}
- if (!flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
+ // VsyncModulator should react to mode switches on the pacesetter display.
+ if (displayId == flinger->scheduler()->pacesetterDisplayId() &&
+ !flinger->scheduler()->vsyncModulator().isVsyncConfigEarly()) {
*result_listener << "VsyncModulator did not shift to early phase";
return false;
}
@@ -335,8 +338,10 @@
return true;
}
-MATCHER_P(ModeSettledTo, modeId, "") {
- if (const auto desiredOpt = arg->getDesiredMode()) {
+MATCHER_P2(ModeSettledTo, dmc, modeId, "") {
+ const auto displayId = arg->getPhysicalId();
+
+ if (const auto desiredOpt = dmc->getDesiredMode(displayId)) {
*result_listener << "Unsettled desired mode "
<< ftl::to_underlying(desiredOpt->mode.modePtr->getId());
return false;
@@ -344,7 +349,7 @@
ftl::FakeGuard guard(kMainThreadContext);
- if (arg->getActiveMode().modePtr->getId() != modeId) {
+ if (dmc->getActiveMode(displayId).modePtr->getId() != modeId) {
*result_listener << "Settled to unexpected active mode " << ftl::to_underlying(modeId);
return false;
}
@@ -365,14 +370,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- // Only the inner display is powered on.
- mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -385,41 +390,45 @@
0.f, 120.f)));
EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
-
- mFlinger.commit();
-
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
-
- mFlinger.commit();
-
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
-
- innerDisplay->setPowerMode(hal::PowerMode::OFF);
- outerDisplay->setPowerMode(hal::PowerMode::ON);
-
- // Only the outer display is powered on.
- mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
-
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
-
EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId60, false,
+ 0.f, 120.f)));
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId60);
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
}
TEST_F(DisplayModeSwitchingTest, innerAndOuterDisplay) {
@@ -434,16 +443,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- outerDisplay->setPowerMode(hal::PowerMode::ON);
+ mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
- // Both displays are powered on.
- mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
-
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -469,13 +476,13 @@
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringModeSet) {
EXPECT_TRUE(mDisplay->isPoweredOn());
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId60));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
@@ -485,7 +492,7 @@
EXPECT_THAT(mDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
// Power off the display before the mode has been set.
- mDisplay->setPowerMode(hal::PowerMode::OFF);
+ mFlinger.setPowerModeInternal(mDisplay, hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
@@ -498,7 +505,7 @@
mFlinger.commit();
- EXPECT_THAT(mDisplay, ModeSettledTo(kModeId90));
+ EXPECT_THAT(mDisplay, ModeSettledTo(&dmc(), kModeId90));
}
TEST_F(DisplayModeSwitchingTest, powerOffDuringConcurrentModeSet) {
@@ -514,16 +521,14 @@
EXPECT_TRUE(innerDisplay->isPoweredOn());
EXPECT_FALSE(outerDisplay->isPoweredOn());
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
- outerDisplay->setPowerMode(hal::PowerMode::ON);
+ mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::ON);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
- // Both displays are powered on.
- mFlinger.onActiveDisplayChanged(nullptr, *innerDisplay);
-
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId60));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId60));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
EXPECT_EQ(NO_ERROR,
mFlinger.setDesiredDisplayModeSpecs(innerDisplay->getDisplayToken().promote(),
@@ -539,40 +544,42 @@
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
// Power off the outer display before the mode has been set.
- outerDisplay->setPowerMode(hal::PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::OFF);
const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
EXPECT_SET_ACTIVE_CONFIG(kInnerDisplayHwcId, kModeId90);
-
- mFlinger.commit();
-
- // Powering off the inactive display should abort the mode set.
- EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
-
- mFlinger.commit();
-
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId120));
-
- innerDisplay->setPowerMode(hal::PowerMode::OFF);
- outerDisplay->setPowerMode(hal::PowerMode::ON);
-
- // Only the outer display is powered on.
- mFlinger.onActiveDisplayChanged(innerDisplay.get(), *outerDisplay);
-
EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId60);
mFlinger.commit();
- // The mode set should resume once the display becomes active.
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
+ // Powering off the inactive display should not abort the mode set.
+ EXPECT_THAT(innerDisplay, ModeSwitchingTo(&mFlinger, kModeId90));
EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId60));
mFlinger.commit();
- EXPECT_THAT(innerDisplay, ModeSettledTo(kModeId90));
- EXPECT_THAT(outerDisplay, ModeSettledTo(kModeId60));
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId60));
+
+ mFlinger.setPowerModeInternal(innerDisplay, hal::PowerMode::OFF);
+ mFlinger.setPowerModeInternal(outerDisplay, hal::PowerMode::ON);
+
+ EXPECT_EQ(NO_ERROR,
+ mFlinger.setDesiredDisplayModeSpecs(outerDisplay->getDisplayToken().promote(),
+ mock::createDisplayModeSpecs(kModeId120, false,
+ 0.f, 120.f)));
+
+ EXPECT_SET_ACTIVE_CONFIG(kOuterDisplayHwcId, kModeId120);
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSwitchingTo(&mFlinger, kModeId120));
+
+ mFlinger.commit();
+
+ EXPECT_THAT(innerDisplay, ModeSettledTo(&dmc(), kModeId90));
+ EXPECT_THAT(outerDisplay, ModeSettledTo(&dmc(), kModeId120));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
index 4e9fba7..f424133 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_GetDisplayStatsTest.cpp
@@ -42,8 +42,8 @@
}
TEST_F(SurfaceFlingerGetDisplayStatsTest, invalidToken) {
- const String8 displayName("fakeDisplay");
- sp<IBinder> displayToken = mFlinger.createDisplay(displayName, false);
+ static const std::string kDisplayName("fakeDisplay");
+ sp<IBinder> displayToken = mFlinger.createVirtualDisplay(kDisplayName, false /*isSecure*/);
DisplayStatInfo info;
status_t status = mFlinger.getDisplayStats(displayToken, &info);
EXPECT_EQ(status, NAME_NOT_FOUND);
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
index c0796df..933d03d 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetupNewDisplayDeviceInternalTest.cpp
@@ -232,7 +232,9 @@
// Invocation
DisplayDeviceState state;
- if constexpr (constexpr auto connectionType = Case::Display::CONNECTION_TYPE::value) {
+
+ constexpr auto kConnectionTypeOpt = Case::Display::CONNECTION_TYPE::value;
+ if constexpr (kConnectionTypeOpt) {
const auto displayId = PhysicalDisplayId::tryCast(Case::Display::DISPLAY_ID::get());
ASSERT_TRUE(displayId);
const auto hwcDisplayId = Case::Display::HWC_DISPLAY_ID_OPT::value;
@@ -255,10 +257,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,
+ *kConnectionTypeOpt, 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);
@@ -286,9 +293,12 @@
EXPECT_EQ(Case::Display::DISPLAY_FLAGS & DisplayDevice::eReceivesInput,
device->receivesInput());
- if constexpr (Case::Display::CONNECTION_TYPE::value) {
+ if constexpr (kConnectionTypeOpt) {
ftl::FakeGuard guard(kMainThreadContext);
- EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID, device->getActiveMode().modePtr->getHwcId());
+ EXPECT_EQ(Case::Display::HWC_ACTIVE_CONFIG_ID,
+ mFlinger.mutableDisplayModeController()
+ .getActiveMode(device->getPhysicalId())
+ .modePtr->getHwcId());
}
}
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1e02c67..f063809 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -53,7 +53,7 @@
factory, selectorPtr->getActiveMode().fps, timeStats) {
const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId();
registerDisplay(displayId, std::move(selectorPtr), std::move(controller),
- std::move(tracker));
+ std::move(tracker), displayId);
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
// Execute task to prevent broken promise exception on destruction.
@@ -85,14 +85,16 @@
void registerDisplay(
PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
+ std::optional<PhysicalDisplayId> activeDisplayIdOpt = {},
std::shared_ptr<VSyncTracker> vsyncTracker = std::make_shared<mock::VSyncTracker>()) {
registerDisplay(displayId, std::move(selectorPtr),
- std::make_unique<mock::VsyncController>(), vsyncTracker);
+ std::make_unique<mock::VsyncController>(), vsyncTracker,
+ activeDisplayIdOpt.value_or(displayId));
}
void registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr,
std::unique_ptr<VsyncController> controller,
- std::shared_ptr<VSyncTracker> tracker) {
+ std::shared_ptr<VSyncTracker> tracker, PhysicalDisplayId activeDisplayId) {
ftl::FakeGuard guard(kMainThreadContext);
Scheduler::registerDisplayInternal(displayId, std::move(selectorPtr),
std::shared_ptr<VsyncSchedule>(
@@ -101,14 +103,15 @@
mock::VSyncDispatch>(),
std::move(controller),
mockRequestHardwareVsync
- .AsStdFunction())));
+ .AsStdFunction())),
+ activeDisplayId);
}
testing::MockFunction<void(PhysicalDisplayId, bool)> mockRequestHardwareVsync;
- void unregisterDisplay(PhysicalDisplayId displayId) {
+ void setDisplayPowerMode(PhysicalDisplayId displayId, hal::PowerMode powerMode) {
ftl::FakeGuard guard(kMainThreadContext);
- Scheduler::unregisterDisplay(displayId);
+ Scheduler::setDisplayPowerMode(displayId, powerMode);
}
std::optional<PhysicalDisplayId> pacesetterDisplayId() const NO_THREAD_SAFETY_ANALYSIS {
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index bce7729..4197cbd 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,14 +40,15 @@
#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"
#include "Scheduler/MessageQueue.h"
#include "Scheduler/RefreshRateSelector.h"
-#include "StartPropertySetThread.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"
@@ -94,10 +97,6 @@
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override {
- return sp<StartPropertySetThread>::make(timestampPropertyValue);
- }
-
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override {
return sp<DisplayDevice>::make(creationArgs);
}
@@ -132,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);
}
@@ -192,6 +191,8 @@
void setupComposer(std::unique_ptr<Hwc2::Composer> composer) {
mFlinger->mCompositionEngine->setHwComposer(
std::make_unique<impl::HWComposer>(std::move(composer)));
+ mFlinger->mDisplayModeController.setHwComposer(
+ &mFlinger->mCompositionEngine->getHwComposer());
}
void setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor> powerAdvisor) {
@@ -202,6 +203,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 {
@@ -416,12 +422,21 @@
commit(kComposite);
}
- auto createDisplay(const String8& displayName, bool secure, float requestedRefreshRate = 0.0f) {
- return mFlinger->createDisplay(displayName, secure, requestedRefreshRate);
+ auto createVirtualDisplay(const std::string& displayName, bool isSecure,
+ float requestedRefreshRate = 0.0f) {
+ static const std::string kTestId =
+ "virtual:libsurfaceflinger_unittest:TestableSurfaceFlinger";
+ return mFlinger->createVirtualDisplay(displayName, isSecure, kTestId, requestedRefreshRate);
}
- auto destroyDisplay(const sp<IBinder>& displayToken) {
- return mFlinger->destroyDisplay(displayToken);
+ auto createVirtualDisplay(const std::string& displayName, bool isSecure,
+ const std::string& uniqueId, float requestedRefreshRate = 0.0f) {
+ return mFlinger->createVirtualDisplay(displayName, isSecure, uniqueId,
+ requestedRefreshRate);
+ }
+
+ auto destroyVirtualDisplay(const sp<IBinder>& displayToken) {
+ return mFlinger->destroyVirtualDisplay(displayToken);
}
auto getDisplay(const sp<IBinder>& displayToken) {
@@ -444,6 +459,7 @@
void commitTransactionsLocked(uint32_t transactionFlags) {
Mutex::Autolock lock(mFlinger->mStateLock);
ftl::FakeGuard guard(kMainThreadContext);
+ mFlinger->processDisplayChangesLocked();
mFlinger->commitTransactionsLocked(transactionFlags);
}
@@ -471,23 +487,28 @@
return mFlinger->setPowerModeInternal(display, mode);
}
- auto renderScreenImpl(std::shared_ptr<const RenderArea> renderArea,
- SurfaceFlinger::GetLayerSnapshotsFunction traverseLayers,
+ auto renderScreenImpl(const sp<DisplayDevice> display,
+ std::unique_ptr<const RenderArea> renderArea,
+ SurfaceFlinger::GetLayerSnapshotsFunction getLayerSnapshotsFn,
const std::shared_ptr<renderengine::ExternalTexture>& buffer,
bool regionSampling) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ ftl::FakeGuard guard(kMainThreadContext);
+
ScreenCaptureResults captureResults;
- return FTL_FAKE_GUARD(kMainThreadContext,
- mFlinger->renderScreenImpl(std::move(renderArea), traverseLayers,
- buffer, regionSampling,
- false /* grayscale */,
- false /* isProtected */, captureResults));
+ auto displayState = std::optional{display->getCompositionDisplay()->getState()};
+ auto layers = getLayerSnapshotsFn();
+ auto layerFEs = mFlinger->extractLayerFEs(layers);
+
+ return mFlinger->renderScreenImpl(std::move(renderArea), buffer, regionSampling,
+ false /* grayscale */, false /* isProtected */,
+ captureResults, displayState, layers, layerFEs);
}
auto traverseLayersInLayerStack(ui::LayerStack layerStack, int32_t uid,
std::unordered_set<uint32_t> excludeLayerIds,
const LayerVector::Visitor& visitor) {
- return mFlinger->SurfaceFlinger::traverseLayersInLayerStack(layerStack, uid,
- excludeLayerIds, visitor);
+ return mFlinger->traverseLayersInLayerStack(layerStack, uid, excludeLayerIds, visitor);
}
auto getDisplayNativePrimaries(const sp<IBinder>& displayToken,
@@ -497,9 +518,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();
}
@@ -518,7 +541,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() {
@@ -588,6 +613,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.
*/
@@ -603,15 +635,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();
@@ -637,6 +674,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; }
@@ -671,10 +709,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,
@@ -952,14 +989,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;
}
@@ -1001,8 +1038,9 @@
return *this;
}
- auto& skipRegisterDisplay() {
- mRegisterDisplay = false;
+ // Used to avoid overwriting mocks injected by TestableSurfaceFlinger::setupMockScheduler.
+ auto& skipSchedulerRegistration() {
+ mSchedulerRegistration = false;
return *this;
}
@@ -1015,12 +1053,23 @@
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;
- 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 =
@@ -1042,50 +1091,41 @@
mCreationArgs.refreshRateSelector =
std::make_shared<scheduler::RefreshRateSelector>(modes, activeModeId);
}
+
+ const auto activeModeOpt = modes.get(activeModeId);
+ LOG_ALWAYS_FATAL_IF(!activeModeOpt);
+
+ // Save a copy for use after `modes` is consumed.
+ const Fps refreshRate = 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);
+
+ mFlinger.mutableDisplayModeController().setActiveMode(*physicalId, activeModeId,
+ refreshRate, refreshRate);
+
+ if (mFlinger.scheduler() && mSchedulerRegistration) {
+ mFlinger.scheduler()->registerDisplay(*physicalId,
+ mCreationArgs.refreshRateSelector,
+ std::move(controller), std::move(tracker),
+ mFlinger.mutableActiveDisplayId());
+ }
}
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);
- }
-
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
@@ -1097,7 +1137,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/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index d4d5b32..85b61f8 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -94,7 +94,7 @@
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
layer->setBuffer(externalTexture, bufferData, postTime, /*desiredPresentTime*/ 30, false,
- dequeueTime, FrameTimelineInfo{});
+ FrameTimelineInfo{});
commitTransaction(layer.get());
nsecs_t latchTime = 25;
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/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index 5046a86..46733b9 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -99,7 +99,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
acquireFence->signalForTest(12);
commitTransaction(layer.get());
@@ -134,7 +134,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -151,7 +151,7 @@
2ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
nsecs_t end = systemTime();
acquireFence2->signalForTest(12);
@@ -197,7 +197,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
acquireFence->signalForTest(12);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
@@ -232,7 +232,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -275,7 +275,7 @@
FrameTimelineInfo ftInfo3;
ftInfo3.vsyncId = 3;
ftInfo3.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo3);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo3);
EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -320,7 +320,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -335,7 +335,7 @@
1ULL /* bufferId */,
HAL_PIXEL_FORMAT_RGBA_8888,
0ULL /*usage*/);
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfo);
acquireFence2->signalForTest(12);
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -372,7 +372,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture1, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture1, bufferData, 10, 20, false, ftInfo);
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX;
@@ -392,7 +392,7 @@
FrameTimelineInfo ftInfoInv;
ftInfoInv.vsyncId = FrameTimelineInfo::INVALID_VSYNC_ID;
ftInfoInv.inputEventId = 0;
- layer->setBuffer(externalTexture2, bufferData, 10, 20, false, std::nullopt, ftInfoInv);
+ layer->setBuffer(externalTexture2, bufferData, 10, 20, false, ftInfoInv);
auto dropEndTime1 = systemTime();
EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size());
ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX);
@@ -413,7 +413,7 @@
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
- layer->setBuffer(externalTexture3, bufferData, 10, 20, false, std::nullopt, ftInfo2);
+ layer->setBuffer(externalTexture3, bufferData, 10, 20, false, ftInfo2);
auto dropEndTime2 = systemTime();
acquireFence3->signalForTest(12);
@@ -462,7 +462,7 @@
FrameTimelineInfo ftInfo;
ftInfo.vsyncId = 1;
ftInfo.inputEventId = 0;
- layer->setBuffer(externalTexture, bufferData, 10, 20, false, std::nullopt, ftInfo);
+ layer->setBuffer(externalTexture, bufferData, 10, 20, false, ftInfo);
FrameTimelineInfo ftInfo2;
ftInfo2.vsyncId = 2;
ftInfo2.inputEventId = 0;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c22deab..3b09554 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -50,10 +50,11 @@
bool needsMoreSamples() const final { return false; }
bool isVSyncInPhase(nsecs_t, Fps) final { return false; }
void setDisplayModePtr(ftl::NonNull<DisplayModePtr>) final {}
- void setRenderRate(Fps) final {}
+ void setRenderRate(Fps, bool) final {}
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/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 6b9ea56..5109ea6 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -535,6 +535,28 @@
}
}
+TEST_F(VSyncPredictorTest, isVSyncInPhaseWithRenderRate) {
+ SET_FLAG_FOR_TEST(flags::vrr_bugfix_24q4, true);
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + mPeriod), Eq(mNow + 2 * mPeriod));
+
+ const auto renderRateFps = Fps::fromPeriodNsecs(mPeriod * 2);
+ tracker.setRenderRate(renderRateFps, /*applyImmediately*/ true);
+
+ EXPECT_FALSE(tracker.isVSyncInPhase(mNow, renderRateFps));
+ EXPECT_TRUE(tracker.isVSyncInPhase(mNow + mPeriod, renderRateFps));
+ EXPECT_FALSE(tracker.isVSyncInPhase(mNow + 2 * mPeriod, renderRateFps));
+ EXPECT_TRUE(tracker.isVSyncInPhase(mNow + 3 * mPeriod, renderRateFps));
+}
+
TEST_F(VSyncPredictorTest, isVSyncInPhaseForDivisors) {
auto last = mNow;
for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
@@ -620,15 +642,15 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod), /*applyImmediately*/ false);
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 4 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 7 * mPeriod));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 7 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 2100), Eq(mNow + 3 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3100), Eq(mNow + 6 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 4100), Eq(mNow + 6 * mPeriod));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) {
@@ -640,7 +662,7 @@
tracker.addVsyncTimestamp(mNow);
}
- tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod), /*applyImmediately*/ false);
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod));
@@ -651,6 +673,181 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod));
}
+TEST_F(VSyncPredictorTest, setRenderRateHighIsAppliedImmediately) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // commit to a vsync in the future
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(3500), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2500), /*applyImmediately*/ false);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(8000, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(12000, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(15500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(19000, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+ EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(9500, vrrTracker.nextAnticipatedVSyncTimeFrom(8000, 8000));
+ EXPECT_EQ(11500, vrrTracker.nextAnticipatedVSyncTimeFrom(10000, 10000));
+ EXPECT_EQ(13500, vrrTracker.nextAnticipatedVSyncTimeFrom(12000, 12000));
+ EXPECT_EQ(16500, vrrTracker.nextAnticipatedVSyncTimeFrom(15500, 15500));
+ EXPECT_EQ(20500, vrrTracker.nextAnticipatedVSyncTimeFrom(19000, 19000));
+
+ // matches the previous cadence
+ EXPECT_EQ(21500, vrrTracker.nextAnticipatedVSyncTimeFrom(20500, 20500));
+}
+
+TEST_F(VSyncPredictorTest, minFramePeriodDoesntApplyWhenSameWithRefreshRate) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(1000);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // Assume that the last vsync is wrong due to a vsync drift. It shouldn't matter.
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1700));
+}
+
+TEST_F(VSyncPredictorTest, setRenderRateExplicitAppliedImmediately) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
+ EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000, 1000));
+
+ // commit to a vsync in the future
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 2000));
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000));
+ EXPECT_EQ(7000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(9000, vrrTracker.nextAnticipatedVSyncTimeFrom(7000, 7000));
+}
+
+TEST_F(VSyncPredictorTest, selectsClosestVsyncAfterInactivity) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(5000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4700));
+ EXPECT_EQ(10000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+
+ mClock->setNow(50000);
+ EXPECT_EQ(50500, vrrTracker.nextAnticipatedVSyncTimeFrom(50000, 10000));
+}
+
+TEST_F(VSyncPredictorTest, returnsCorrectVsyncWhenLastIsNot) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto vsyncRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), vsyncRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(Fps::fromPeriodNsecs(1000), /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+ EXPECT_EQ(2500, vrrTracker.nextAnticipatedVSyncTimeFrom(1234, 1234));
+}
+
TEST_F(VSyncPredictorTest, adjustsVrrTimeline) {
SET_FLAG_FOR_TEST(flags::vrr_config, true);
@@ -669,7 +866,7 @@
VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
kMinimumSamplesForPrediction, kOutlierTolerancePercent};
- vrrTracker.setRenderRate(minFrameRate);
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
vrrTracker.addVsyncTimestamp(0);
EXPECT_EQ(1000, vrrTracker.nextAnticipatedVSyncTimeFrom(700));
EXPECT_EQ(2000, vrrTracker.nextAnticipatedVSyncTimeFrom(1000));
@@ -688,6 +885,56 @@
EXPECT_EQ(10500, vrrTracker.nextAnticipatedVSyncTimeFrom(9000, 7000));
}
+TEST_F(VSyncPredictorTest, adjustsVrrTimelineTwoClients) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+
+ const int32_t kGroup = 0;
+ const auto kResolution = ui::Size(1920, 1080);
+ const auto refreshRate = Fps::fromPeriodNsecs(500);
+ const auto minFrameRate = Fps::fromPeriodNsecs(1000);
+ hal::VrrConfig vrrConfig;
+ vrrConfig.minFrameIntervalNs = minFrameRate.getPeriodNsecs();
+ const ftl::NonNull<DisplayModePtr> kMode =
+ ftl::as_non_null(createDisplayModeBuilder(DisplayModeId(0), refreshRate, kGroup,
+ kResolution, DEFAULT_DISPLAY_ID)
+ .setVrrConfig(std::move(vrrConfig))
+ .build());
+
+ VSyncPredictor vrrTracker{std::make_unique<ClockWrapper>(mClock), kMode, kHistorySize,
+ kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+
+ vrrTracker.setRenderRate(minFrameRate, /*applyImmediately*/ false);
+ vrrTracker.addVsyncTimestamp(0);
+
+ // App runs ahead
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3000, 3000));
+ EXPECT_EQ(5000, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+
+ // SF starts to catch up
+ EXPECT_EQ(3000, vrrTracker.nextAnticipatedVSyncTimeFrom(2700));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(3000), TimePoint::fromNs(0));
+
+ // SF misses last frame (3000) and observes that when committing (4000)
+ EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
+ EXPECT_EQ(4000, vrrTracker.nextAnticipatedVSyncTimeFrom(3700));
+ vrrTracker.onFrameMissed(TimePoint::fromNs(4000));
+
+ // SF wakes up again instead of the (4000) missed frame
+ EXPECT_EQ(4500, vrrTracker.nextAnticipatedVSyncTimeFrom(4000, 4000));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(4500), TimePoint::fromNs(4500));
+
+ // Timeline shifted. The app needs to get the next frame at (7500) as its last frame (6500) will
+ // be presented at (7500)
+ EXPECT_EQ(7500, vrrTracker.nextAnticipatedVSyncTimeFrom(6000, 6000));
+ EXPECT_EQ(5500, vrrTracker.nextAnticipatedVSyncTimeFrom(4500, 4500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(5500), TimePoint::fromNs(4500));
+
+ EXPECT_EQ(8500, vrrTracker.nextAnticipatedVSyncTimeFrom(7500, 7500));
+ EXPECT_EQ(6500, vrrTracker.nextAnticipatedVSyncTimeFrom(5500, 5500));
+ vrrTracker.onFrameBegin(TimePoint::fromNs(6500), TimePoint::fromNs(5500));
+}
+
TEST_F(VSyncPredictorTest, renderRateIsPreservedForCommittedVsyncs) {
tracker.addVsyncTimestamp(1000);
@@ -695,21 +942,22 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
- tracker.setRenderRate(Fps::fromPeriodNsecs(2000));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ false);
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
- tracker.setRenderRate(Fps::fromPeriodNsecs(3000));
+ tracker.setRenderRate(Fps::fromPeriodNsecs(3000), /*applyImmediately*/ false);
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(6000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(7000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(8000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(10000));
- EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(10000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(7001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(8001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(11000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(10001), Eq(11000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(11001), Eq(14000));
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(12001), Eq(14000));
@@ -721,6 +969,27 @@
EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(6001), Eq(8000));
}
+// b/329310308
+TEST_F(VSyncPredictorTest, renderRateChangeAfterAppliedImmediately) {
+ tracker.addVsyncTimestamp(1000);
+
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(2000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(2001), Eq(3000));
+
+ tracker.setRenderRate(Fps::fromPeriodNsecs(2000), /*applyImmediately*/ true);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000));
+
+ tracker.setRenderRate(Fps::fromPeriodNsecs(4000), /*applyImmediately*/ false);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1), Eq(1000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(1001), Eq(3000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(3001), Eq(5000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(5001), Eq(9000));
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(9001), Eq(13000));
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
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/MockIPowerHintSession.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
deleted file mode 100644
index 27564b2..0000000
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.h
+++ /dev/null
@@ -1,58 +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 "binder/Status.h"
-
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wconversion"
-#include <aidl/android/hardware/power/IPower.h>
-#pragma clang diagnostic pop
-
-#include <gmock/gmock.h>
-
-using aidl::android::hardware::power::IPowerHintSession;
-using aidl::android::hardware::power::SessionConfig;
-using aidl::android::hardware::power::SessionHint;
-using aidl::android::hardware::power::SessionMode;
-using android::binder::Status;
-
-using namespace aidl::android::hardware::power;
-
-namespace android::Hwc2::mock {
-
-class MockIPowerHintSession : public IPowerHintSession {
-public:
- MockIPowerHintSession();
-
- MOCK_METHOD(ndk::SpAIBinder, asBinder, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, pause, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, resume, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, close, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceVersion, (int32_t * version), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getInterfaceHash, (std::string * hash), (override));
- MOCK_METHOD(bool, isRemote, (), (override));
- MOCK_METHOD(ndk::ScopedAStatus, updateTargetWorkDuration, (int64_t), (override));
- MOCK_METHOD(ndk::ScopedAStatus, reportActualWorkDuration, (const ::std::vector<WorkDuration>&),
- (override));
- MOCK_METHOD(ndk::ScopedAStatus, sendHint, (SessionHint), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setThreads, (const ::std::vector<int32_t>&), (override));
- MOCK_METHOD(ndk::ScopedAStatus, setMode, (SessionMode, bool), (override));
- MOCK_METHOD(ndk::ScopedAStatus, getSessionConfig, (SessionConfig * _aidl_return), (override));
-};
-
-} // namespace android::Hwc2::mock
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/DisplayHardware/MockPowerHalController.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
index ae41e7e..af1862d 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHalController.h
@@ -44,10 +44,10 @@
MOCK_METHOD(HalResult<void>, setBoost, (aidl::android::hardware::power::Boost, int32_t),
(override));
MOCK_METHOD(HalResult<void>, setMode, (aidl::android::hardware::power::Mode, bool), (override));
- MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ MOCK_METHOD(HalResult<std::shared_ptr<android::power::PowerHintSessionWrapper>>,
createHintSession, (int32_t, int32_t, const std::vector<int32_t>&, int64_t),
(override));
- MOCK_METHOD(HalResult<std::shared_ptr<aidl::android::hardware::power::IPowerHintSession>>,
+ MOCK_METHOD(HalResult<std::shared_ptr<android::power::PowerHintSessionWrapper>>,
createHintSessionWithConfig,
(int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
int64_t durationNanos, aidl::android::hardware::power::SessionTag tag,
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
similarity index 75%
rename from services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
rename to services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
index 770bc15..d383283 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockIPowerHintSession.cpp
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * 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.
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-#include "mock/DisplayHardware/MockIPowerHintSession.h"
+#include "mock/DisplayHardware/MockPowerHintSessionWrapper.h"
namespace android::Hwc2::mock {
// Explicit default instantiation is recommended.
-MockIPowerHintSession::MockIPowerHintSession() = default;
+MockPowerHintSessionWrapper::MockPowerHintSessionWrapper()
+ : power::PowerHintSessionWrapper(nullptr) {}
} // namespace android::Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
new file mode 100644
index 0000000..bc6950c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerHintSessionWrapper.h
@@ -0,0 +1,55 @@
+/*
+ * 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 "binder/Status.h"
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include <aidl/android/hardware/power/IPower.h>
+#include <powermanager/PowerHintSessionWrapper.h>
+#pragma clang diagnostic pop
+
+#include <gmock/gmock.h>
+
+using aidl::android::hardware::power::IPowerHintSession;
+using aidl::android::hardware::power::SessionConfig;
+using aidl::android::hardware::power::SessionHint;
+using aidl::android::hardware::power::SessionMode;
+using android::binder::Status;
+
+using namespace aidl::android::hardware::power;
+
+namespace android::Hwc2::mock {
+
+class MockPowerHintSessionWrapper : public power::PowerHintSessionWrapper {
+public:
+ MockPowerHintSessionWrapper();
+
+ MOCK_METHOD(power::HalResult<void>, updateTargetWorkDuration, (int64_t), (override));
+ MOCK_METHOD(power::HalResult<void>, reportActualWorkDuration,
+ (const ::std::vector<WorkDuration>&), (override));
+ MOCK_METHOD(power::HalResult<void>, pause, (), (override));
+ MOCK_METHOD(power::HalResult<void>, resume, (), (override));
+ MOCK_METHOD(power::HalResult<void>, close, (), (override));
+ MOCK_METHOD(power::HalResult<void>, sendHint, (SessionHint), (override));
+ MOCK_METHOD(power::HalResult<void>, setThreads, (const ::std::vector<int32_t>&), (override));
+ MOCK_METHOD(power::HalResult<void>, setMode, (SessionMode, bool), (override));
+ MOCK_METHOD(power::HalResult<SessionConfig>, getSessionConfig, (), (override));
+};
+
+} // namespace android::Hwc2::mock
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/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index 4ca0542..8f21cdb 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -30,6 +30,8 @@
MOCK_METHOD(void, onChoreographerAttached, (), (override));
MOCK_METHOD(void, onExpectedPresentTimePosted, (TimePoint, ftl::NonNull<DisplayModePtr>, Fps),
(override));
+ MOCK_METHOD(void, onCommitNotComposited, (PhysicalDisplayId), (override));
+ MOCK_METHOD(void, vrrDisplayIdle, (bool), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
@@ -39,6 +41,8 @@
void triggerOnFrameRateOverridesChanged() override {}
void onChoreographerAttached() override {}
void onExpectedPresentTimePosted(TimePoint, ftl::NonNull<DisplayModePtr>, Fps) override {}
+ void onCommitNotComposited(PhysicalDisplayId) override {}
+ void vrrDisplayIdle(bool) override {}
};
} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
index 6d10a5c..4f44d1b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h
@@ -36,10 +36,11 @@
MOCK_METHOD(bool, needsMoreSamples, (), (const, override));
MOCK_METHOD(bool, isVSyncInPhase, (nsecs_t, Fps), (override));
MOCK_METHOD(void, setDisplayModePtr, (ftl::NonNull<DisplayModePtr>), (override));
- MOCK_METHOD(void, setRenderRate, (Fps), (override));
+ MOCK_METHOD(void, setRenderRate, (Fps, bool), (override));
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/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/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 c08cfc6..715c221 100644
--- a/services/vibratorservice/test/test_utils.h
+++ b/services/vibratorservice/test/test_utils.h
@@ -119,4 +119,4 @@
} // 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 e9204ab..ef213f0 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -967,14 +967,20 @@
PFN_vkVoidFunction GetDeviceProcAddr(VkDevice device, const char* pName) {
const ProcHook* hook = GetProcHook(pName);
+ PFN_vkVoidFunction drv_func = GetData(device).driver.GetDeviceProcAddr(device, pName);
+
if (!hook)
- return GetData(device).driver.GetDeviceProcAddr(device, pName);
+ return drv_func;
if (hook->type != ProcHook::DEVICE) {
ALOGE("internal vkGetDeviceProcAddr called for %s", pName);
return nullptr;
}
+ // Don't hook if we don't have a device entry function below for the core function.
+ if (!drv_func && (hook->extension >= ProcHook::EXTENSION_CORE_1_0))
+ return nullptr;
+
return (GetData(device).hook_extensions[hook->extension]) ? hook->proc
: nullptr;
}
@@ -1375,6 +1381,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
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 1314193..9e67725 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -555,8 +555,7 @@
return native_format;
}
-DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace,
- PixelFormat pixelFormat) {
+DataSpace GetNativeDataspace(VkColorSpaceKHR colorspace, VkFormat format) {
switch (colorspace) {
case VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
return DataSpace::SRGB;
@@ -575,7 +574,7 @@
case VK_COLOR_SPACE_BT709_NONLINEAR_EXT:
return DataSpace::SRGB;
case VK_COLOR_SPACE_BT2020_LINEAR_EXT:
- if (pixelFormat == PixelFormat::RGBA_FP16) {
+ if (format == VK_FORMAT_R16G16B16A16_SFLOAT) {
return DataSpace::BT2020_LINEAR_EXTENDED;
} else {
return DataSpace::BT2020_LINEAR;
@@ -764,21 +763,20 @@
{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
};
+ VkFormat format = VK_FORMAT_UNDEFINED;
if (colorspace_ext) {
for (VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspace) {
- if (GetNativeDataspace(colorSpace, GetNativePixelFormat(
- VK_FORMAT_R8G8B8A8_UNORM)) !=
- DataSpace::UNKNOWN) {
+ format = VK_FORMAT_R8G8B8A8_UNORM;
+ if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) {
all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_UNORM, colorSpace});
+ VkSurfaceFormatKHR{format, colorSpace});
}
- if (GetNativeDataspace(colorSpace, GetNativePixelFormat(
- VK_FORMAT_R8G8B8A8_SRGB)) !=
- DataSpace::UNKNOWN) {
+ format = VK_FORMAT_R8G8B8A8_SRGB;
+ if (GetNativeDataspace(colorSpace, format) != DataSpace::UNKNOWN) {
all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_R8G8B8A8_SRGB, colorSpace});
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
}
@@ -787,78 +785,73 @@
// Android users. This includes the ANGLE team (a layered implementation of
// OpenGL-ES).
+ format = VK_FORMAT_R5G6B5_UNORM_PACK16;
desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
if (AHardwareBuffer_isSupported(&desc)) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
if (colorspace_ext) {
for (VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspace) {
- if (GetNativeDataspace(
- colorSpace,
- GetNativePixelFormat(VK_FORMAT_R5G6B5_UNORM_PACK16)) !=
+ if (GetNativeDataspace(colorSpace, format) !=
DataSpace::UNKNOWN) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R5G6B5_UNORM_PACK16, colorSpace});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
}
}
+ format = VK_FORMAT_R16G16B16A16_SFLOAT;
desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
if (AHardwareBuffer_isSupported(&desc)) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
if (colorspace_ext) {
for (VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspace) {
- if (GetNativeDataspace(
- colorSpace,
- GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) !=
+ if (GetNativeDataspace(colorSpace, format) !=
DataSpace::UNKNOWN) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
for (
VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspaceOnFP16SurfaceOnly) {
- if (GetNativeDataspace(
- colorSpace,
- GetNativePixelFormat(VK_FORMAT_R16G16B16A16_SFLOAT)) !=
+ if (GetNativeDataspace(colorSpace, format) !=
DataSpace::UNKNOWN) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R16G16B16A16_SFLOAT, colorSpace});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
}
}
+ format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
if (AHardwareBuffer_isSupported(&desc)) {
all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
- VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
if (colorspace_ext) {
for (VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspace) {
- if (GetNativeDataspace(
- colorSpace, GetNativePixelFormat(
- VK_FORMAT_A2B10G10R10_UNORM_PACK32)) !=
+ if (GetNativeDataspace(colorSpace, format) !=
DataSpace::UNKNOWN) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_A2B10G10R10_UNORM_PACK32, colorSpace});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
}
}
+ format = VK_FORMAT_R8_UNORM;
desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM;
if (AHardwareBuffer_isSupported(&desc)) {
if (colorspace_ext) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R8_UNORM, VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, VK_COLOR_SPACE_PASS_THROUGH_EXT});
}
}
@@ -877,22 +870,18 @@
rgba10x6_formats_ext = true;
}
}
+ format = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16;
desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
if (AHardwareBuffer_isSupported(&desc) && rgba10x6_formats_ext) {
all_formats.emplace_back(
- VkSurfaceFormatKHR{VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
- VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ VkSurfaceFormatKHR{format, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
if (colorspace_ext) {
for (VkColorSpaceKHR colorSpace :
colorSpaceSupportedByVkEXTSwapchainColorspace) {
- if (GetNativeDataspace(
- colorSpace,
- GetNativePixelFormat(
- VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16)) !=
+ if (GetNativeDataspace(colorSpace, format) !=
DataSpace::UNKNOWN) {
- all_formats.emplace_back(VkSurfaceFormatKHR{
- VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16,
- colorSpace});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{format, colorSpace});
}
}
}
@@ -1185,7 +1174,8 @@
pSurfaceFormat);
if (surfaceCompressionProps &&
- driver.GetPhysicalDeviceImageFormatProperties2KHR) {
+ (driver.GetPhysicalDeviceImageFormatProperties2KHR ||
+ driver.GetPhysicalDeviceImageFormatProperties2)) {
VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = {};
imageFormatInfo.sType =
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2;
@@ -1216,7 +1206,7 @@
imageFormatProps.pNext = &compressionProps;
VkResult compressionRes =
- driver.GetPhysicalDeviceImageFormatProperties2KHR(
+ GetPhysicalDeviceImageFormatProperties2(
physicalDevice, &imageFormatInfo,
&imageFormatProps);
if (compressionRes == VK_SUCCESS) {
@@ -1670,8 +1660,8 @@
PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
- DataSpace native_dataspace =
- GetNativeDataspace(create_info->imageColorSpace, native_pixel_format);
+ DataSpace native_dataspace = GetNativeDataspace(
+ create_info->imageColorSpace, create_info->imageFormat);
if (native_dataspace == DataSpace::UNKNOWN) {
ALOGE(
"CreateSwapchainKHR(VkSwapchainCreateInfoKHR.imageColorSpace = %d) "
diff --git a/vulkan/scripts/driver_generator.py b/vulkan/scripts/driver_generator.py
index 78b550c..48c0ae9 100644
--- a/vulkan/scripts/driver_generator.py
+++ b/vulkan/scripts/driver_generator.py
@@ -239,6 +239,8 @@
f.write(gencom.indent(2) + gencom.base_ext_name(ext) + ',\n')
f.write('\n')
+ # EXTENSION_CORE_xxx API list must be the last set of enums after the extensions.
+ # This allows to easily identify "a" core function hook
for version in gencom.version_code_list:
f.write(gencom.indent(2) + 'EXTENSION_CORE_' + version + ',\n')