Merge "Add flag to cmd for dynamic services" into rvc-dev
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index cf75bba..544e26c 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -571,81 +571,6 @@
return true;
}
-// Poke all the binder-enabled processes in the system to get them to re-read
-// their system properties.
-static bool pokeBinderServices()
-{
- sp<IServiceManager> sm = defaultServiceManager();
- Vector<String16> services = sm->listServices();
- for (size_t i = 0; i < services.size(); i++) {
- sp<IBinder> obj = sm->checkService(services[i]);
- if (obj != nullptr) {
- Parcel data;
- if (obj->transact(IBinder::SYSPROPS_TRANSACTION, data,
- nullptr, 0) != OK) {
- if (false) {
- // XXX: For some reason this fails on tablets trying to
- // poke the "phone" service. It's not clear whether some
- // are expected to fail.
- String8 svc(services[i]);
- fprintf(stderr, "error poking binder service %s\n",
- svc.string());
- return false;
- }
- }
- }
- }
- return true;
-}
-
-// Poke all the HAL processes in the system to get them to re-read
-// their system properties.
-static void pokeHalServices()
-{
- using ::android::hidl::base::V1_0::IBase;
- using ::android::hidl::manager::V1_0::IServiceManager;
- using ::android::hardware::hidl_string;
- using ::android::hardware::Return;
-
- sp<IServiceManager> sm = ::android::hardware::defaultServiceManager();
-
- if (sm == nullptr) {
- fprintf(stderr, "failed to get IServiceManager to poke hal services\n");
- return;
- }
-
- auto listRet = sm->list([&](const auto &interfaces) {
- for (size_t i = 0; i < interfaces.size(); i++) {
- string fqInstanceName = interfaces[i];
- string::size_type n = fqInstanceName.find('/');
- if (n == std::string::npos || interfaces[i].size() == n+1)
- continue;
- hidl_string fqInterfaceName = fqInstanceName.substr(0, n);
- hidl_string instanceName = fqInstanceName.substr(n+1, std::string::npos);
- Return<sp<IBase>> interfaceRet = sm->get(fqInterfaceName, instanceName);
- if (!interfaceRet.isOk()) {
- // ignore
- continue;
- }
-
- sp<IBase> interface = interfaceRet;
- if (interface == nullptr) {
- // ignore
- continue;
- }
-
- auto notifyRet = interface->notifySyspropsChanged();
- if (!notifyRet.isOk()) {
- // ignore
- }
- }
- });
- if (!listRet.isOk()) {
- // TODO(b/34242478) fix this when we determine the correct ACL
- //fprintf(stderr, "failed to list services: %s\n", listRet.description().c_str());
- }
-}
-
// Set the trace tags that userland tracing uses, and poke the running
// processes to pick up the new value.
static bool setTagsProperty(uint64_t tags)
@@ -876,10 +801,6 @@
}
ok &= setAppCmdlineProperty(&packageList[0]);
ok &= setTagsProperty(tags);
-#if !ATRACE_SHMEM
- ok &= pokeBinderServices();
- pokeHalServices();
-#endif
if (g_tracePdx) {
ok &= ServiceUtility::PokeServices();
}
@@ -891,8 +812,6 @@
{
setTagsProperty(0);
clearAppProperties();
- pokeBinderServices();
- pokeHalServices();
if (g_tracePdx) {
ServiceUtility::PokeServices();
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index 87ea520..a0b9cbb 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -84,7 +84,8 @@
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd,
int bugreport_mode,
- const sp<IDumpstateListener>& listener) {
+ const sp<IDumpstateListener>& listener,
+ bool is_screenshot_requested) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
// Ensure there is only one bugreport in progress at a time.
@@ -118,9 +119,9 @@
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
- screenshot_fd);
+ screenshot_fd, is_screenshot_requested);
- if (bugreport_fd.get() == -1 || (options->do_fb && screenshot_fd.get() == -1)) {
+ if (bugreport_fd.get() == -1 || (options->do_screenshot && screenshot_fd.get() == -1)) {
MYLOGE("Invalid filedescriptor");
signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
}
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 6dc0225..ac8d3ac 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -41,7 +41,8 @@
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
android::base::unique_fd bugreport_fd,
android::base::unique_fd screenshot_fd, int bugreport_mode,
- const sp<IDumpstateListener>& listener) override;
+ const sp<IDumpstateListener>& listener,
+ bool is_screenshot_requested) override;
// No-op
binder::Status cancelBugreport();
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index 3f359c8..ba008bb 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -64,10 +64,12 @@
* @param screenshotFd the file to which screenshot should be written
* @param bugreportMode the mode that specifies other run time options; must be one of above
* @param listener callback for updates; optional
+ * @param isScreenshotRequested indicates screenshot is requested or not
*/
void startBugreport(int callingUid, @utf8InCpp String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener);
+ int bugreportMode, IDumpstateListener listener,
+ boolean isScreenshotRequested);
/*
* Cancels the bugreport currently in progress.
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index e486460..e17f18e 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -61,4 +61,9 @@
* Called when taking bugreport finishes successfully.
*/
void onFinished();
+
+ /**
+ * Called when screenshot is taken.
+ */
+ void onScreenshotTaken(boolean success);
}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 814a4ed..01b7e63 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -647,6 +647,24 @@
std::lock_guard<std::mutex> lock(lock_);
result_ = APPROVED;
MYLOGD("User approved consent to share bugreport\n");
+
+ // Maybe copy screenshot so calling app can display the screenshot to the user as soon as
+ // consent is granted.
+ if (ds.options_->is_screenshot_copied) {
+ return android::binder::Status::ok();
+ }
+
+ if (!ds.options_->do_screenshot || ds.options_->screenshot_fd.get() == -1 ||
+ !ds.do_early_screenshot_) {
+ return android::binder::Status::ok();
+ }
+
+ bool copy_succeeded = android::os::CopyFileToFd(ds.screenshot_path_,
+ ds.options_->screenshot_fd.get());
+ ds.options_->is_screenshot_copied = copy_succeeded;
+ if (copy_succeeded) {
+ android::os::UnlinkAndLogOnError(ds.screenshot_path_);
+ }
return android::binder::Status::ok();
}
@@ -1407,7 +1425,7 @@
/* Dump Bluetooth HCI logs */
ds.AddDir("/data/misc/bluetooth/logs", true);
- if (ds.options_->do_fb && !ds.do_early_screenshot_) {
+ if (ds.options_->do_screenshot && !ds.do_early_screenshot_) {
MYLOGI("taking late screenshot\n");
ds.TakeScreenshot();
}
@@ -1459,6 +1477,8 @@
ds.AddDir(WMTRACE_DATA_DIR, false);
}
+ ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
+
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
/* Migrate the ril_dumpstate to a device specific dumpstate? */
@@ -1587,7 +1607,6 @@
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
- ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
ds.AddDir(PROFILE_DATA_DIR_REF, true);
@@ -1710,6 +1729,8 @@
RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"telephony.registry"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
if (include_sensitive_info) {
// Contains raw IP addresses, omit from reports on user builds.
RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
@@ -2149,8 +2170,8 @@
ds.base_name_ += "-wifi";
}
- if (ds.options_->do_fb) {
- ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.png" : ".png");
+ if (ds.options_->do_screenshot) {
+ ds.screenshot_path_ = ds.GetPath(ds.CalledByApi() ? "-png.tmp" : ".png");
}
ds.tmp_path_ = ds.GetPath(".tmp");
ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
@@ -2169,7 +2190,7 @@
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
if (ds.options_->do_zip_file) {
- ds.path_ = ds.GetPath(ds.CalledByApi() ? "-tmp.zip" : ".zip");
+ ds.path_ = ds.GetPath(ds.CalledByApi() ? "-zip.tmp" : ".zip");
MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
create_parent_dirs(ds.path_.c_str());
ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
@@ -2228,44 +2249,47 @@
}
}
-static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options) {
+static void SetOptionsFromMode(Dumpstate::BugreportMode mode, Dumpstate::DumpOptions* options,
+ bool is_screenshot_requested) {
+ // Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for
+ // default system screenshots.
options->bugreport_mode = ModeToString(mode);
switch (mode) {
case Dumpstate::BugreportMode::BUGREPORT_FULL:
- options->do_fb = true;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::FULL;
break;
case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
// Currently, the dumpstate binder is only used by Shell to update progress.
options->do_start_service = true;
options->do_progress_updates = true;
- options->do_fb = false;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::INTERACTIVE;
break;
case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
options->do_vibrate = false;
options->is_remote_mode = true;
- options->do_fb = false;
+ options->do_screenshot = false;
options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
options->do_start_service = true;
options->do_progress_updates = true;
options->do_zip_file = true;
- options->do_fb = true;
+ options->do_screenshot = is_screenshot_requested;
options->dumpstate_hal_mode = DumpstateMode::WEAR;
break;
// TODO(b/148168577) rename TELEPHONY everywhere to CONNECTIVITY.
case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
options->telephony_only = true;
options->do_progress_updates = true;
- options->do_fb = false;
+ options->do_screenshot = false;
options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
options->wifi_only = true;
options->do_zip_file = true;
- options->do_fb = false;
+ options->do_screenshot = false;
options->dumpstate_hal_mode = DumpstateMode::WIFI;
break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
@@ -2275,12 +2299,13 @@
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
MYLOGI(
- "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+ "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_screenshot: %d "
"is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
"wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
"args: %s\n",
options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
- options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+ options.do_screenshot, options.is_remote_mode, options.show_header_only,
+ options.do_start_service,
options.telephony_only, options.wifi_only, options.do_progress_updates,
options.bugreport_fd.get(), options.bugreport_mode.c_str(),
toString(options.dumpstate_hal_mode).c_str(), options.args.c_str());
@@ -2288,7 +2313,8 @@
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
const android::base::unique_fd& bugreport_fd_in,
- const android::base::unique_fd& screenshot_fd_in) {
+ const android::base::unique_fd& screenshot_fd_in,
+ bool is_screenshot_requested) {
// In the new API world, date is always added; output is always a zip file.
// TODO(111441001): remove these options once they are obsolete.
do_add_date = true;
@@ -2298,7 +2324,7 @@
bugreport_fd.reset(dup(bugreport_fd_in.get()));
screenshot_fd.reset(dup(screenshot_fd_in.get()));
- SetOptionsFromMode(bugreport_mode, this);
+ SetOptionsFromMode(bugreport_mode, this, is_screenshot_requested);
}
Dumpstate::RunStatus Dumpstate::DumpOptions::Initialize(int argc, char* argv[]) {
@@ -2313,7 +2339,7 @@
case 'S': use_control_socket = true; break;
case 'v': show_header_only = true; break;
case 'q': do_vibrate = false; break;
- case 'p': do_fb = true; break;
+ case 'p': do_screenshot = true; break;
case 'P': do_progress_updates = true; break;
case 'R': is_remote_mode = true; break;
case 'V': break; // compatibility no-op
@@ -2543,11 +2569,6 @@
Vibrate(150);
}
- if (options_->do_fb && do_early_screenshot_) {
- MYLOGI("taking early screenshot\n");
- TakeScreenshot();
- }
-
if (options_->do_zip_file && zip_file != nullptr) {
if (chown(path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of zip file %s: %s\n", path_.c_str(),
@@ -2593,19 +2614,20 @@
PrintHeader();
if (options_->telephony_only) {
+ MaybeTakeEarlyScreenshot();
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly(calling_package);
DumpstateBoard();
} else if (options_->wifi_only) {
+ MaybeTakeEarlyScreenshot();
MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateWifiOnly();
} else {
- // Invoking the critical dumpsys calls before DumpTraces() to try and
- // keep the system stats as close to its initial state as possible.
+ // Invoke critical dumpsys first to preserve system state, before doing anything else.
RunDumpsysCritical();
- // Run consent check only after critical dumpsys has finished -- so the consent
- // isn't going to pollute the system state / logs.
+ // Take screenshot and get consent only after critical dumpsys has finished.
+ MaybeTakeEarlyScreenshot();
MaybeCheckUserConsent(calling_uid, calling_package);
// Dump state for the default case. This also drops root.
@@ -2640,7 +2662,9 @@
MYLOGI("User denied consent. Returning\n");
return status;
}
- if (options_->do_fb && options_->screenshot_fd.get() != -1) {
+ if (options_->do_screenshot &&
+ options_->screenshot_fd.get() != -1 &&
+ !options_->is_screenshot_copied) {
bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
options_->screenshot_fd.get());
if (copy_succeeded) {
@@ -2694,6 +2718,14 @@
: RunStatus::OK;
}
+void Dumpstate::MaybeTakeEarlyScreenshot() {
+ if (!options_->do_screenshot || !do_early_screenshot_) {
+ return;
+ }
+
+ TakeScreenshot();
+}
+
void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
if (calling_uid == AID_SHELL || !CalledByApi()) {
// No need to get consent for shell triggered dumpstates, or not through
@@ -3630,6 +3662,11 @@
} else {
MYLOGE("Failed to take screenshot on %s\n", real_path.c_str());
}
+ if (listener_ != nullptr) {
+ // Show a visual indication to indicate screenshot is taken via
+ // IDumpstateListener.onScreenshotTaken()
+ listener_->onScreenshotTaken(status == 0);
+ }
}
bool is_dir(const char* pathname) {
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 111c098..7b8d282 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -359,7 +359,8 @@
// Writes bugreport content to a socket; only flatfile format is supported.
bool use_socket = false;
bool use_control_socket = false;
- bool do_fb = false;
+ bool do_screenshot = false;
+ bool is_screenshot_copied = false;
bool is_remote_mode = false;
bool show_header_only = false;
bool do_start_service = false;
@@ -389,7 +390,8 @@
/* Initializes options from the requested mode. */
void Initialize(BugreportMode bugreport_mode, const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd);
+ const android::base::unique_fd& screenshot_fd,
+ bool is_screenshot_requested);
/* Returns true if the options set so far are consistent. */
bool ValidateOptions() const;
@@ -494,6 +496,8 @@
RunStatus DumpstateDefaultAfterCritical();
+ void MaybeTakeEarlyScreenshot();
+
void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
// Removes the in progress files output files (tmp file, zip/txt file, screenshot),
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index dac90d9..047a1c3 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -167,6 +167,12 @@
return binder::Status::ok();
}
+ binder::Status onScreenshotTaken(bool success) override {
+ std::lock_guard<std::mutex> lock(lock_);
+ dprintf(out_fd_, "\rResult of taking screenshot: %s", success ? "success" : "failure");
+ return binder::Status::ok();
+ }
+
bool getIsFinished() {
std::lock_guard<std::mutex> lock(lock_);
return is_finished_;
@@ -445,7 +451,7 @@
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener, true);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
EXPECT_TRUE(status.isOk());
@@ -482,7 +488,7 @@
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
2000, // invalid bugreport mode
- listener);
+ listener, false);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
// The service should have died, freeing itself up for a new invocation.
@@ -513,13 +519,13 @@
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1, true);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
- Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2, true);
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 76b9960..2efb130 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -65,6 +65,7 @@
MOCK_METHOD1(onProgress, binder::Status(int32_t progress));
MOCK_METHOD1(onError, binder::Status(int32_t error_code));
MOCK_METHOD0(onFinished, binder::Status());
+ MOCK_METHOD1(onScreenshotTaken, binder::Status(bool success));
protected:
MOCK_METHOD0(onAsBinder, IBinder*());
@@ -173,7 +174,7 @@
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
@@ -199,7 +200,7 @@
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
EXPECT_FALSE(options_.show_header_only);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
@@ -225,16 +226,16 @@
EXPECT_FALSE(options_.do_zip_file);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
@@ -249,12 +250,12 @@
}
TEST_F(DumpOptionsTest, InitializeInteractiveBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_start_service);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_TRUE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
// Other options retain default values
@@ -266,12 +267,12 @@
}
TEST_F(DumpOptionsTest, InitializeRemoteBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_REMOTE, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
// Other options retain default values
@@ -282,9 +283,9 @@
}
TEST_F(DumpOptionsTest, InitializeWearBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_start_service);
@@ -299,9 +300,9 @@
}
TEST_F(DumpOptionsTest, InitializeTelephonyBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_TELEPHONY, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
@@ -316,9 +317,9 @@
}
TEST_F(DumpOptionsTest, InitializeWifiBugReport) {
- options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd);
+ options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.wifi_only);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
@@ -346,7 +347,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
@@ -384,7 +385,7 @@
// Other options retain default values
EXPECT_FALSE(options_.show_header_only);
EXPECT_TRUE(options_.do_vibrate);
- EXPECT_FALSE(options_.do_fb);
+ EXPECT_FALSE(options_.do_screenshot);
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
@@ -407,7 +408,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.show_header_only);
EXPECT_FALSE(options_.do_vibrate);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.is_remote_mode);
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 70bbc33..1af6edd 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -1273,11 +1273,6 @@
Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path,
bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
- // We don't create an image for secondary dex files.
- if (is_secondary_dex) {
- return Dex2oatFileWrapper();
- }
-
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp
index eefbe4f..d773790 100644
--- a/cmds/installd/otapreopt.cpp
+++ b/cmds/installd/otapreopt.cpp
@@ -138,10 +138,10 @@
return 4;
}
- PrepareEnvironment();
+ PrepareEnvironmentVariables();
- if (!PrepareBootImage(/* force */ false)) {
- LOG(ERROR) << "Failed preparing boot image.";
+ if (!EnsureBootImageAndDalvikCache()) {
+ LOG(ERROR) << "Bad boot image.";
return 5;
}
@@ -302,7 +302,7 @@
return parameters_.ReadArguments(argc, const_cast<const char**>(argv));
}
- void PrepareEnvironment() {
+ void PrepareEnvironmentVariables() {
environ_.push_back(StringPrintf("BOOTCLASSPATH=%s", boot_classpath_.c_str()));
environ_.push_back(StringPrintf("ANDROID_DATA=%s", GetOTADataDirectory().c_str()));
environ_.push_back(StringPrintf("ANDROID_ROOT=%s", android_root_.c_str()));
@@ -312,9 +312,8 @@
}
}
- // Ensure that we have the right boot image. The first time any app is
- // compiled, we'll try to generate it.
- bool PrepareBootImage(bool force) const {
+ // Ensure that we have the right boot image and cache file structures.
+ bool EnsureBootImageAndDalvikCache() const {
if (parameters_.instruction_set == nullptr) {
LOG(ERROR) << "Instruction set missing.";
return false;
@@ -340,34 +339,19 @@
}
}
- // Check whether we have files in /data.
+ // Clear cached artifacts.
+ ClearDirectory(isa_path);
+
+ // Check whether we have a boot image.
// TODO: check that the files are correct wrt/ jars.
- std::string art_path = isa_path + "/system@framework@boot.art";
- std::string oat_path = isa_path + "/system@framework@boot.oat";
- bool cleared = false;
- if (access(art_path.c_str(), F_OK) == 0 && access(oat_path.c_str(), F_OK) == 0) {
- // Files exist, assume everything is alright if not forced. Otherwise clean up.
- if (!force) {
- return true;
- }
- ClearDirectory(isa_path);
- cleared = true;
+ std::string preopted_boot_art_path =
+ StringPrintf("/apex/com.android.art/javalib/%s/boot.art", isa);
+ if (access(preopted_boot_art_path.c_str(), F_OK) != 0) {
+ PLOG(ERROR) << "Bad access() to " << preopted_boot_art_path;
+ return false;
}
- // Check whether we have an image in /system.
- // TODO: check that the files are correct wrt/ jars.
- std::string preopted_boot_art_path = StringPrintf("/system/framework/%s/boot.art", isa);
- if (access(preopted_boot_art_path.c_str(), F_OK) == 0) {
- // Note: we ignore |force| here.
- return true;
- }
-
-
- if (!cleared) {
- ClearDirectory(isa_path);
- }
-
- return Dex2oatBootImage(boot_classpath_, art_path, oat_path, isa);
+ return true;
}
static bool CreatePath(const std::string& path) {
@@ -432,77 +416,6 @@
CHECK_EQ(0, closedir(c_dir)) << "Unable to close directory.";
}
- bool Dex2oatBootImage(const std::string& boot_cp,
- const std::string& art_path,
- const std::string& oat_path,
- const char* isa) const {
- // This needs to be kept in sync with ART, see art/runtime/gc/space/image_space.cc.
- std::vector<std::string> cmd;
- cmd.push_back(kDex2oatPath);
- cmd.push_back(StringPrintf("--image=%s", art_path.c_str()));
- for (const std::string& boot_part : Split(boot_cp, ":")) {
- cmd.push_back(StringPrintf("--dex-file=%s", boot_part.c_str()));
- }
- cmd.push_back(StringPrintf("--oat-file=%s", oat_path.c_str()));
-
- int32_t base_offset = ChooseRelocationOffsetDelta(
- art::imagevalues::GetImageMinBaseAddressDelta(),
- art::imagevalues::GetImageMaxBaseAddressDelta());
- cmd.push_back(StringPrintf("--base=0x%x",
- art::imagevalues::GetImageBaseAddress() + base_offset));
-
- cmd.push_back(StringPrintf("--instruction-set=%s", isa));
-
- // These things are pushed by AndroidRuntime, see frameworks/base/core/jni/AndroidRuntime.cpp.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xms",
- "-Xms",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-Xmx",
- "-Xmx",
- true,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-filter",
- "--compiler-filter=",
- false,
- cmd);
- cmd.push_back("--profile-file=/system/etc/boot-image.prof");
- // TODO: Compiled-classes.
- const std::string* extra_opts =
- system_properties_.GetProperty("dalvik.vm.image-dex2oat-flags");
- if (extra_opts != nullptr) {
- std::vector<std::string> extra_vals = Split(*extra_opts, " ");
- cmd.insert(cmd.end(), extra_vals.begin(), extra_vals.end());
- }
- // TODO: Should we lower this? It's usually set close to max, because
- // normally there's not much else going on at boot.
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-threads",
- "-j",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty("dalvik.vm.image-dex2oat-cpu-set",
- "--cpu-set=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.variant", isa).c_str(),
- "--instruction-set-variant=",
- false,
- cmd);
- AddCompilerOptionFromSystemProperty(
- StringPrintf("dalvik.vm.isa.%s.features", isa).c_str(),
- "--instruction-set-features=",
- false,
- cmd);
-
- std::string error_msg;
- bool result = Exec(cmd, &error_msg);
- if (!result) {
- LOG(ERROR) << "Could not generate boot image: " << error_msg;
- }
- return result;
- }
-
static const char* ParseNull(const char* arg) {
return (strcmp(arg, "!") == 0) ? nullptr : arg;
}
@@ -592,22 +505,6 @@
return 0;
}
- // If the dexopt failed, we may have a stale boot image from a previous OTA run.
- // Then regenerate and retry.
- if (WEXITSTATUS(dexopt_result) ==
- static_cast<int>(::art::dex2oat::ReturnCode::kCreateRuntime)) {
- if (!PrepareBootImage(/* force */ true)) {
- LOG(ERROR) << "Forced boot image creating failed. Original error return was "
- << dexopt_result;
- return dexopt_result;
- }
-
- int dexopt_result_boot_image_retry = Dexopt();
- if (dexopt_result_boot_image_retry == 0) {
- return 0;
- }
- }
-
// If this was a profile-guided run, we may have profile version issues. Try to downgrade,
// if possible.
if ((parameters_.dexopt_flags & DEXOPT_PROFILE_GUIDED) == 0) {
diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp
index a7ccf64..fb11cee 100644
--- a/cmds/lshal/ListCommand.cpp
+++ b/cmds/lshal/ListCommand.cpp
@@ -206,9 +206,12 @@
static bool scanBinderContext(pid_t pid,
const std::string &contextName,
std::function<void(const std::string&)> eachLine) {
- std::ifstream ifs("/d/binder/proc/" + std::to_string(pid));
+ std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid));
if (!ifs.is_open()) {
- return false;
+ ifs.open("/d/binder/proc/" + std::to_string(pid));
+ if (!ifs.is_open()) {
+ return false;
+ }
}
static const std::regex kContextLine("^context (\\w+)$");
@@ -403,7 +406,7 @@
return false;
}
- if (fqInstance.getPackage() == gIBaseFqName.package()) {
+ if (fqInstance.getPackage() == "android.hidl.base") {
return true; // always remove IBase from manifest
}
diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h
index b3ed23d..acc0dcf 100644
--- a/cmds/lshal/ListCommand.h
+++ b/cmds/lshal/ListCommand.h
@@ -104,7 +104,8 @@
Status fetchBinderizedEntry(const sp<::android::hidl::manager::V1_0::IServiceManager> &manager,
TableEntry *entry);
- // Get relevant information for a PID by parsing files under /d/binder.
+ // Get relevant information for a PID by parsing files under
+ // /dev/binderfs/binder_logs or /d/binder.
// It is a virtual member function so that it can be mocked.
virtual bool getPidInfo(pid_t serverPid, PidInfo *info) const;
// Retrieve from mCachedPidInfos and call getPidInfo if necessary.
diff --git a/data/etc/android.hardware.device_unique_attestation.xml b/data/etc/android.hardware.device_unique_attestation.xml
new file mode 100644
index 0000000..309be7a
--- /dev/null
+++ b/data/etc/android.hardware.device_unique_attestation.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- Feature for devices with Keymaster that support unique attestation. -->
+<permissions>
+ <feature name="android.hardware.device_unique_attestation" />
+</permissions>
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
new file mode 100644
index 0000000..1acc59d
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-0.5-crop-11.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
new file mode 100644
index 0000000..4ab9ca4
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-169.png
Binary files differ
diff --git a/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
new file mode 100644
index 0000000..d74e673
--- /dev/null
+++ b/docs/images/camera2/metadata/android.control.zoomRatio/zoom-ratio-2-crop-43.png
Binary files differ
diff --git a/include/android/bitmap.h b/include/android/bitmap.h
index 727a4af..2631b14 100644
--- a/include/android/bitmap.h
+++ b/include/android/bitmap.h
@@ -227,6 +227,7 @@
AndroidBitmap_CompressWriteFunc fn) __INTRODUCED_IN(30);
struct AHardwareBuffer;
+typedef struct AHardwareBuffer AHardwareBuffer;
/**
* Retrieve the native object associated with a HARDWARE Bitmap.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0f4b4d9..3247fa1 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -109,7 +109,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef int (*AThermal_StatusCallback)(void *data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 8ca178c..7ca9031 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -404,6 +404,13 @@
*/
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;
+
private:
// True if touch resampling is enabled.
const bool mResampleTouch;
diff --git a/include/ui/FatVector.h b/include/ui/FatVector.h
deleted file mode 120000
index c2047c0..0000000
--- a/include/ui/FatVector.h
+++ /dev/null
@@ -1 +0,0 @@
-../../libs/ui/include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/binder/ActivityManager.cpp b/libs/binder/ActivityManager.cpp
index 5e4c98f..4f2709d 100644
--- a/libs/binder/ActivityManager.cpp
+++ b/libs/binder/ActivityManager.cpp
@@ -98,6 +98,15 @@
return PROCESS_STATE_UNKNOWN;
}
+bool ActivityManager::isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+{
+ sp<IActivityManager> service = getService();
+ if (service != nullptr) {
+ return service->isUidActiveOrForeground(uid, callingPackage);
+ }
+ return false;
+}
+
status_t ActivityManager::linkToDeath(const sp<IBinder::DeathRecipient>& recipient) {
sp<IActivityManager> service = getService();
if (service != nullptr) {
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index bc541f4..e6cfeb4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -158,7 +158,9 @@
filegroup {
name: "libbinder_aidl",
srcs: [
+ "aidl/android/content/pm/IPackageChangeObserver.aidl",
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/content/pm/PackageChangeEvent.aidl",
"aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index aeca12b..2174ce2 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -30,24 +30,10 @@
namespace android {
-namespace {
-
-#if defined(__BRILLO__)
-// Because Brillo has no application model, security policy is managed
-// statically (at build time) with SELinux controls.
-// As a consequence, it also never runs the AppOpsManager service.
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_ALLOWED;
-#else
-const int APP_OPS_MANAGER_UNAVAILABLE_MODE = AppOpsManager::MODE_IGNORED;
-#endif // defined(__BRILLO__)
-
-} // namespace
-
-static String16 _appops("appops");
-static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gClientId;
-
static const sp<IBinder>& getClientId() {
+ static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+ static sp<IBinder> gClientId;
+
pthread_mutex_lock(&gClientIdMutex);
if (gClientId == nullptr) {
gClientId = new BBinder();
@@ -56,22 +42,13 @@
return gClientId;
}
-thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
-thread_local int32_t uidOfThisBinderTransaction = -1;
-
-// Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
-uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
-
AppOpsManager::AppOpsManager()
{
}
-#if defined(__BRILLO__)
-// There is no AppOpsService on Brillo
-sp<IAppOpsService> AppOpsManager::getService() { return NULL; }
-#else
sp<IAppOpsService> AppOpsManager::getService()
{
+ static String16 _appops("appops");
std::lock_guard<Mutex> scoped_lock(mLock);
int64_t startTime = 0;
@@ -96,14 +73,13 @@
}
return service;
}
-#endif // defined(__BRILLO__)
int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage)
{
sp<IAppOpsService> service = getService();
return service != nullptr
? service->checkOperation(op, uid, callingPackage)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ : AppOpsManager::MODE_IGNORED;
}
int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid,
@@ -111,7 +87,7 @@
sp<IAppOpsService> service = getService();
return service != nullptr
? service->checkAudioOperation(op, usage, uid, callingPackage)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ : AppOpsManager::MODE_IGNORED;
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) {
@@ -120,12 +96,12 @@
}
int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId, const String16& message) {
+ const std::unique_ptr<String16>& attributionTag, const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->noteOperation(op, uid, callingPackage, featureId, shouldCollectNotes(op),
- message)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ ? service->noteOperation(op, uid, callingPackage, attributionTag,
+ shouldCollectNotes(op), message)
+ : AppOpsManager::MODE_IGNORED;
return mode;
}
@@ -137,13 +113,13 @@
}
int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
? service->startOperation(getClientId(), op, uid, callingPackage,
- featureId, startIfModeDefault, shouldCollectNotes(op), message)
- : APP_OPS_MANAGER_UNAVAILABLE_MODE;
+ attributionTag, startIfModeDefault, shouldCollectNotes(op), message)
+ : AppOpsManager::MODE_IGNORED;
return mode;
}
@@ -153,10 +129,10 @@
}
void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& callingFeatureId) {
+ const std::unique_ptr<String16>& attributionTag) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
+ service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag);
}
}
@@ -192,6 +168,9 @@
// check it the appops needs to be collected and cache result
bool AppOpsManager::shouldCollectNotes(int32_t opcode) {
+ // Whether an appop should be collected: 0 == not initialized, 1 == don't note, 2 == note
+ static uint8_t appOpsToNote[AppOpsManager::_NUM_OP] = {0};
+
if (appOpsToNote[opcode] == 0) {
if (getService()->shouldCollectNotes(opcode)) {
appOpsToNote[opcode] = 2;
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index 2f6e9c3..e0fb543 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -141,7 +141,7 @@
// ---------------------------------------------------------------------------
-BBinder::BBinder() : mExtras(nullptr)
+BBinder::BBinder() : mExtras(nullptr), mStability(0)
{
}
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index f16c39c..d2b9b8f 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -138,6 +138,7 @@
BpBinder::BpBinder(int32_t handle, int32_t trackedUid)
: mHandle(handle)
+ , mStability(0)
, mAlive(1)
, mObitsSent(0)
, mObituaries(nullptr)
diff --git a/libs/binder/IActivityManager.cpp b/libs/binder/IActivityManager.cpp
index 1eb5363..9e1249b 100644
--- a/libs/binder/IActivityManager.cpp
+++ b/libs/binder/IActivityManager.cpp
@@ -104,6 +104,18 @@
}
return reply.readInt32();
}
+
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IActivityManager::getInterfaceDescriptor());
+ data.writeInt32(uid);
+ data.writeString16(callingPackage);
+ remote()->transact(IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION, data, &reply);
+ // fail on exception
+ if (reply.readExceptionCode() != 0) return false;
+ return reply.readInt32() == 1;
+ }
};
// ------------------------------------------------------------------------------------
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index a5555a3..0714723 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -47,14 +47,14 @@
}
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
data.writeString16(message);
remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply);
@@ -64,7 +64,7 @@
}
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId,
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -72,7 +72,7 @@
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
data.writeInt32(startIfModeDefault ? 1 : 0);
data.writeInt32(shouldCollectAsyncNotedOp ? 1 : 0);
data.writeString16(message);
@@ -83,14 +83,14 @@
}
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId) {
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
data.writeStrongBinder(token);
data.writeInt32(code);
data.writeInt32(uid);
data.writeString16(packageName);
- data.writeString16(featureId);
+ data.writeString16(attributionTag);
remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply);
}
@@ -182,11 +182,11 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
- int32_t res = noteOperation(code, uid, packageName, featureId,
+ int32_t res = noteOperation(code, uid, packageName, attributionTag,
shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
@@ -198,12 +198,12 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
bool startIfModeDefault = data.readInt32() == 1;
bool shouldCollectAsyncNotedOp = data.readInt32() == 1;
String16 message = data.readString16();
- int32_t res = startOperation(token, code, uid, packageName, featureId,
+ int32_t res = startOperation(token, code, uid, packageName, attributionTag,
startIfModeDefault, shouldCollectAsyncNotedOp, message);
reply->writeNoException();
reply->writeInt32(res);
@@ -215,9 +215,9 @@
int32_t code = data.readInt32();
int32_t uid = data.readInt32();
String16 packageName = data.readString16();
- std::unique_ptr<String16> featureId;
- data.readString16(&featureId);
- finishOperation(token, code, uid, packageName, featureId);
+ std::unique_ptr<String16> attributionTag;
+ data.readString16(&attributionTag);
+ finishOperation(token, code, uid, packageName, attributionTag);
reply->writeNoException();
return NO_ERROR;
} break;
diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp
index 7ce5e36..e1565fa 100644
--- a/libs/binder/Stability.cpp
+++ b/libs/binder/Stability.cpp
@@ -15,6 +15,9 @@
*/
#include <binder/Stability.h>
+#include <binder/BpBinder.h>
+#include <binder/Binder.h>
+
namespace android {
namespace internal {
@@ -78,11 +81,12 @@
if (currentStability == stability) return OK;
- binder->attachObject(
- reinterpret_cast<void*>(&Stability::get),
- reinterpret_cast<void*>(stability),
- nullptr /*cleanupCookie*/,
- nullptr /*cleanup function*/);
+ BBinder* local = binder->localBinder();
+ if (local != nullptr) {
+ local->mStability = static_cast<int32_t>(stability);
+ } else {
+ binder->remoteBinder()->mStability = static_cast<int32_t>(stability);
+ }
return OK;
}
@@ -90,8 +94,12 @@
Stability::Level Stability::get(IBinder* binder) {
if (binder == nullptr) return UNDECLARED;
- return static_cast<Level>(reinterpret_cast<intptr_t>(
- binder->findObject(reinterpret_cast<void*>(&Stability::get))));
+ BBinder* local = binder->localBinder();
+ if (local != nullptr) {
+ return static_cast<Stability::Level>(local->mStability);
+ }
+
+ return static_cast<Stability::Level>(binder->remoteBinder()->mStability);
}
bool Stability::check(int32_t provided, Level required) {
diff --git a/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
new file mode 100644
index 0000000..6929a6c
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/IPackageChangeObserver.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.content.pm.PackageChangeEvent;
+
+/**
+ * This is a non-blocking notification when a package has changed.
+ *
+ * @hide
+ */
+oneway interface IPackageChangeObserver {
+ void onPackageChanged(in PackageChangeEvent event);
+}
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 618f88c..dc8d74c 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -17,6 +17,8 @@
package android.content.pm;
+import android.content.pm.IPackageChangeObserver;
+
/**
* Parallel implementation of certain {@link PackageManager} APIs that need to
* be exposed to native code.
@@ -90,4 +92,13 @@
/* Returns the names of all packages. */
@utf8InCpp String[] getAllPackages();
+
+ /** Register an extra package change observer to receive the multi-cast. */
+ void registerPackageChangeObserver(in IPackageChangeObserver observer);
+
+ /**
+ * Unregister an existing package change observer.
+ * This does nothing if this observer was not already registered.
+ */
+ void unregisterPackageChangeObserver(in IPackageChangeObserver observer);
}
diff --git a/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
new file mode 100644
index 0000000..e30e907
--- /dev/null
+++ b/libs/binder/aidl/android/content/pm/PackageChangeEvent.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+/**
+ * This event is designed for notification to native code listener about
+ * any changes on a package including update, deletion and etc.
+ *
+ * @hide
+ */
+parcelable PackageChangeEvent {
+ @utf8InCpp String packageName;
+ long version;
+ long lastUpdateTimeMillis;
+ boolean newInstalled;
+ boolean dataRemoved;
+ boolean isDeleted;
+}
diff --git a/libs/binder/include/binder/ActivityManager.h b/libs/binder/include/binder/ActivityManager.h
index 9108e31..0bb6d28 100644
--- a/libs/binder/include/binder/ActivityManager.h
+++ b/libs/binder/include/binder/ActivityManager.h
@@ -46,25 +46,24 @@
PROCESS_STATE_PERSISTENT = 0,
PROCESS_STATE_PERSISTENT_UI = 1,
PROCESS_STATE_TOP = 2,
- PROCESS_STATE_FOREGROUND_SERVICE_LOCATION = 3,
- PROCESS_STATE_BOUND_TOP = 4,
- PROCESS_STATE_FOREGROUND_SERVICE = 5,
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 6,
- PROCESS_STATE_IMPORTANT_FOREGROUND = 7,
- PROCESS_STATE_IMPORTANT_BACKGROUND = 8,
- PROCESS_STATE_TRANSIENT_BACKGROUND = 9,
- PROCESS_STATE_BACKUP = 10,
- PROCESS_STATE_SERVICE = 11,
- PROCESS_STATE_RECEIVER = 12,
- PROCESS_STATE_TOP_SLEEPING = 13,
- PROCESS_STATE_HEAVY_WEIGHT = 14,
- PROCESS_STATE_HOME = 15,
- PROCESS_STATE_LAST_ACTIVITY = 16,
- PROCESS_STATE_CACHED_ACTIVITY = 17,
- PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 18,
- PROCESS_STATE_CACHED_RECENT = 19,
- PROCESS_STATE_CACHED_EMPTY = 20,
- PROCESS_STATE_NONEXISTENT = 21,
+ PROCESS_STATE_BOUND_TOP = 3,
+ PROCESS_STATE_FOREGROUND_SERVICE = 4,
+ PROCESS_STATE_BOUND_FOREGROUND_SERVICE = 5,
+ PROCESS_STATE_IMPORTANT_FOREGROUND = 6,
+ PROCESS_STATE_IMPORTANT_BACKGROUND = 7,
+ PROCESS_STATE_TRANSIENT_BACKGROUND = 8,
+ PROCESS_STATE_BACKUP = 9,
+ PROCESS_STATE_SERVICE = 10,
+ PROCESS_STATE_RECEIVER = 11,
+ PROCESS_STATE_TOP_SLEEPING = 12,
+ PROCESS_STATE_HEAVY_WEIGHT = 13,
+ PROCESS_STATE_HOME = 14,
+ PROCESS_STATE_LAST_ACTIVITY = 15,
+ PROCESS_STATE_CACHED_ACTIVITY = 16,
+ PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 17,
+ PROCESS_STATE_CACHED_RECENT = 18,
+ PROCESS_STATE_CACHED_EMPTY = 19,
+ PROCESS_STATE_NONEXISTENT = 20,
};
ActivityManager();
@@ -77,6 +76,7 @@
void unregisterUidObserver(const sp<IUidObserver>& observer);
bool isUidActive(const uid_t uid, const String16& callingPackage);
int getUidProcessState(const uid_t uid, const String16& callingPackage);
+ bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage);
status_t linkToDeath(const sp<IBinder::DeathRecipient>& recipient);
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index 5b6eb68..2ee5930 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -134,18 +134,18 @@
// const String16&) instead
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage);
int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId, const String16& message);
+ const std::unique_ptr<String16>& attributionTag, const String16& message);
// @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&,
// const String16&) instead
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
bool startIfModeDefault);
int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage,
- bool startIfModeDefault, const std::unique_ptr<String16>& featureId,
+ bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag,
const String16& message);
// @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead
void finishOp(int32_t op, int32_t uid, const String16& callingPackage);
void finishOp(int32_t op, int32_t uid, const String16& callingPackage,
- const std::unique_ptr<String16>& featureId);
+ const std::unique_ptr<String16>& attributionTag);
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback);
void stopWatchingMode(const sp<IAppOpsCallback>& callback);
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index 3be61f9..74e52db 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -24,6 +24,10 @@
// ---------------------------------------------------------------------------
namespace android {
+namespace internal {
+class Stability;
+}
+
class BBinder : public IBinder
{
public:
@@ -88,7 +92,12 @@
Extras* getOrCreateExtras();
std::atomic<Extras*> mExtras;
- void* mReserved0;
+
+ friend ::android::internal::Stability;
+ union {
+ int32_t mStability;
+ void* mReserved0;
+ };
};
// ---------------------------------------------------------------------------
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 7dca733..8e871b8 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -27,6 +27,10 @@
// ---------------------------------------------------------------------------
namespace android {
+namespace internal {
+class Stability;
+};
+
using binder_proxy_limit_callback = void(*)(int);
class BpBinder : public IBinder
@@ -116,6 +120,9 @@
private:
const int32_t mHandle;
+ friend ::android::internal::Stability;
+ int32_t mStability;
+
struct Obituary {
wp<DeathRecipient> recipient;
void* cookie;
diff --git a/libs/binder/include/binder/IActivityManager.h b/libs/binder/include/binder/IActivityManager.h
index e0248f6..1815ecc 100644
--- a/libs/binder/include/binder/IActivityManager.h
+++ b/libs/binder/include/binder/IActivityManager.h
@@ -39,13 +39,15 @@
virtual void unregisterUidObserver(const sp<IUidObserver>& observer) = 0;
virtual bool isUidActive(const uid_t uid, const String16& callingPackage) = 0;
virtual int32_t getUidProcessState(const uid_t uid, const String16& callingPackage) = 0;
+ virtual bool isUidActiveOrForeground(const uid_t uid, const String16& callingPackage) = 0;
enum {
OPEN_CONTENT_URI_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
REGISTER_UID_OBSERVER_TRANSACTION,
UNREGISTER_UID_OBSERVER_TRANSACTION,
IS_UID_ACTIVE_TRANSACTION,
- GET_UID_PROCESS_STATE_TRANSACTION
+ GET_UID_PROCESS_STATE_TRANSACTION,
+ IS_UID_ACTIVE_OR_FOREGROUND_TRANSACTION,
};
};
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 1b4bcce..1ffb8de 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -36,13 +36,13 @@
virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0;
virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName,
- const std::unique_ptr<String16>& featureId, bool shouldCollectAsyncNotedOp,
+ const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp,
const String16& message) = 0;
virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId,
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag,
bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0;
virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid,
- const String16& packageName, const std::unique_ptr<String16>& featureId) = 0;
+ const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0;
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
diff --git a/libs/binder/include/binder/Nullable.h b/libs/binder/include/binder/Nullable.h
new file mode 100644
index 0000000..b605bd3
--- /dev/null
+++ b/libs/binder/include/binder/Nullable.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <memory>
+#include <utility>
+
+namespace android {
+
+namespace aidl {
+
+// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr
+// usage:
+// nullable<Foo> a;
+// nullable<Foo> b = make_nullable<Foo>(...);
+// auto c = make_nullable<Foo>(...);
+// c.reset();
+// c = make_nullable<Foo>(...);
+// c = std::move(a);
+
+template <typename T>
+using nullable = std::unique_ptr<T>;
+
+template <typename T, typename... Args>
+inline nullable<T> make_nullable(Args&&... args) {
+ return std::make_unique<T>(std::forward<Args>(args)...);
+}
+
+} // namespace aidl
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 75dcdc8..649faa1 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -486,7 +486,6 @@
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
- LOG(ERROR) << __func__ << ": on null binder";
return;
}
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 1b42b69..b2b74dc 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -57,6 +57,7 @@
"android.hardware.audio@5.0::IDevicesFactory",
"android.hardware.audio@6.0::IDevicesFactory",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
+ "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
"android.hardware.drm@1.0::IDrmFactory",
@@ -74,6 +75,7 @@
"android.hardware.automotive.audiocontrol@1.0::IAudioControl",
"android.hardware.automotive.vehicle@2.0::IVehicle",
"android.hardware.automotive.evs@1.0::IEvsCamera",
+ "android.hardware.neuralnetworks@1.0::IDevice",
NULL,
};
diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp
index befabee..4809c1f 100644
--- a/libs/graphicsenv/GraphicsEnv.cpp
+++ b/libs/graphicsenv/GraphicsEnv.cpp
@@ -61,26 +61,25 @@
VNDKSP = 1,
};
-static constexpr const char* kNativeLibrariesSystemConfigPath[] = {"/etc/llndk.libraries.txt",
- "/etc/vndksp.libraries.txt"};
+static constexpr const char* kNativeLibrariesSystemConfigPath[] =
+ {"/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt",
+ "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt"};
static std::string vndkVersionStr() {
#ifdef __BIONIC__
- std::string version = android::base::GetProperty("ro.vndk.version", "");
- if (version != "" && version != "current") {
- return "." + version;
- }
+ return android::base::GetProperty("ro.vndk.version", "");
#endif
return "";
}
static void insertVndkVersionStr(std::string* fileName) {
LOG_ALWAYS_FATAL_IF(!fileName, "fileName should never be nullptr");
- size_t insertPos = fileName->find_last_of(".");
- if (insertPos == std::string::npos) {
- insertPos = fileName->length();
+ std::string version = vndkVersionStr();
+ size_t pos = fileName->find("{}");
+ while (pos != std::string::npos) {
+ fileName->replace(pos, 2, version);
+ pos = fileName->find("{}", pos + version.size());
}
- fileName->insert(insertPos, vndkVersionStr());
}
static bool readConfig(const std::string& configFile, std::vector<std::string>* soNames) {
@@ -103,11 +102,7 @@
}
static const std::string getSystemNativeLibraries(NativeLibrary type) {
- static const char* androidRootEnv = getenv("ANDROID_ROOT");
- static const std::string rootDir = androidRootEnv != nullptr ? androidRootEnv : "/system";
-
- std::string nativeLibrariesSystemConfig = rootDir + kNativeLibrariesSystemConfigPath[type];
-
+ std::string nativeLibrariesSystemConfig = kNativeLibrariesSystemConfigPath[type];
insertVndkVersionStr(&nativeLibrariesSystemConfig);
std::vector<std::string> soNames;
diff --git a/libs/gui/BufferHubConsumer.cpp b/libs/gui/BufferHubConsumer.cpp
index 0ddb87e..b5cdeb2 100644
--- a/libs/gui/BufferHubConsumer.cpp
+++ b/libs/gui/BufferHubConsumer.cpp
@@ -147,16 +147,6 @@
return INVALID_OPERATION;
}
-status_t BufferHubConsumer::setFrameRate(float /*frameRate*/) {
- ALOGE("BufferHubConsumer::setFrameRate: not implemented.");
- return INVALID_OPERATION;
-}
-
-status_t BufferHubConsumer::getFrameRate(float* /*frameRate*/) const {
- ALOGE("BufferHubConsumer::getFrameRate: not implemented.");
- return INVALID_OPERATION;
-}
-
status_t BufferHubConsumer::dumpState(const String8& /*prefix*/, String8* /*outResult*/) const {
ALOGE("BufferHubConsumer::dumpState: not implemented.");
return INVALID_OPERATION;
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 4435265..da6143c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -783,18 +783,6 @@
return NO_ERROR;
}
-status_t BufferQueueConsumer::setFrameRate(float frameRate) {
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- mCore->mFrameRate = frameRate;
- return NO_ERROR;
-}
-
-status_t BufferQueueConsumer::getFrameRate(float* frameRate) const {
- std::lock_guard<std::mutex> lock(mCore->mMutex);
- *frameRate = mCore->mFrameRate;
- return NO_ERROR;
-}
-
status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResult) const {
struct passwd* pwd = getpwnam("shell");
uid_t shellUid = pwd ? pwd->pw_uid : 0;
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index 9e5d681..a1803d8 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -130,8 +130,7 @@
mLastQueuedSlot(INVALID_BUFFER_SLOT),
mUniqueId(getUniqueId()),
mAutoPrerotation(false),
- mTransformHintInUse(0),
- mFrameRate(0) {
+ mTransformHintInUse(0) {
int numStartingBuffers = getMaxBufferCountLocked();
for (int s = 0; s < numStartingBuffers; s++) {
mFreeSlots.insert(s);
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index 9e86838..3f4c5da 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -1676,14 +1676,4 @@
return NO_ERROR;
}
-status_t BufferQueueProducer::setFrameRate(float frameRate) {
- ATRACE_CALL();
- BQ_LOGV("setFrameRate: %.0f", frameRate);
-
- std::lock_guard<std::mutex> lock(mCore->mMutex);
-
- mCore->mFrameRate = frameRate;
- return NO_ERROR;
-}
-
} // namespace android
diff --git a/libs/gui/ConsumerBase.cpp b/libs/gui/ConsumerBase.cpp
index 515f45c..9f91d9d 100644
--- a/libs/gui/ConsumerBase.cpp
+++ b/libs/gui/ConsumerBase.cpp
@@ -363,24 +363,6 @@
return OK;
}
-status_t ConsumerBase::setFrameRate(float frameRate) {
- Mutex::Autolock _l(mMutex);
- if (mAbandoned) {
- CB_LOGE("setFrameRate: ConsumerBase is abandoned!");
- return NO_INIT;
- }
- return mConsumer->setFrameRate(frameRate);
-}
-
-status_t ConsumerBase::getFrameRate(float* frameRate) {
- Mutex::Autolock _l(mMutex);
- if (mAbandoned) {
- CB_LOGE("getFrameRate: ConsumerBase is abandoned!");
- return NO_INIT;
- }
- return mConsumer->getFrameRate(frameRate);
-}
-
void ConsumerBase::dumpState(String8& result) const {
dumpState(result, "");
}
diff --git a/libs/gui/IGraphicBufferConsumer.cpp b/libs/gui/IGraphicBufferConsumer.cpp
index 2521a7c..c705d39 100644
--- a/libs/gui/IGraphicBufferConsumer.cpp
+++ b/libs/gui/IGraphicBufferConsumer.cpp
@@ -51,8 +51,6 @@
GET_SIDEBAND_STREAM,
GET_OCCUPANCY_HISTORY,
DISCARD_FREE_BUFFERS,
- SET_FRAME_RATE,
- GET_FRAME_RATE,
DUMP_STATE,
LAST = DUMP_STATE,
};
@@ -165,16 +163,6 @@
Tag::DISCARD_FREE_BUFFERS);
}
- status_t setFrameRate(float frameRate) override {
- using Signature = decltype(&IGraphicBufferConsumer::setFrameRate);
- return callRemote<Signature>(Tag::SET_FRAME_RATE, frameRate);
- }
-
- status_t getFrameRate(float* frameRate) const override {
- using Signature = decltype(&IGraphicBufferConsumer::getFrameRate);
- return callRemote<Signature>(Tag::GET_FRAME_RATE, frameRate);
- }
-
status_t dumpState(const String8& prefix, String8* outResult) const override {
using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
return callRemote<Signature>(Tag::DUMP_STATE, prefix, outResult);
@@ -232,10 +220,6 @@
return callLocal(data, reply, &IGraphicBufferConsumer::getOccupancyHistory);
case Tag::DISCARD_FREE_BUFFERS:
return callLocal(data, reply, &IGraphicBufferConsumer::discardFreeBuffers);
- case Tag::SET_FRAME_RATE:
- return callLocal(data, reply, &IGraphicBufferConsumer::setFrameRate);
- case Tag::GET_FRAME_RATE:
- return callLocal(data, reply, &IGraphicBufferConsumer::getFrameRate);
case Tag::DUMP_STATE: {
using Signature = status_t (IGraphicBufferConsumer::*)(const String8&, String8*) const;
return callLocal<Signature>(data, reply, &IGraphicBufferConsumer::dumpState);
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 7b5596e..ad00939 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -74,7 +74,6 @@
GET_CONSUMER_USAGE,
SET_LEGACY_BUFFER_DROP,
SET_AUTO_PREROTATION,
- SET_FRAME_RATE,
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -560,14 +559,6 @@
}
return result;
}
-
- virtual status_t setFrameRate(float frameRate) {
- Parcel data, reply;
- data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
- data.writeFloat(frameRate);
- status_t result = remote()->transact(SET_FRAME_RATE, data, &reply, IBinder::FLAG_ONEWAY);
- return result;
- }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -700,8 +691,6 @@
status_t setAutoPrerotation(bool autoPrerotation) override {
return mBase->setAutoPrerotation(autoPrerotation);
}
-
- status_t setFrameRate(float frameRate) override { return mBase->setFrameRate(frameRate); }
};
IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer,
@@ -721,12 +710,6 @@
return INVALID_OPERATION;
}
-status_t IGraphicBufferProducer::setFrameRate(float frameRate) {
- // No-op for IGBP other than BufferQueue.
- (void)frameRate;
- return INVALID_OPERATION;
-}
-
status_t IGraphicBufferProducer::exportToParcel(Parcel* parcel) {
status_t res = OK;
res = parcel->writeUint32(USE_BUFFER_QUEUE);
@@ -1096,13 +1079,6 @@
reply->writeInt32(result);
return NO_ERROR;
}
- case SET_FRAME_RATE: {
- CHECK_INTERFACE(IGraphicBuffer, data, reply);
- float frameRate = data.readFloat();
- status_t result = setFrameRate(frameRate);
- reply->writeInt32(result);
- return NO_ERROR;
- }
}
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index ce41eab..8d79cf8 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -1140,8 +1140,7 @@
return err;
}
- err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply,
- IBinder::FLAG_ONEWAY);
+ err = remote()->transact(BnSurfaceComposer::SET_FRAME_RATE, data, &reply);
if (err != NO_ERROR) {
ALOGE("setFrameRate: failed to transact: %s (%d)", strerror(-err), err);
return err;
@@ -1281,6 +1280,9 @@
std::unordered_set<sp<IBinder>, SpHash<IBinder>> excludeHandles;
int numExcludeHandles = data.readInt32();
+ if (numExcludeHandles >= static_cast<int>(MAX_LAYERS)) {
+ return BAD_VALUE;
+ }
excludeHandles.reserve(numExcludeHandles);
for (int i = 0; i < numExcludeHandles; i++) {
excludeHandles.emplace(data.readStrongBinder());
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index a9c9b74..f7158d0 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -443,39 +443,18 @@
// ------------------------------- InputWindowCommands ----------------------------------------
void InputWindowCommands::merge(const InputWindowCommands& other) {
- transferTouchFocusCommands
- .insert(transferTouchFocusCommands.end(),
- std::make_move_iterator(other.transferTouchFocusCommands.begin()),
- std::make_move_iterator(other.transferTouchFocusCommands.end()));
-
syncInputWindows |= other.syncInputWindows;
}
void InputWindowCommands::clear() {
- transferTouchFocusCommands.clear();
syncInputWindows = false;
}
void InputWindowCommands::write(Parcel& output) const {
- output.writeUint32(static_cast<uint32_t>(transferTouchFocusCommands.size()));
- for (const auto& transferTouchFocusCommand : transferTouchFocusCommands) {
- output.writeStrongBinder(transferTouchFocusCommand.fromToken);
- output.writeStrongBinder(transferTouchFocusCommand.toToken);
- }
-
output.writeBool(syncInputWindows);
}
void InputWindowCommands::read(const Parcel& input) {
- size_t count = input.readUint32();
- transferTouchFocusCommands.clear();
- for (size_t i = 0; i < count; i++) {
- TransferTouchFocusCommand transferTouchFocusCommand;
- transferTouchFocusCommand.fromToken = input.readStrongBinder();
- transferTouchFocusCommand.toToken = input.readStrongBinder();
- transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
- }
-
syncInputWindows = input.readBool();
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index dc4860a..2307fbf 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1290,15 +1290,6 @@
return *this;
}
-SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transferTouchFocus(
- const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
- InputWindowCommands::TransferTouchFocusCommand transferTouchFocusCommand;
- transferTouchFocusCommand.fromToken = fromToken;
- transferTouchFocusCommand.toToken = toToken;
- mInputWindowCommands.transferTouchFocusCommands.emplace_back(transferTouchFocusCommand);
- return *this;
-}
-
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() {
mInputWindowCommands.syncInputWindows = true;
return *this;
diff --git a/libs/gui/include/gui/BufferHubConsumer.h b/libs/gui/include/gui/BufferHubConsumer.h
index d756203..d380770 100644
--- a/libs/gui/include/gui/BufferHubConsumer.h
+++ b/libs/gui/include/gui/BufferHubConsumer.h
@@ -93,12 +93,6 @@
// See |IGraphicBufferConsumer::discardFreeBuffers|
status_t discardFreeBuffers() override;
- // See |IGraphicBufferConsumer::setFrameRate|
- status_t setFrameRate(float frameRate) override;
-
- // See |IGraphicBufferConsumer::getFrameRate|
- status_t getFrameRate(float* frameRate) const override;
-
// See |IGraphicBufferConsumer::dumpState|
status_t dumpState(const String8& prefix, String8* outResult) const override;
diff --git a/libs/gui/include/gui/BufferQueueConsumer.h b/libs/gui/include/gui/BufferQueueConsumer.h
index e9f0449..7db69ec 100644
--- a/libs/gui/include/gui/BufferQueueConsumer.h
+++ b/libs/gui/include/gui/BufferQueueConsumer.h
@@ -149,12 +149,6 @@
// See IGraphicBufferConsumer::discardFreeBuffers
virtual status_t discardFreeBuffers() override;
- // See IGraphicBufferConsumer::setFrameRate.
- virtual status_t setFrameRate(float frameRate) override;
-
- // See IGraphicBufferConsumer::getFrameRate.
- virtual status_t getFrameRate(float* frameRate) const override;
-
// dump our state in a String
status_t dumpState(const String8& prefix, String8* outResult) const override;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 05c2074..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -354,9 +354,6 @@
// mTransformHintInUse is to cache the mTransformHint used by the producer.
uint32_t mTransformHintInUse;
- // The frame rate the app intends to run at.
- float mFrameRate;
-
}; // class BufferQueueCore
} // namespace android
diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h
index cbace5b..a7f7d1d 100644
--- a/libs/gui/include/gui/BufferQueueProducer.h
+++ b/libs/gui/include/gui/BufferQueueProducer.h
@@ -198,9 +198,6 @@
// See IGraphicBufferProducer::setAutoPrerotation
virtual status_t setAutoPrerotation(bool autoPrerotation);
- // See IGraphicBufferProducer::setFrameRate
- virtual status_t setFrameRate(float frameRate) override;
-
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
diff --git a/libs/gui/include/gui/ConsumerBase.h b/libs/gui/include/gui/ConsumerBase.h
index cfed9aa..8ff0cd0 100644
--- a/libs/gui/include/gui/ConsumerBase.h
+++ b/libs/gui/include/gui/ConsumerBase.h
@@ -111,12 +111,6 @@
// See IGraphicBufferConsumer::discardFreeBuffers
status_t discardFreeBuffers();
- // See IGraphicBufferConsumer::setFrameRate
- status_t setFrameRate(float frameRate);
-
- // See IGraphicBufferConsumer::getFrameRate
- status_t getFrameRate(float* frameRate);
-
private:
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
diff --git a/libs/gui/include/gui/IGraphicBufferConsumer.h b/libs/gui/include/gui/IGraphicBufferConsumer.h
index 56fe949..0b92e7d 100644
--- a/libs/gui/include/gui/IGraphicBufferConsumer.h
+++ b/libs/gui/include/gui/IGraphicBufferConsumer.h
@@ -275,16 +275,6 @@
// call to free up any of its locally cached buffers.
virtual status_t discardFreeBuffers() = 0;
- // Set the frame rate the producer will run at.
- //
- // Return of a value other than NO_ERROR means an unknown error has occurred.
- virtual status_t setFrameRate(float frameRate) = 0;
-
- // Get the frame rate the producer will run at.
- //
- // Return of a value other than NO_ERROR means an unknown error has occurred.
- virtual status_t getFrameRate(float* frameRate) const = 0;
-
// dump state into a string
virtual status_t dumpState(const String8& prefix, String8* outResult) const = 0;
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index 87989da..d7f3492 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -639,9 +639,6 @@
// the width and height used for dequeueBuffer will be additionally swapped.
virtual status_t setAutoPrerotation(bool autoPrerotation);
- // Sets the apps intended frame rate.
- virtual status_t setFrameRate(float frameRate);
-
#ifndef NO_BINDER
// Static method exports any IGraphicBufferProducer object to a parcel. It
// handles null producer as well.
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 0659f0d..09487ea 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -76,6 +76,8 @@
public:
DECLARE_META_INTERFACE(SurfaceComposer)
+ static constexpr size_t MAX_LAYERS = 4096;
+
// flags for setTransactionState()
enum {
eSynchronous = 0x01,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 7e3d5d5..2b2f773 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -271,12 +271,6 @@
};
struct InputWindowCommands {
- struct TransferTouchFocusCommand {
- sp<IBinder> fromToken;
- sp<IBinder> toToken;
- };
-
- std::vector<TransferTouchFocusCommand> transferTouchFocusCommands;
bool syncInputWindows{false};
void merge(const InputWindowCommands& other);
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index ad7cbfe..917c0d4 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -178,7 +178,6 @@
status_t getUniqueId(uint64_t* outId) const;
status_t getConsumerUsage(uint64_t* outUsage) const;
- // See IGraphicBufferProducer::setFrameRate
status_t setFrameRate(float frameRate, int8_t compatibility);
protected:
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0cf141d..2fb9538 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -507,7 +507,6 @@
#ifndef NO_INPUT
Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info);
- Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken);
Transaction& syncInputWindows();
#endif
diff --git a/libs/gui/include/gui/mock/GraphicBufferConsumer.h b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
index e940cf3..98f24c2 100644
--- a/libs/gui/include/gui/mock/GraphicBufferConsumer.h
+++ b/libs/gui/include/gui/mock/GraphicBufferConsumer.h
@@ -49,8 +49,6 @@
MOCK_CONST_METHOD1(getSidebandStream, status_t(sp<NativeHandle>*));
MOCK_METHOD2(getOccupancyHistory, status_t(bool, std::vector<OccupancyTracker::Segment>*));
MOCK_METHOD0(discardFreeBuffers, status_t());
- MOCK_METHOD1(setFrameRate, status_t(float));
- MOCK_CONST_METHOD1(getFrameRate, status_t(float*));
MOCK_CONST_METHOD2(dumpState, status_t(const String8&, String8*));
};
diff --git a/libs/gui/tests/AndroidTest.xml b/libs/gui/tests/AndroidTest.xml
index c02e020..5e09fff 100644
--- a/libs/gui/tests/AndroidTest.xml
+++ b/libs/gui/tests/AndroidTest.xml
@@ -18,6 +18,10 @@
<option name="cleanup" value="true" />
<option name="push" value="libgui_test->/data/local/tmp/libgui_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+ <option name="force-skip-system-props" value="true" /> <!-- avoid restarting device -->
+ <option name="screen-always-on" value="on" />
+ </target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 7335b30..ef7cc7d 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -1130,6 +1130,16 @@
return !mBatches.isEmpty();
}
+int32_t InputConsumer::getPendingBatchSource() const {
+ if (mBatches.isEmpty()) {
+ return AINPUT_SOURCE_CLASS_NONE;
+ }
+
+ const Batch& batch = mBatches.itemAt(0);
+ const InputMessage& head = batch.samples.itemAt(0);
+ return head.body.motion.source;
+}
+
ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const {
for (size_t i = 0; i < mBatches.size(); i++) {
const Batch& batch = mBatches.itemAt(i);
diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp
index 145b4ae..c62e098 100644
--- a/libs/input/TouchVideoFrame.cpp
+++ b/libs/input/TouchVideoFrame.cpp
@@ -43,13 +43,13 @@
void TouchVideoFrame::rotate(int32_t orientation) {
switch (orientation) {
case DISPLAY_ORIENTATION_90:
- rotateQuarterTurn(true /*clockwise*/);
+ rotateQuarterTurn(false /*clockwise*/);
break;
case DISPLAY_ORIENTATION_180:
rotate180();
break;
case DISPLAY_ORIENTATION_270:
- rotateQuarterTurn(false /*clockwise*/);
+ rotateQuarterTurn(true /*clockwise*/);
break;
}
}
diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp
index 1ec9358..654b236 100644
--- a/libs/input/tests/TouchVideoFrame_test.cpp
+++ b/libs/input/tests/TouchVideoFrame_test.cpp
@@ -86,14 +86,14 @@
TEST(TouchVideoFrame, Rotate90_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate90_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_90);
ASSERT_EQ(frame, frameRotated);
}
@@ -171,14 +171,14 @@
TEST(TouchVideoFrame, Rotate270_2x2) {
TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
TEST(TouchVideoFrame, Rotate270_3x2) {
TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP);
- TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP);
+ TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP);
frame.rotate(DISPLAY_ORIENTATION_270);
ASSERT_EQ(frame, frameRotated);
}
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 0f7e2fb..0ff33ac 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -185,9 +185,18 @@
}
void Choreographer::scheduleCallbacks() {
- AutoMutex _{mLock};
- nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
- if (mFrameCallbacks.top().dueTime <= now) {
+ const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ nsecs_t dueTime;
+ {
+ AutoMutex _{mLock};
+ // If there are no pending callbacks then don't schedule a vsync
+ if (mFrameCallbacks.empty()) {
+ return;
+ }
+ dueTime = mFrameCallbacks.top().dueTime;
+ }
+
+ if (dueTime <= now) {
ALOGV("choreographer %p ~ scheduling vsync", this);
scheduleVsync();
return;
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 3d77059..eb6080f 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -55,12 +55,11 @@
"gl/GLShadowTexture.cpp",
"gl/GLShadowVertexGenerator.cpp",
"gl/GLSkiaShadowPort.cpp",
+ "gl/GLVertexBuffer.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
"gl/filters/BlurFilter.cpp",
- "gl/filters/KawaseBlurFilter.cpp",
- "gl/filters/GaussianBlurFilter.cpp",
"gl/filters/GenericProgram.cpp",
],
}
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index e11b59f..e73f245 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -50,8 +50,6 @@
#include "Program.h"
#include "ProgramCache.h"
#include "filters/BlurFilter.h"
-#include "filters/GaussianBlurFilter.h"
-#include "filters/KawaseBlurFilter.h"
extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
@@ -383,15 +381,6 @@
LOG_ALWAYS_FATAL_IF(!success, "can't make default context current");
}
- const uint16_t protTexData[] = {0};
- glGenTextures(1, &mProtectedTexName);
- glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
-
// mColorBlindnessCorrection = M;
if (mUseColorManagement) {
@@ -430,17 +419,12 @@
}
if (args.supportsBackgroundBlur) {
- char isGaussian[PROPERTY_VALUE_MAX];
- property_get("debug.sf.gaussianBlur", isGaussian, "0");
- if (atoi(isGaussian)) {
- mBlurFilter = new GaussianBlurFilter(*this);
- } else {
- mBlurFilter = new KawaseBlurFilter(*this);
- }
+ mBlurFilter = new BlurFilter(*this);
checkErrors("BlurFilter creation");
}
mImageManager = std::make_unique<ImageManager>(this);
+ mImageManager->initThread();
mDrawingBuffer = createFramebuffer();
}
@@ -973,9 +957,13 @@
return NO_ERROR;
}
- if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) {
- ATRACE_NAME("Waiting before draw");
- sync_wait(bufferFence.get(), -1);
+ if (bufferFence.get() >= 0) {
+ // Duplicate the fence for passing to waitFence.
+ base::unique_fd bufferFenceDup(dup(bufferFence.get()));
+ if (bufferFenceDup < 0 || !waitFence(std::move(bufferFenceDup))) {
+ ATRACE_NAME("Waiting before draw");
+ sync_wait(bufferFence.get(), -1);
+ }
}
if (buffer == nullptr) {
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index ebf78fe..32dbad1 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -178,7 +178,6 @@
EGLSurface mDummySurface;
EGLContext mProtectedEGLContext;
EGLSurface mProtectedDummySurface;
- GLuint mProtectedTexName;
GLint mMaxViewportDims[2];
GLint mMaxTextureSize;
GLuint mVpWidth;
@@ -261,8 +260,6 @@
friend class ImageManager;
friend class GLFramebuffer;
friend class BlurFilter;
- friend class GaussianBlurFilter;
- friend class KawaseBlurFilter;
friend class GenericProgram;
std::unique_ptr<FlushTracer> mFlushTracer;
std::unique_ptr<ImageManager> mImageManager = std::make_unique<ImageManager>(this);
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
index 153935b..cb0d5cf 100644
--- a/libs/renderengine/gl/GLFramebuffer.cpp
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -32,23 +32,14 @@
namespace gl {
GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine)
- : GLFramebuffer(engine, false /* multiTarget */) {}
-
-GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine, bool multiTarget)
: mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
glGenTextures(1, &mTextureName);
- if (multiTarget) {
- glGenTextures(1, &mSecondaryTextureName);
- }
glGenFramebuffers(1, &mFramebufferName);
}
GLFramebuffer::~GLFramebuffer() {
glDeleteFramebuffers(1, &mFramebufferName);
glDeleteTextures(1, &mTextureName);
- if (mSecondaryTextureName != -1) {
- glDeleteTextures(1, &mSecondaryTextureName);
- }
}
bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected,
@@ -87,28 +78,12 @@
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- const bool multiTarget = mSecondaryTextureName != -1;
- if (multiTarget) {
- glBindTexture(GL_TEXTURE_2D, mSecondaryTextureName);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
- }
-
mBufferHeight = height;
mBufferWidth = width;
mEngine.checkErrors("Allocating Fbo texture");
bind();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureName, 0);
- if (multiTarget) {
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1_EXT, GL_TEXTURE_2D,
- mSecondaryTextureName, 0);
- GLenum buffers[] = {GL_COLOR_ATTACHMENT0_EXT, GL_COLOR_ATTACHMENT1_EXT};
- glDrawBuffers(2, buffers);
- }
mStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
unbind();
glBindTexture(GL_TEXTURE_2D, 0);
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
index 69102d6..b88da3b 100644
--- a/libs/renderengine/gl/GLFramebuffer.h
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -42,7 +42,6 @@
void allocateBuffers(uint32_t width, uint32_t height);
EGLImageKHR getEGLImage() const { return mEGLImage; }
uint32_t getTextureName() const { return mTextureName; }
- uint32_t getSecondaryTextureName() const { return mSecondaryTextureName; }
uint32_t getFramebufferName() const { return mFramebufferName; }
int32_t getBufferHeight() const { return mBufferHeight; }
int32_t getBufferWidth() const { return mBufferWidth; }
@@ -59,7 +58,6 @@
bool usingFramebufferCache = false;
GLenum mStatus = GL_FRAMEBUFFER_UNSUPPORTED;
uint32_t mTextureName, mFramebufferName;
- uint32_t mSecondaryTextureName = -1;
int32_t mBufferHeight = 0;
int32_t mBufferWidth = 0;
diff --git a/libs/renderengine/gl/GLVertexBuffer.cpp b/libs/renderengine/gl/GLVertexBuffer.cpp
new file mode 100644
index 0000000..e50c471
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLVertexBuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES2/gl2.h>
+#include <nativebase/nativebase.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLVertexBuffer::GLVertexBuffer() {
+ glGenBuffers(1, &mBufferName);
+}
+
+GLVertexBuffer::~GLVertexBuffer() {
+ glDeleteBuffers(1, &mBufferName);
+}
+
+void GLVertexBuffer::allocateBuffers(const GLfloat data[], const GLuint size) {
+ ATRACE_CALL();
+ bind();
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(GLfloat), data, GL_STATIC_DRAW);
+ unbind();
+}
+
+void GLVertexBuffer::bind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, mBufferName);
+}
+
+void GLVertexBuffer::unbind() const {
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLVertexBuffer.h b/libs/renderengine/gl/GLVertexBuffer.h
new file mode 100644
index 0000000..c0fd0c1
--- /dev/null
+++ b/libs/renderengine/gl/GLVertexBuffer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLESRenderEngine;
+
+class GLVertexBuffer {
+public:
+ explicit GLVertexBuffer();
+ ~GLVertexBuffer();
+
+ void allocateBuffers(const GLfloat data[], const GLuint size);
+ uint32_t getBufferName() const { return mBufferName; }
+ void bind() const;
+ void unbind() const;
+
+private:
+ uint32_t mBufferName;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/ImageManager.cpp b/libs/renderengine/gl/ImageManager.cpp
index 5af0e4f..6256649 100644
--- a/libs/renderengine/gl/ImageManager.cpp
+++ b/libs/renderengine/gl/ImageManager.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <pthread.h>
@@ -27,7 +30,10 @@
namespace renderengine {
namespace gl {
-ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {
+ImageManager::ImageManager(GLESRenderEngine* engine) : mEngine(engine) {}
+
+void ImageManager::initThread() {
+ mThread = std::thread([this]() { threadMain(); });
pthread_setname_np(mThread.native_handle(), "ImageManager");
// Use SCHED_FIFO to minimize jitter
struct sched_param param = {0};
@@ -133,6 +139,8 @@
entry.barrier->condition.notify_one();
}
}
+
+ ALOGD("Reached end of threadMain, terminating ImageManager thread!");
}
} // namespace gl
diff --git a/libs/renderengine/gl/ImageManager.h b/libs/renderengine/gl/ImageManager.h
index b5ba554..be67de8 100644
--- a/libs/renderengine/gl/ImageManager.h
+++ b/libs/renderengine/gl/ImageManager.h
@@ -39,6 +39,10 @@
};
ImageManager(GLESRenderEngine* engine);
~ImageManager();
+ // Starts the background thread for the ImageManager
+ // We need this to guarantee that the class is fully-constructed before the
+ // thread begins running.
+ void initThread();
void cacheAsync(const sp<GraphicBuffer>& buffer, const std::shared_ptr<Barrier>& barrier)
EXCLUDES(mMutex);
status_t cache(const sp<GraphicBuffer>& buffer);
@@ -57,7 +61,7 @@
void queueOperation(const QueueEntry&& entry);
void threadMain();
GLESRenderEngine* const mEngine;
- std::thread mThread = std::thread([this]() { threadMain(); });
+ std::thread mThread;
std::condition_variable_any mCondition;
std::mutex mMutex;
std::queue<QueueEntry> mQueue GUARDED_BY(mMutex);
diff --git a/libs/renderengine/gl/filters/BlurFilter.cpp b/libs/renderengine/gl/filters/BlurFilter.cpp
index eb66c8f..6ba78dc 100644
--- a/libs/renderengine/gl/filters/BlurFilter.cpp
+++ b/libs/renderengine/gl/filters/BlurFilter.cpp
@@ -31,13 +31,38 @@
namespace gl {
BlurFilter::BlurFilter(GLESRenderEngine& engine)
- : mEngine(engine), mCompositionFbo(engine), mBlurredFbo(engine), mMixProgram(engine) {
+ : mEngine(engine),
+ mCompositionFbo(engine),
+ mPingFbo(engine),
+ mPongFbo(engine),
+ mMixProgram(engine),
+ mBlurProgram(engine) {
mMixProgram.compile(getVertexShader(), getMixFragShader());
mMPosLoc = mMixProgram.getAttributeLocation("aPosition");
mMUvLoc = mMixProgram.getAttributeLocation("aUV");
mMTextureLoc = mMixProgram.getUniformLocation("uTexture");
mMCompositionTextureLoc = mMixProgram.getUniformLocation("uCompositionTexture");
mMMixLoc = mMixProgram.getUniformLocation("uMix");
+
+ mBlurProgram.compile(getVertexShader(), getFragmentShader());
+ mBPosLoc = mBlurProgram.getAttributeLocation("aPosition");
+ mBUvLoc = mBlurProgram.getAttributeLocation("aUV");
+ mBTextureLoc = mBlurProgram.getUniformLocation("uTexture");
+ mBOffsetLoc = mBlurProgram.getUniformLocation("uOffset");
+
+ static constexpr auto size = 2.0f;
+ static constexpr auto translation = 1.0f;
+ const GLfloat vboData[] = {
+ // Vertex data
+ translation-size, -translation-size,
+ translation-size, -translation+size,
+ translation+size, -translation+size,
+ // UV data
+ 0.0f, 0.0f-translation,
+ 0.0f, size-translation,
+ size, size-translation
+ };
+ mMeshBuffer.allocateBuffers(vboData, 12 /* size */);
}
status_t BlurFilter::setAsDrawTarget(const DisplaySettings& display, uint32_t radius) {
@@ -51,18 +76,26 @@
const uint32_t fboWidth = floorf(mDisplayWidth * kFboScale);
const uint32_t fboHeight = floorf(mDisplayHeight * kFboScale);
- mBlurredFbo.allocateBuffers(fboWidth, fboHeight);
- allocateTextures();
+ mPingFbo.allocateBuffers(fboWidth, fboHeight);
+ mPongFbo.allocateBuffers(fboWidth, fboHeight);
mTexturesAllocated = true;
- }
- if (mBlurredFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid blur buffer");
- return mBlurredFbo.getStatus();
- }
- if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid composition buffer");
- return mCompositionFbo.getStatus();
+ if (mPingFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid ping buffer");
+ return mPingFbo.getStatus();
+ }
+ if (mPongFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid pong buffer");
+ return mPongFbo.getStatus();
+ }
+ if (mCompositionFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
+ ALOGE("Invalid composition buffer");
+ return mCompositionFbo.getStatus();
+ }
+ if (!mBlurProgram.isValid()) {
+ ALOGE("Invalid shader");
+ return GL_INVALID_OPERATION;
+ }
}
mCompositionFbo.bind();
@@ -71,29 +104,62 @@
}
void BlurFilter::drawMesh(GLuint uv, GLuint position) {
- static constexpr auto size = 2.0f;
- static constexpr auto translation = 1.0f;
- GLfloat positions[] = {
- translation-size, -translation-size,
- translation-size, -translation+size,
- translation+size, -translation+size
- };
- GLfloat texCoords[] = {
- 0.0f, 0.0f-translation,
- 0.0f, size-translation,
- size, size-translation
- };
- // set attributes
glEnableVertexAttribArray(uv);
- glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray(position);
- glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
- positions);
+ mMeshBuffer.bind();
+ glVertexAttribPointer(position, 2 /* size */, GL_FLOAT, GL_FALSE,
+ 2 * sizeof(GLfloat) /* stride */, 0 /* offset */);
+ glVertexAttribPointer(uv, 2 /* size */, GL_FLOAT, GL_FALSE, 0 /* stride */,
+ (GLvoid*)(6 * sizeof(GLfloat)) /* offset */);
+ mMeshBuffer.unbind();
// draw mesh
glDrawArrays(GL_TRIANGLES, 0 /* first */, 3 /* count */);
- mEngine.checkErrors("Drawing blur mesh");
+}
+
+status_t BlurFilter::prepare() {
+ ATRACE_NAME("BlurFilter::prepare");
+ blit(mCompositionFbo, mPingFbo);
+
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ auto radius = mRadius / 6.0f;
+
+ // Calculate how many passes we'll do, based on the radius.
+ // Too many passes will make the operation expensive.
+ auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
+
+ // We'll ping pong between our textures, to accumulate the result of various offsets.
+ mBlurProgram.useProgram();
+ GLFramebuffer* read = &mPingFbo;
+ GLFramebuffer* draw = &mPongFbo;
+ float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
+ float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
+ glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(mBTextureLoc, 0);
+ for (auto i = 0; i < passes; i++) {
+ ATRACE_NAME("BlurFilter::renderPass");
+ draw->bind();
+
+ glBindTexture(GL_TEXTURE_2D, read->getTextureName());
+ glUniform2f(mBOffsetLoc, stepX * i, stepY * i);
+
+ drawMesh(mBUvLoc, mBPosLoc);
+
+ // Swap buffers for next iteration
+ auto tmp = draw;
+ draw = read;
+ read = tmp;
+ }
+ mLastDrawTarget = read;
+
+ // Cleanup
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ return NO_ERROR;
}
status_t BlurFilter::render(bool multiPass) {
@@ -107,9 +173,10 @@
// be writing onto it. Let's disable the crossfade, otherwise we'd need 1 extra frame buffer,
// as large as the screen size.
if (mix >= 1 || multiPass) {
- mBlurredFbo.bindAsReadBuffer();
- glBlitFramebuffer(0, 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(), 0, 0,
- mDisplayWidth, mDisplayHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
+ mLastDrawTarget->bindAsReadBuffer();
+ glBlitFramebuffer(0, 0, mLastDrawTarget->getBufferWidth(),
+ mLastDrawTarget->getBufferHeight(), 0, 0, mDisplayWidth, mDisplayHeight,
+ GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return NO_ERROR;
}
@@ -117,22 +184,23 @@
mMixProgram.useProgram();
glUniform1f(mMMixLoc, mix);
glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
+ glBindTexture(GL_TEXTURE_2D, mLastDrawTarget->getTextureName());
glUniform1i(mMTextureLoc, 0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, mCompositionFbo.getTextureName());
glUniform1i(mMCompositionTextureLoc, 1);
- mEngine.checkErrors("Setting final pass uniforms");
drawMesh(mMUvLoc, mMPosLoc);
glUseProgram(0);
glActiveTexture(GL_TEXTURE0);
+ mEngine.checkErrors("Drawing blur mesh");
return NO_ERROR;
}
string BlurFilter::getVertexShader() const {
return R"SHADER(#version 310 es
+ precision mediump float;
in vec2 aPosition;
in highp vec2 aUV;
@@ -145,6 +213,28 @@
)SHADER";
}
+string BlurFilter::getFragmentShader() const {
+ return R"SHADER(#version 310 es
+ precision mediump float;
+
+ uniform sampler2D uTexture;
+ uniform vec2 uOffset;
+
+ in highp vec2 vUV;
+ out vec4 fragColor;
+
+ void main() {
+ fragColor = texture(uTexture, vUV, 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
+ fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
+
+ fragColor = vec4(fragColor.rgb * 0.2, 1.0);
+ }
+ )SHADER";
+}
+
string BlurFilter::getMixFragShader() const {
string shader = R"SHADER(#version 310 es
precision mediump float;
@@ -165,6 +255,15 @@
return shader;
}
+void BlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
+ read.bindAsReadBuffer();
+ draw.bindAsDrawBuffer();
+ glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
+ draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
+ GL_LINEAR);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/filters/BlurFilter.h b/libs/renderengine/gl/filters/BlurFilter.h
index 52dc8aa..3eb5c96 100644
--- a/libs/renderengine/gl/filters/BlurFilter.h
+++ b/libs/renderengine/gl/filters/BlurFilter.h
@@ -19,6 +19,7 @@
#include <ui/GraphicTypes.h>
#include "../GLESRenderEngine.h"
#include "../GLFramebuffer.h"
+#include "../GLVertexBuffer.h"
#include "GenericProgram.h"
using namespace std;
@@ -27,10 +28,17 @@
namespace renderengine {
namespace gl {
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
class BlurFilter {
public:
// Downsample FBO to improve performance
static constexpr float kFboScale = 0.25f;
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 6;
// To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
// image, up to this radius.
static constexpr float kMaxCrossFadeRadius = 30.0f;
@@ -40,36 +48,46 @@
// Set up render targets, redirecting output to offscreen texture.
status_t setAsDrawTarget(const DisplaySettings&, uint32_t radius);
- // Allocate any textures needed for the filter.
- virtual void allocateTextures() = 0;
// Execute blur passes, rendering to offscreen texture.
- virtual status_t prepare() = 0;
+ status_t prepare();
// Render blur to the bound framebuffer (screen).
status_t render(bool multiPass);
-protected:
+private:
uint32_t mRadius;
void drawMesh(GLuint uv, GLuint position);
+ void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
string getVertexShader() const;
+ string getFragmentShader() const;
+ string getMixFragShader() const;
GLESRenderEngine& mEngine;
// Frame buffer holding the composited background.
GLFramebuffer mCompositionFbo;
- // Frame buffer holding the blur result.
- GLFramebuffer mBlurredFbo;
+ // Frame buffers holding the blur passes.
+ GLFramebuffer mPingFbo;
+ GLFramebuffer mPongFbo;
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
-
-private:
- string getMixFragShader() const;
+ // Buffer holding the final blur pass.
+ GLFramebuffer* mLastDrawTarget;
bool mTexturesAllocated = false;
+ // VBO containing vertex and uv data of a fullscreen triangle.
+ GLVertexBuffer mMeshBuffer;
+
GenericProgram mMixProgram;
GLuint mMPosLoc;
GLuint mMUvLoc;
GLuint mMMixLoc;
GLuint mMTextureLoc;
GLuint mMCompositionTextureLoc;
+
+ GenericProgram mBlurProgram;
+ GLuint mBPosLoc;
+ GLuint mBUvLoc;
+ GLuint mBTextureLoc;
+ GLuint mBOffsetLoc;
};
} // namespace gl
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp b/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
deleted file mode 100644
index a0d7af8..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "GaussianBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-#include <cstdint>
-
-#include <utils/Trace.h>
-
-#define PI 3.14159265359
-#define THETA 0.352
-#define K 1.0 / (2.0 * THETA * THETA)
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-GaussianBlurFilter::GaussianBlurFilter(GLESRenderEngine& engine)
- : BlurFilter(engine),
- mVerticalPassFbo(engine),
- mVerticalProgram(engine),
- mHorizontalProgram(engine) {
- mVerticalProgram.compile(getVertexShader(), getFragmentShader(false));
- mVPosLoc = mVerticalProgram.getAttributeLocation("aPosition");
- mVUvLoc = mVerticalProgram.getAttributeLocation("aUV");
- mVTextureLoc = mVerticalProgram.getUniformLocation("uTexture");
- mVGaussianOffsetLoc = mVerticalProgram.getUniformLocation("uGaussianOffsets");
- mVNumSamplesLoc = mVerticalProgram.getUniformLocation("uSamples");
- mVGaussianWeightLoc = mVerticalProgram.getUniformLocation("uGaussianWeights");
-
- mHorizontalProgram.compile(getVertexShader(), getFragmentShader(true));
- mHPosLoc = mHorizontalProgram.getAttributeLocation("aPosition");
- mHUvLoc = mHorizontalProgram.getAttributeLocation("aUV");
- mHTextureLoc = mHorizontalProgram.getUniformLocation("uTexture");
- mHGaussianOffsetLoc = mHorizontalProgram.getUniformLocation("uGaussianOffsets");
- mHNumSamplesLoc = mHorizontalProgram.getUniformLocation("uSamples");
- mHGaussianWeightLoc = mHorizontalProgram.getUniformLocation("uGaussianWeights");
-}
-
-void GaussianBlurFilter::allocateTextures() {
- mVerticalPassFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-static void calculateLinearGaussian(uint32_t samples, double dimension,
- GLfloat* gaussianLinearOffsets, GLfloat* gaussianWeights,
- GLfloat* gaussianLinearWeights) {
- // The central point in the symmetric bell curve is not offset.
- // This decision allows one less sampling in the GPU.
- gaussianLinearWeights[0] = gaussianWeights[0];
- gaussianLinearOffsets[0] = 0.0;
-
- // Calculate the linear weights.
- // This is a vector reduction where an element of the packed reduced array
- // contains the sum of two adjacent members of the original packed array.
- // We start preserving the element 1 of the array and then perform sum for
- // every other (i+=2) element of the gaussianWeights array.
- gaussianLinearWeights[1] = gaussianWeights[1];
- const auto start = 1 + ((samples - 1) & 0x1);
- for (size_t i = start; i < samples; i += 2) {
- gaussianLinearWeights[start + i / 2] = gaussianWeights[i] + gaussianWeights[i + 1];
- }
-
- // Calculate the texture coordinates offsets as an average of the initial offsets,
- // weighted by the Gaussian weights as described in the original article.
- gaussianLinearOffsets[1] = 1.0 / dimension;
- for (size_t i = start; i < samples; i += 2) {
- GLfloat offset_1 = float(i) / dimension;
- GLfloat offset_2 = float(i + 1) / dimension;
- gaussianLinearOffsets[start + i / 2] =
- (offset_1 * gaussianWeights[i] + offset_2 * gaussianWeights[i + 1]) /
- gaussianLinearWeights[start + i / 2];
- }
-}
-status_t GaussianBlurFilter::prepare() {
- ATRACE_NAME("GaussianBlurFilter::prepare");
-
- if (mVerticalPassFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid vertical FBO");
- return mVerticalPassFbo.getStatus();
- }
- if (!mVerticalProgram.isValid()) {
- ALOGE("Invalid vertical shader");
- return GL_INVALID_OPERATION;
- }
- if (!mHorizontalProgram.isValid()) {
- ALOGE("Invalid horizontal shader");
- return GL_INVALID_OPERATION;
- }
-
- mCompositionFbo.bindAsReadBuffer();
- mBlurredFbo.bindAsDrawBuffer();
- glBlitFramebuffer(0, 0, mCompositionFbo.getBufferWidth(), mCompositionFbo.getBufferHeight(), 0,
- 0, mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight(),
- GL_COLOR_BUFFER_BIT, GL_LINEAR);
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
-
- // First, we'll apply the vertical pass, that receives the flattened background layers.
- mVerticalPassFbo.bind();
- mVerticalProgram.useProgram();
-
- // Precompute gaussian bell curve, and send it to the shader to avoid unnecessary computations.
- double radiusD = fmax(1.0, mRadius * kFboScale);
- auto samples = int(fmin(radiusD, kNumSamples));
- GLfloat gaussianWeights[kNumSamples] = {};
-
- gaussianWeights[0] = 1.0f;
- auto totalWeight = gaussianWeights[0];
-
- // Gaussian weights calculation.
- for (size_t i = 1; i < samples; i++) {
- const double normalized = i / radiusD;
- gaussianWeights[i] = (float)exp(-K * normalized * normalized);
- totalWeight += 2.0 * gaussianWeights[i];
- }
-
- // Gaussian weights normalization to avoid work in the GPU.
- for (size_t i = 0; i < samples; i++) {
- gaussianWeights[i] /= totalWeight;
- }
-
- auto width = mVerticalPassFbo.getBufferWidth();
- auto height = mVerticalPassFbo.getBufferHeight();
- glViewport(0, 0, width, height);
-
- // Allocate space for the corrected Gaussian weights and offsets.
- // We could use less space, but let's keep the code simple.
- GLfloat gaussianLinearWeights[kNumSamples] = {};
- GLfloat gaussianLinearOffsets[kNumSamples] = {};
-
- // Calculate the weights and offsets for the vertical pass.
- // This only need to be called every time mRadius or height changes, so it could be optimized.
- calculateLinearGaussian(samples, double(height), gaussianLinearOffsets, gaussianWeights,
- gaussianLinearWeights);
- // set uniforms
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mBlurredFbo.getTextureName());
- glUniform1i(mVTextureLoc, 0);
- glUniform1i(mVNumSamplesLoc, 1 + (samples + 1) / 2);
- glUniform1fv(mVGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
- glUniform1fv(mVGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
- mEngine.checkErrors("Setting vertical pass uniforms");
-
- drawMesh(mVUvLoc, mVPosLoc);
-
- // Blur vertically on a secondary pass
- mBlurredFbo.bind();
- mHorizontalProgram.useProgram();
-
- // Calculate the weights and offsets for the horizontal pass.
- // This only needs to be called every time mRadius or width change, so it could be optimized.
- calculateLinearGaussian(samples, double(width), gaussianLinearOffsets, gaussianWeights,
- gaussianLinearWeights);
- // set uniforms
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mVerticalPassFbo.getTextureName());
- glUniform1i(mHTextureLoc, 0);
- glUniform1i(mHNumSamplesLoc, 1 + (samples + 1) / 2);
- glUniform1fv(mHGaussianWeightLoc, kNumSamples, gaussianLinearWeights);
- glUniform1fv(mHGaussianOffsetLoc, kNumSamples, gaussianLinearOffsets);
- mEngine.checkErrors("Setting horizontal pass uniforms");
-
- drawMesh(mHUvLoc, mHPosLoc);
-
- // reset active texture
- mBlurredFbo.unbind();
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- // unbind program
- glUseProgram(0);
-
- return NO_ERROR;
-}
-
-string GaussianBlurFilter::getFragmentShader(bool horizontal) const {
- stringstream shader;
- shader << "#version 310 es\n"
- << "#define DIRECTION " << (horizontal ? "1" : "0") << "\n"
- << "#define NUM_SAMPLES " << 1 + (kNumSamples + 1) / 2 <<
- R"SHADER(
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform float[NUM_SAMPLES] uGaussianWeights;
- uniform float[NUM_SAMPLES] uGaussianOffsets;
- uniform int uSamples;
-
- highp in vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- #if DIRECTION == 1
- const vec2 direction = vec2(1.0, 0.0);
- #else
- const vec2 direction = vec2(0.0, 1.0);
- #endif
-
- // Iteration zero outside loop to avoid sampling the central point twice.
- vec4 blurred = uGaussianWeights[0] * (texture(uTexture, vUV, 0.0));
-
- // Iterate one side of the bell to halve the loop iterations.
- for (int i = 1; i <= uSamples; i++) {
- vec2 offset = uGaussianOffsets[i] * direction;
- blurred += uGaussianWeights[i] * (texture(uTexture, vUV + offset, 0.0));
- blurred += uGaussianWeights[i] * (texture(uTexture, vUV - offset, 0.0));
- }
-
- fragColor = vec4(blurred.rgb, 1.0);
- }
- )SHADER";
- return shader.str();
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/GaussianBlurFilter.h b/libs/renderengine/gl/filters/GaussianBlurFilter.h
deleted file mode 100644
index 44f5fde..0000000
--- a/libs/renderengine/gl/filters/GaussianBlurFilter.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "BlurFilter.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-// Class that implements a Gaussian Filter that uses Linear Sampling
-// to halve the number of samples and reduce runtime by 40% as described in:
-// http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling
-class GaussianBlurFilter : public BlurFilter {
-public:
- static constexpr uint32_t kNumSamples = 22;
-
- explicit GaussianBlurFilter(GLESRenderEngine& engine);
- status_t prepare() override;
- void allocateTextures() override;
-
-private:
- string getFragmentShader(bool horizontal) const;
-
- // Initial, vertical render pass
- GLFramebuffer mVerticalPassFbo;
-
- // Vertical pass and its uniforms
- GenericProgram mVerticalProgram;
- GLuint mVPosLoc;
- GLuint mVUvLoc;
- GLuint mVTextureLoc;
- GLuint mVGaussianOffsetLoc;
- GLuint mVNumSamplesLoc;
- GLuint mVGaussianWeightLoc;
-
- // Horizontal pass and its uniforms
- GenericProgram mHorizontalProgram;
- GLuint mHPosLoc;
- GLuint mHUvLoc;
- GLuint mHTextureLoc;
- GLuint mHGaussianOffsetLoc;
- GLuint mHNumSamplesLoc;
- GLuint mHGaussianWeightLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
\ No newline at end of file
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp b/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
deleted file mode 100644
index 7524c6d..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "KawaseBlurFilter.h"
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES3/gl3.h>
-#include <GLES3/gl3ext.h>
-#include <ui/GraphicTypes.h>
-
-#include <utils/Trace.h>
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-KawaseBlurFilter::KawaseBlurFilter(GLESRenderEngine& engine)
- : BlurFilter(engine), mFbo(engine), mProgram(engine) {
- mProgram.compile(getVertexShader(), getFragmentShader());
- mPosLoc = mProgram.getAttributeLocation("aPosition");
- mUvLoc = mProgram.getAttributeLocation("aUV");
- mTextureLoc = mProgram.getUniformLocation("uTexture");
- mOffsetLoc = mProgram.getUniformLocation("uOffset");
-}
-
-void KawaseBlurFilter::allocateTextures() {
- mFbo.allocateBuffers(mBlurredFbo.getBufferWidth(), mBlurredFbo.getBufferHeight());
-}
-
-status_t KawaseBlurFilter::prepare() {
- ATRACE_NAME("KawaseBlurFilter::prepare");
-
- if (mFbo.getStatus() != GL_FRAMEBUFFER_COMPLETE) {
- ALOGE("Invalid FBO");
- return mFbo.getStatus();
- }
- if (!mProgram.isValid()) {
- ALOGE("Invalid shader");
- return GL_INVALID_OPERATION;
- }
-
- blit(mCompositionFbo, mBlurredFbo);
-
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- auto radius = mRadius / 6.0f;
-
- // Calculate how many passes we'll do, based on the radius.
- // Too many passes will make the operation expensive.
- auto passes = min(kMaxPasses, (uint32_t)ceil(radius));
-
- // We'll ping pong between our textures, to accumulate the result of various offsets.
- mProgram.useProgram();
- GLFramebuffer* draw = &mFbo;
- GLFramebuffer* read = &mBlurredFbo;
- float stepX = radius / (float)mCompositionFbo.getBufferWidth() / (float)passes;
- float stepY = radius / (float)mCompositionFbo.getBufferHeight() / (float)passes;
- glActiveTexture(GL_TEXTURE0);
- glUniform1i(mTextureLoc, 0);
- for (auto i = 0; i < passes; i++) {
- ATRACE_NAME("KawaseBlurFilter::renderPass");
- draw->bind();
-
- glViewport(0, 0, draw->getBufferWidth(), draw->getBufferHeight());
- glBindTexture(GL_TEXTURE_2D, read->getTextureName());
- glUniform2f(mOffsetLoc, stepX * i, stepY * i);
- mEngine.checkErrors("Setting uniforms");
-
- drawMesh(mUvLoc, mPosLoc);
-
- // Swap buffers for next iteration
- auto tmp = draw;
- draw = read;
- read = tmp;
- }
-
- // Copy texture, given that we're expected to end on mBlurredFbo.
- if (draw == &mBlurredFbo) {
- blit(mFbo, mBlurredFbo);
- }
-
- // Cleanup
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-
- return NO_ERROR;
-}
-
-string KawaseBlurFilter::getFragmentShader() const {
- return R"SHADER(#version 310 es
- precision mediump float;
-
- uniform sampler2D uTexture;
- uniform vec2 uOffset;
-
- highp in vec2 vUV;
- out vec4 fragColor;
-
- void main() {
- fragColor = texture(uTexture, vUV, 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2( uOffset.x, -uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, uOffset.y), 0.0);
- fragColor += texture(uTexture, vUV + vec2(-uOffset.x, -uOffset.y), 0.0);
-
- fragColor = vec4(fragColor.rgb * 0.2, 1.0);
- }
- )SHADER";
-}
-
-void KawaseBlurFilter::blit(GLFramebuffer& read, GLFramebuffer& draw) const {
- read.bindAsReadBuffer();
- draw.bindAsDrawBuffer();
- glBlitFramebuffer(0, 0, read.getBufferWidth(), read.getBufferHeight(), 0, 0,
- draw.getBufferWidth(), draw.getBufferHeight(), GL_COLOR_BUFFER_BIT,
- GL_LINEAR);
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
-}
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/renderengine/gl/filters/KawaseBlurFilter.h b/libs/renderengine/gl/filters/KawaseBlurFilter.h
deleted file mode 100644
index 20009cf..0000000
--- a/libs/renderengine/gl/filters/KawaseBlurFilter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <ui/GraphicTypes.h>
-#include "../GLESRenderEngine.h"
-#include "../GLFramebuffer.h"
-#include "BlurFilter.h"
-#include "GenericProgram.h"
-
-using namespace std;
-
-namespace android {
-namespace renderengine {
-namespace gl {
-
-class KawaseBlurFilter : public BlurFilter {
-public:
- static constexpr uint32_t kMaxPasses = 6;
-
- explicit KawaseBlurFilter(GLESRenderEngine& engine);
- status_t prepare() override;
- void allocateTextures() override;
-
-private:
- string getFragmentShader() const;
- void blit(GLFramebuffer& read, GLFramebuffer& draw) const;
-
- GLFramebuffer mFbo;
-
- GenericProgram mProgram;
- GLuint mPosLoc;
- GLuint mUvLoc;
- GLuint mTextureLoc;
- GLuint mOffsetLoc;
-};
-
-} // namespace gl
-} // namespace renderengine
-} // namespace android
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index 30c48c8..d8e4059 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -77,6 +77,7 @@
outDescriptorInfo->layerCount = layerCount;
outDescriptorInfo->format = static_cast<hardware::graphics::common::V1_2::PixelFormat>(format);
outDescriptorInfo->usage = usage;
+ outDescriptorInfo->reservedSize = 0;
}
} // anonymous namespace
@@ -364,7 +365,7 @@
}
*outYcbcr = ycbcr;
- return static_cast<status_t>(Error::UNSUPPORTED);
+ return static_cast<status_t>(Error::NONE);
}
int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index cd2a448..bf487c4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -67,20 +67,19 @@
// ----------------------------------------------------------------------------
Region::Region() {
- mStorage.push_back(Rect(0, 0));
+ mStorage.add(Rect(0,0));
}
Region::Region(const Region& rhs)
+ : mStorage(rhs.mStorage)
{
- mStorage.clear();
- mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
#if defined(VALIDATE_REGIONS)
validate(rhs, "rhs copy-ctor");
#endif
}
Region::Region(const Rect& rhs) {
- mStorage.push_back(rhs);
+ mStorage.add(rhs);
}
Region::~Region()
@@ -101,8 +100,8 @@
* final, correctly ordered region buffer. Each rectangle will be compared with the span directly
* above it, and subdivided to resolve any remaining T-junctions.
*/
-static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end, FatVector<Rect>& dst,
- int spanDirection) {
+static void reverseRectsResolvingJunctions(const Rect* begin, const Rect* end,
+ Vector<Rect>& dst, int spanDirection) {
dst.clear();
const Rect* current = end - 1;
@@ -110,7 +109,7 @@
// add first span immediately
do {
- dst.push_back(*current);
+ dst.add(*current);
current--;
} while (current->top == lastTop && current >= begin);
@@ -148,12 +147,12 @@
if (prev.right <= left) break;
if (prev.right > left && prev.right < right) {
- dst.push_back(Rect(prev.right, top, right, bottom));
+ dst.add(Rect(prev.right, top, right, bottom));
right = prev.right;
}
if (prev.left > left && prev.left < right) {
- dst.push_back(Rect(prev.left, top, right, bottom));
+ dst.add(Rect(prev.left, top, right, bottom));
right = prev.left;
}
@@ -167,12 +166,12 @@
if (prev.left >= right) break;
if (prev.left > left && prev.left < right) {
- dst.push_back(Rect(left, top, prev.left, bottom));
+ dst.add(Rect(left, top, prev.left, bottom));
left = prev.left;
}
if (prev.right > left && prev.right < right) {
- dst.push_back(Rect(left, top, prev.right, bottom));
+ dst.add(Rect(left, top, prev.right, bottom));
left = prev.right;
}
// if an entry in the previous span is too far left, nothing further right in the
@@ -184,7 +183,7 @@
}
if (left < right) {
- dst.push_back(Rect(left, top, right, bottom));
+ dst.add(Rect(left, top, right, bottom));
}
current--;
@@ -202,14 +201,13 @@
if (r.isEmpty()) return r;
if (r.isRect()) return r;
- FatVector<Rect> reversed;
+ Vector<Rect> reversed;
reverseRectsResolvingJunctions(r.begin(), r.end(), reversed, direction_RTL);
Region outputRegion;
- reverseRectsResolvingJunctions(reversed.data(), reversed.data() + reversed.size(),
- outputRegion.mStorage, direction_LTR);
- outputRegion.mStorage.push_back(
- r.getBounds()); // to make region valid, mStorage must end with bounds
+ reverseRectsResolvingJunctions(reversed.begin(), reversed.end(),
+ outputRegion.mStorage, direction_LTR);
+ outputRegion.mStorage.add(r.getBounds()); // to make region valid, mStorage must end with bounds
#if defined(VALIDATE_REGIONS)
validate(outputRegion, "T-Junction free region");
@@ -224,8 +222,7 @@
validate(*this, "this->operator=");
validate(rhs, "rhs.operator=");
#endif
- mStorage.clear();
- mStorage.insert(mStorage.begin(), rhs.mStorage.begin(), rhs.mStorage.end());
+ mStorage = rhs.mStorage;
return *this;
}
@@ -234,7 +231,7 @@
if (mStorage.size() >= 2) {
const Rect bounds(getBounds());
mStorage.clear();
- mStorage.push_back(bounds);
+ mStorage.add(bounds);
}
return *this;
}
@@ -258,25 +255,25 @@
void Region::clear()
{
mStorage.clear();
- mStorage.push_back(Rect(0, 0));
+ mStorage.add(Rect(0,0));
}
void Region::set(const Rect& r)
{
mStorage.clear();
- mStorage.push_back(r);
+ mStorage.add(r);
}
void Region::set(int32_t w, int32_t h)
{
mStorage.clear();
- mStorage.push_back(Rect(w, h));
+ mStorage.add(Rect(w, h));
}
void Region::set(uint32_t w, uint32_t h)
{
mStorage.clear();
- mStorage.push_back(Rect(w, h));
+ mStorage.add(Rect(w, h));
}
bool Region::isTriviallyEqual(const Region& region) const {
@@ -302,7 +299,8 @@
void Region::addRectUnchecked(int l, int t, int r, int b)
{
Rect rect(l,t,r,b);
- mStorage.insert(mStorage.end() - 1, rect);
+ size_t where = mStorage.size() - 1;
+ mStorage.insertAt(rect, where, 1);
}
// ----------------------------------------------------------------------------
@@ -352,7 +350,7 @@
Region& Region::scaleSelf(float sx, float sy) {
size_t count = mStorage.size();
- Rect* rects = mStorage.data();
+ Rect* rects = mStorage.editArray();
while (count) {
rects->left = static_cast<int32_t>(static_cast<float>(rects->left) * sx + 0.5f);
rects->right = static_cast<int32_t>(static_cast<float>(rects->right) * sx + 0.5f);
@@ -457,10 +455,10 @@
class Region::rasterizer : public region_operator<Rect>::region_rasterizer
{
Rect bounds;
- FatVector<Rect>& storage;
+ Vector<Rect>& storage;
Rect* head;
Rect* tail;
- FatVector<Rect> span;
+ Vector<Rect> span;
Rect* cur;
public:
explicit rasterizer(Region& reg)
@@ -487,8 +485,8 @@
flushSpan();
}
if (storage.size()) {
- bounds.top = storage.front().top;
- bounds.bottom = storage.back().bottom;
+ bounds.top = storage.itemAt(0).top;
+ bounds.bottom = storage.top().bottom;
if (storage.size() == 1) {
storage.clear();
}
@@ -496,7 +494,7 @@
bounds.left = 0;
bounds.right = 0;
}
- storage.push_back(bounds);
+ storage.add(bounds);
}
void Region::rasterizer::operator()(const Rect& rect)
@@ -511,15 +509,15 @@
return;
}
}
- span.push_back(rect);
- cur = span.data() + (span.size() - 1);
+ span.add(rect);
+ cur = span.editArray() + (span.size() - 1);
}
void Region::rasterizer::flushSpan()
{
bool merge = false;
if (tail-head == ssize_t(span.size())) {
- Rect const* p = span.data();
+ Rect const* p = span.editArray();
Rect const* q = head;
if (p->top == q->bottom) {
merge = true;
@@ -534,17 +532,17 @@
}
}
if (merge) {
- const int bottom = span.front().bottom;
+ const int bottom = span[0].bottom;
Rect* r = head;
while (r != tail) {
r->bottom = bottom;
r++;
}
} else {
- bounds.left = min(span.front().left, bounds.left);
- bounds.right = max(span.back().right, bounds.right);
- storage.insert(storage.end(), span.begin(), span.end());
- tail = storage.data() + storage.size();
+ bounds.left = min(span.itemAt(0).left, bounds.left);
+ bounds.right = max(span.top().right, bounds.right);
+ storage.appendVector(span);
+ tail = storage.editArray() + storage.size();
head = tail - span.size();
}
span.clear();
@@ -552,7 +550,7 @@
bool Region::validate(const Region& reg, const char* name, bool silent)
{
- if (reg.mStorage.empty()) {
+ if (reg.mStorage.isEmpty()) {
ALOGE_IF(!silent, "%s: mStorage is empty, which is never valid", name);
// return immediately as the code below assumes mStorage is non-empty
return false;
@@ -691,8 +689,9 @@
}
sk_dst.op(sk_lhs, sk_rhs, sk_op);
- if (sk_dst.empty() && dst.empty()) return;
-
+ if (sk_dst.isEmpty() && dst.isEmpty())
+ return;
+
bool same = true;
Region::const_iterator head = dst.begin();
Region::const_iterator const tail = dst.end();
@@ -787,7 +786,7 @@
validate(reg, "translate (before)");
#endif
size_t count = reg.mStorage.size();
- Rect* rects = reg.mStorage.data();
+ Rect* rects = reg.mStorage.editArray();
while (count) {
rects->offsetBy(dx, dy);
rects++;
@@ -867,25 +866,24 @@
ALOGE("Region::unflatten() failed, invalid region");
return BAD_VALUE;
}
- mStorage.clear();
- mStorage.insert(mStorage.begin(), result.mStorage.begin(), result.mStorage.end());
+ mStorage = result.mStorage;
return NO_ERROR;
}
// ----------------------------------------------------------------------------
Region::const_iterator Region::begin() const {
- return mStorage.data();
+ return mStorage.array();
}
Region::const_iterator Region::end() const {
// Workaround for b/77643177
// mStorage should never be empty, but somehow it is and it's causing
// an abort in ubsan
- if (mStorage.empty()) return mStorage.data();
+ if (mStorage.isEmpty()) return mStorage.array();
size_t numRects = isRect() ? 1 : mStorage.size() - 1;
- return mStorage.data() + numRects;
+ return mStorage.array() + numRects;
}
Rect const* Region::getArray(size_t* count) const {
diff --git a/libs/ui/include/ui/DisplayConfig.h b/libs/ui/include/ui/DisplayConfig.h
index 09b8211..d6fbaab 100644
--- a/libs/ui/include/ui/DisplayConfig.h
+++ b/libs/ui/include/ui/DisplayConfig.h
@@ -33,6 +33,7 @@
nsecs_t appVsyncOffset = 0;
nsecs_t sfVsyncOffset = 0;
nsecs_t presentationDeadline = 0;
+ int configGroup = -1;
};
static_assert(std::is_trivially_copyable_v<DisplayConfig>);
diff --git a/libs/ui/include/ui/FatVector.h b/libs/ui/include/ui/FatVector.h
deleted file mode 100644
index 25fe3a0..0000000
--- a/libs/ui/include/ui/FatVector.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_REGION_FAT_VECTOR_H
-#define ANDROID_REGION_FAT_VECTOR_H
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <utils/Log.h>
-#include <type_traits>
-
-#include <vector>
-
-namespace android {
-
-template <typename T, size_t SIZE = 4>
-class InlineStdAllocator {
-public:
- struct Allocation {
- private:
- Allocation(const Allocation&) = delete;
- void operator=(const Allocation&) = delete;
-
- public:
- Allocation() {}
- // char array instead of T array, so memory is uninitialized, with no destructors run
- char array[sizeof(T) * SIZE];
- bool inUse = false;
- };
-
- typedef T value_type; // needed to implement std::allocator
- typedef T* pointer; // needed to implement std::allocator
-
- explicit InlineStdAllocator(Allocation& allocation) : mAllocation(allocation) {}
- InlineStdAllocator(const InlineStdAllocator& other) : mAllocation(other.mAllocation) {}
- ~InlineStdAllocator() {}
-
- T* allocate(size_t num, const void* = 0) {
- if (!mAllocation.inUse && num <= SIZE) {
- mAllocation.inUse = true;
- return static_cast<T*>(static_cast<void*>(mAllocation.array));
- } else {
- return static_cast<T*>(static_cast<void*>(malloc(num * sizeof(T))));
- }
- }
-
- void deallocate(pointer p, size_t) {
- if (p == static_cast<T*>(static_cast<void*>(mAllocation.array))) {
- mAllocation.inUse = false;
- } else {
- // 'free' instead of delete here - destruction handled separately
- free(p);
- }
- }
- Allocation& mAllocation;
-};
-
-/**
- * std::vector with SIZE elements preallocated into an internal buffer.
- *
- * Useful for avoiding the cost of malloc in cases where only SIZE or
- * fewer elements are needed in the common case.
- */
-template <typename T, size_t SIZE = 4>
-class FatVector : public std::vector<T, InlineStdAllocator<T, SIZE>> {
-public:
- FatVector()
- : std::vector<T, InlineStdAllocator<T, SIZE>>(InlineStdAllocator<T, SIZE>(mAllocation)) {
- this->reserve(SIZE);
- }
-
- explicit FatVector(size_t capacity) : FatVector() { this->resize(capacity); }
-
-private:
- typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
-};
-
-} // namespace android
-
-#endif // ANDROID_REGION_FAT_VECTOR_H
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 6bb7b8d..2db3b10 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -21,13 +21,13 @@
#include <sys/types.h>
#include <ostream>
+#include <utils/Vector.h>
+
#include <ui/Rect.h>
#include <utils/Flattenable.h>
#include <android-base/macros.h>
-#include "FatVector.h"
-
#include <string>
namespace android {
@@ -180,7 +180,7 @@
// with an extra Rect as the last element which is set to the
// bounds of the region. However, if the region is
// a simple Rect then mStorage contains only that rect.
- FatVector<Rect> mStorage;
+ Vector<Rect> mStorage;
};
@@ -235,3 +235,4 @@
}; // namespace android
#endif // ANDROID_UI_REGION_H
+
diff --git a/libs/ui/include/ui/Size.h b/libs/ui/include/ui/Size.h
index d9b713d..f1e8252 100644
--- a/libs/ui/include/ui/Size.h
+++ b/libs/ui/include/ui/Size.h
@@ -19,6 +19,7 @@
#include <algorithm>
#include <cstdint>
#include <limits>
+#include <ostream>
#include <type_traits>
#include <utility>
@@ -108,31 +109,70 @@
// Takes a value of type FromType, and ensures it can be represented as a value of type ToType,
// clamping the input value to the output range if necessary.
template <typename ToType, typename FromType>
- static Size::remove_cv_reference_t<ToType> clamp(
- typename std::enable_if<
- std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_bounded &&
- std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_bounded,
- FromType&&>::type v) {
- static constexpr auto toHighest = std::numeric_limits<remove_cv_reference_t<ToType>>::max();
- static constexpr auto toLowest =
- std::numeric_limits<remove_cv_reference_t<ToType>>::lowest();
- static constexpr auto fromHighest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::max();
- static constexpr auto fromLowest =
- std::numeric_limits<remove_cv_reference_t<FromType>>::lowest();
+ static Size::remove_cv_reference_t<ToType>
+ clamp(typename std::enable_if<
+ std::numeric_limits<Size::remove_cv_reference_t<ToType>>::is_specialized &&
+ std::numeric_limits<Size::remove_cv_reference_t<FromType>>::is_specialized,
+ FromType>::type v) {
+ using BareToType = remove_cv_reference_t<ToType>;
+ using BareFromType = remove_cv_reference_t<FromType>;
+ static constexpr auto toHighest = std::numeric_limits<BareToType>::max();
+ static constexpr auto toLowest = std::numeric_limits<BareToType>::lowest();
+ static constexpr auto fromHighest = std::numeric_limits<BareFromType>::max();
+ static constexpr auto fromLowest = std::numeric_limits<BareFromType>::lowest();
- // A clamp is needed if the range of FromType is not a subset of the range of ToType
- static constexpr bool isClampNeeded = (toLowest > fromLowest) || (toHighest < fromHighest);
+ // Get the closest representation of [toLowest, toHighest] in type
+ // FromType to use to clamp the input value before conversion.
+
+ // std::common_type<...> is used to get a value-preserving type for the
+ // top end of the range.
+ using CommonHighestType = std::common_type_t<BareToType, BareFromType>;
+
+ // std::make_signed<std::common_type<...>> is used to get a
+ // value-preserving type for the bottom end of the range, except this is
+ // a bit trickier for non-integer types like float.
+ using CommonLowestType =
+ std::conditional_t<std::numeric_limits<CommonHighestType>::is_integer,
+ std::make_signed_t<std::conditional_t<
+ std::numeric_limits<CommonHighestType>::is_integer,
+ CommonHighestType, int /* not used */>>,
+ CommonHighestType>;
+
+ // We can then compute the clamp range in a way that can be later
+ // trivially converted to either the 'from' or 'to' types, and be
+ // representabile in either.
+ static constexpr auto commonClampHighest =
+ std::min(static_cast<CommonHighestType>(fromHighest),
+ static_cast<CommonHighestType>(toHighest));
+ static constexpr auto commonClampLowest =
+ std::max(static_cast<CommonLowestType>(fromLowest),
+ static_cast<CommonLowestType>(toLowest));
+
+ static constexpr auto fromClampHighest = static_cast<BareFromType>(commonClampHighest);
+ static constexpr auto fromClampLowest = static_cast<BareFromType>(commonClampLowest);
+
+ // A clamp is needed only if the range we are clamping to is not the
+ // same as the range of the input.
+ static constexpr bool isClampNeeded =
+ (fromLowest != fromClampLowest) || (fromHighest != fromClampHighest);
// If a clamp is not needed, the conversion is just a trivial cast.
if (!isClampNeeded) {
- return static_cast<ToType>(v);
+ return static_cast<BareToType>(v);
}
- // Otherwise we leverage implicit conversion to safely compare values of
- // different types, to ensure we return a value clamped to the range of
- // ToType.
- return v < toLowest ? toLowest : (static_cast<ToType>(v) > toHighest ? toHighest : static_cast<ToType>(v));
+ // Note: Clang complains about the value of INT32_MAX not being
+ // convertible back to int32_t from float if this is made "constexpr",
+ // when clamping a float value to an int32_t value. This is however
+ // covered by a test case to ensure the run-time cast works correctly.
+ const auto toClampHighest = static_cast<BareToType>(commonClampHighest);
+ const auto toClampLowest = static_cast<BareToType>(commonClampLowest);
+
+ // Otherwise clamping is done by using the already computed endpoints
+ // for each type.
+ return (v <= fromClampLowest)
+ ? toClampLowest
+ : ((v >= fromClampHighest) ? toClampHighest : static_cast<BareToType>(v));
}
};
@@ -154,5 +194,10 @@
return lhs.height < rhs.height;
}
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Size& size, ::std::ostream* os) {
+ *os << "Size(" << size.width << ", " << size.height << ")";
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include_vndk/ui/FatVector.h b/libs/ui/include_vndk/ui/FatVector.h
deleted file mode 120000
index bf30166..0000000
--- a/libs/ui/include_vndk/ui/FatVector.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/FatVector.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index ff55bc2..605c5a9 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -111,6 +111,7 @@
cc_test {
name: "Size_test",
+ test_suites: ["device-tests"],
shared_libs: ["libui"],
srcs: ["Size_test.cpp"],
cflags: ["-Wall", "-Werror"],
diff --git a/libs/ui/tests/Size_test.cpp b/libs/ui/tests/Size_test.cpp
index 69e1ac8..38f37ad 100644
--- a/libs/ui/tests/Size_test.cpp
+++ b/libs/ui/tests/Size_test.cpp
@@ -19,8 +19,18 @@
#include <cmath>
#include <cstdlib>
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wimplicit-int-float-conversion"
+#pragma clang diagnostic error "-Wconversion"
+#endif // __clang__
+
#include <ui/Size.h>
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif // __clang__
+
#include <gtest/gtest.h>
namespace android {
@@ -176,9 +186,34 @@
TEST(SizeTest, FloatRangeIsClamped) {
ClampTest(std::numeric_limits<float>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), std::numeric_limits<float>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::max(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::max(), 0)));
ClampTest(float(0), int32_t(0));
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0),
+ static_cast<int32_t>(nexttowardf(std::numeric_limits<int32_t>::lowest(), 0)));
+ ClampTest(static_cast<float>(std::numeric_limits<int32_t>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
+ ClampTest(nexttowardf(std::numeric_limits<int32_t>::lowest(),
+ std::numeric_limits<float>::lowest()),
+ std::numeric_limits<int32_t>::lowest());
ClampTest(std::numeric_limits<float>::lowest(), std::numeric_limits<int32_t>::lowest());
}
+TEST(SizeTest, Uint32RangeIsClamped) {
+ ClampTest(std::numeric_limits<uint32_t>::max(), std::numeric_limits<int32_t>::max());
+ ClampTest(std::numeric_limits<uint32_t>::max() - 1, std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) + 1,
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()),
+ std::numeric_limits<int32_t>::max());
+ ClampTest(static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) - 1,
+ std::numeric_limits<int32_t>::max() - 1);
+ ClampTest(uint32_t(0), int32_t(0));
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/tests/TEST_MAPPING b/libs/ui/tests/TEST_MAPPING
new file mode 100644
index 0000000..7fcd7de
--- /dev/null
+++ b/libs/ui/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "Size_test"
+ }
+ ]
+}
diff --git a/opengl/TEST_MAPPING b/opengl/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/opengl/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp
index 67d69b4..a58e87c 100644
--- a/opengl/libs/EGL/egl_display.cpp
+++ b/opengl/libs/EGL/egl_display.cpp
@@ -158,13 +158,16 @@
// NOTE: This is only valid if the backend is OpenGL
attrs.push_back(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE);
attrs.push_back(vendorEGL);
+
+ // Context virtualization is only available on GL back-end.
+ // Needed to support threading with GL back-end
+ attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
+ attrs.push_back(EGL_FALSE);
break;
default:
- ALOGV("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
+ ALOGE("%s: Requesting Unknown (%d) ANGLE back-end", __FUNCTION__, cnx->angleBackend);
break;
}
- attrs.push_back(EGL_PLATFORM_ANGLE_CONTEXT_VIRTUALIZATION_ANGLE);
- attrs.push_back(EGL_FALSE);
return true;
}
diff --git a/services/automotive/display/Android.bp b/services/automotive/display/Android.bp
index 8ff0711..c3da216 100644
--- a/services/automotive/display/Android.bp
+++ b/services/automotive/display/Android.bp
@@ -40,4 +40,8 @@
cflags: [
"-DLOG_TAG=\"AutomotiveDisplayService\""
],
+
+ vintf_fragments: [
+ "manifest_android.frameworks.automotive.display@1.0.xml",
+ ],
}
diff --git a/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
new file mode 100644
index 0000000..464dcac
--- /dev/null
+++ b/services/automotive/display/manifest_android.frameworks.automotive.display@1.0.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.automotive.display</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IAutomotiveDisplayProxyService</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index d094532..231d068 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -30,16 +30,11 @@
namespace android {
-GpuStats::GpuStats() {
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
- AStatsManager_registerPullAtomCallback(android::util::GPU_STATS_APP_INFO,
- GpuStats::pullAtomCallback, nullptr, this);
-}
-
GpuStats::~GpuStats() {
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
- AStatsManager_unregisterPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ if (mStatsdRegistered) {
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
+ AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
+ }
}
static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
@@ -97,6 +92,7 @@
ATRACE_CALL();
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
ALOGV("Received:\n"
"\tdriverPackageName[%s]\n"
"\tdriverVersionName[%s]\n"
@@ -150,6 +146,7 @@
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
std::lock_guard<std::mutex> lock(mLock);
+ registerStatsdCallbacksIfNeeded();
if (!mAppStats.count(appStatsKey)) {
return;
}
@@ -179,6 +176,16 @@
mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
}
+void GpuStats::registerStatsdCallbacksIfNeeded() {
+ if (!mStatsdRegistered) {
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
+ GpuStats::pullAtomCallback, this);
+ mStatsdRegistered = true;
+ }
+}
+
void GpuStats::dump(const Vector<String16>& args, std::string* result) {
ATRACE_CALL();
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 8ca4e4e..55f0da1 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -30,7 +30,6 @@
class GpuStats {
public:
- GpuStats();
~GpuStats();
// Insert new gpu driver stats into global stats and app stats.
@@ -66,12 +65,16 @@
void dumpAppLocked(std::string* result);
// Append cpuVulkanVersion and glesVersion to system driver stats
void interceptSystemDriverStatsLocked();
+ // Registers statsd callbacks if they have not already been registered
+ void registerStatsdCallbacksIfNeeded();
// Below limits the memory usage of GpuStats to be less than 10KB. This is
// the preferred number for statsd while maintaining nice data quality.
static const size_t MAX_NUM_APP_RECORDS = 100;
// GpuStats access should be guarded by mLock.
std::mutex mLock;
+ // True if statsd callbacks have been registered.
+ bool mStatsdRegistered = false;
// Key is driver version code.
std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats;
// Key is <app package name>+<driver version code>.
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 4ec4e25..f67c9d0 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -52,7 +52,6 @@
"libstatslog",
"libutils",
"libui",
- "server_configurable_flags",
],
}
diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index db9f26e..b612ca7 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -45,10 +45,7 @@
T pop() {
std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLock(mLock);
- mHasElements.wait(lock, [this]{
- android::base::ScopedLockAssertion assumeLock(mLock);
- return !this->mQueue.empty();
- });
+ mHasElements.wait(lock, [this]() REQUIRES(mLock) { return !this->mQueue.empty(); });
T t = std::move(mQueue.front());
mQueue.erase(mQueue.begin());
return t;
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index ae9a348..77a0716 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -27,7 +27,6 @@
#if defined(__linux__)
#include <pthread.h>
#endif
-#include <server_configurable_flags/get_flags.h>
#include <unordered_set>
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
@@ -46,13 +45,6 @@
namespace android {
-static constexpr bool DEBUG = false;
-
-// Category (=namespace) name for the input settings that are applied at boot time
-static const char* INPUT_NATIVE_BOOT = "input_native_boot";
-// Feature flag name for the deep press feature
-static const char* DEEP_PRESS_ENABLED = "deep_press_enabled";
-
//Max number of elements to store in mEvents.
static constexpr size_t MAX_EVENTS = 5;
@@ -79,20 +71,6 @@
return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
}
-// Check if the "deep touch" feature is on.
-static bool deepPressEnabled() {
- std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
- std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
- if (flag_value == "1" || flag_value == "true") {
- ALOGI("Deep press feature enabled.");
- return true;
- }
- ALOGI("Deep press feature is not enabled.");
- return false;
-}
-
-
// --- ClassifierEvent ---
ClassifierEvent::ClassifierEvent(std::unique_ptr<NotifyMotionArgs> args) :
@@ -141,53 +119,40 @@
// --- MotionClassifier ---
-MotionClassifier::MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient) :
- mDeathRecipient(deathRecipient), mEvents(MAX_EVENTS) {
- mHalThread = std::thread(&MotionClassifier::callInputClassifierHal, this);
-#if defined(__linux__)
- // Set the thread name for debugging
- pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
-#endif
-}
-
-/**
- * This function may block for some time to initialize the HAL, so it should only be called
- * from the "InputClassifier HAL" thread.
- */
-bool MotionClassifier::init() {
- ensureHalThread(__func__);
- sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
- classifier::V1_0::IInputClassifier::getService();
- if (!service) {
- // Not really an error, maybe the device does not have this HAL,
- // but somehow the feature flag is flipped
- ALOGI("Could not obtain InputClassifier HAL");
- return false;
- }
-
- sp<android::hardware::hidl_death_recipient> recipient = mDeathRecipient.promote();
- if (recipient != nullptr) {
- const bool linked = service->linkToDeath(recipient, 0 /* cookie */).withDefault(false);
- if (!linked) {
- ALOGE("Could not link MotionClassifier to the HAL death");
- return false;
- }
- }
-
+MotionClassifier::MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service)
+ : mEvents(MAX_EVENTS), mService(service) {
// Under normal operation, we do not need to reset the HAL here. But in the case where system
// crashed, but HAL didn't, we may be connecting to an existing HAL process that might already
// have received events in the past. That means, that HAL could be in an inconsistent state
// once it receives events from the newly created MotionClassifier.
mEvents.push(ClassifierEvent::createHalResetEvent());
- {
- std::scoped_lock lock(mLock);
- if (mService) {
- ALOGE("MotionClassifier::%s should only be called once", __func__);
- }
- mService = service;
+ mHalThread = std::thread(&MotionClassifier::processEvents, this);
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mHalThread.native_handle(), "InputClassifier");
+#endif
+}
+
+std::unique_ptr<MotionClassifierInterface> MotionClassifier::create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient) {
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service =
+ classifier::V1_0::IInputClassifier::getService();
+ if (!service) {
+ // Not really an error, maybe the device does not have this HAL,
+ // but somehow the feature flag is flipped
+ ALOGI("Could not obtain InputClassifier HAL");
+ return nullptr;
}
- return true;
+
+ const bool linked = service->linkToDeath(deathRecipient, 0 /* cookie */).withDefault(false);
+ if (!linked) {
+ ALOGE("Could not link death recipient to the HAL death");
+ return nullptr;
+ }
+ // Using 'new' to access a non-public constructor
+ return std::unique_ptr<MotionClassifier>(new MotionClassifier(service));
}
MotionClassifier::~MotionClassifier() {
@@ -195,14 +160,6 @@
mHalThread.join();
}
-void MotionClassifier::ensureHalThread(const char* function) {
- if (DEBUG) {
- if (std::this_thread::get_id() != mHalThread.get_id()) {
- LOG_FATAL("Function %s should only be called from InputClassifier thread", function);
- }
- }
-}
-
/**
* Obtain the classification from the HAL for a given MotionEvent.
* Should only be called from the InputClassifier thread (mHalThread).
@@ -213,23 +170,7 @@
* To remove any possibility of negatively affecting the touch latency, the HAL
* is called from a dedicated thread.
*/
-void MotionClassifier::callInputClassifierHal() {
- ensureHalThread(__func__);
- const bool initialized = init();
- if (!initialized) {
- // MotionClassifier no longer useful.
- // Deliver death notification from a separate thread
- // because ~MotionClassifier may be invoked, which calls mHalThread.join()
- std::thread([deathRecipient = mDeathRecipient](){
- sp<android::hardware::hidl_death_recipient> recipient = deathRecipient.promote();
- if (recipient != nullptr) {
- recipient->serviceDied(0 /*cookie*/, nullptr);
- }
- }).detach();
- return;
- }
- // From this point on, mService is guaranteed to be non-null.
-
+void MotionClassifier::processEvents() {
while (true) {
ClassifierEvent event = mEvents.pop();
bool halResponseOk = true;
@@ -250,7 +191,7 @@
case ClassifierEventType::DEVICE_RESET: {
const int32_t deviceId = *(event.getDeviceId());
halResponseOk = mService->resetDevice(deviceId).isOk();
- setClassification(deviceId, MotionClassification::NONE);
+ clearDeviceState(deviceId);
break;
}
case ClassifierEventType::HAL_RESET: {
@@ -321,6 +262,12 @@
mClassifications[deviceId] = MotionClassification::NONE;
}
+void MotionClassifier::clearDeviceState(int32_t deviceId) {
+ std::scoped_lock lock(mLock);
+ mClassifications.erase(deviceId);
+ mLastDownTimes.erase(deviceId);
+}
+
MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) {
if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) {
updateLastDownTime(args.deviceId, args.downTime);
@@ -383,24 +330,41 @@
}
}
+// --- HalDeathRecipient
+
+InputClassifier::HalDeathRecipient::HalDeathRecipient(InputClassifier& parent) : mParent(parent) {}
+
+void InputClassifier::HalDeathRecipient::serviceDied(
+ uint64_t cookie, const wp<android::hidl::base::V1_0::IBase>& who) {
+ sp<android::hidl::base::V1_0::IBase> service = who.promote();
+ if (service) {
+ service->unlinkToDeath(this);
+ }
+ mParent.setMotionClassifier(nullptr);
+}
// --- InputClassifier ---
-InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) :
- mListener(listener) {
- // The rest of the initialization is done in onFirstRef, because we need to obtain
- // an sp to 'this' in order to register for HAL death notifications
-}
+InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener)
+ : mListener(listener), mHalDeathRecipient(new HalDeathRecipient(*this)) {}
-void InputClassifier::onFirstRef() {
- if (!deepPressEnabled()) {
- // If feature is not enabled, MotionClassifier should stay null to avoid unnecessary work.
- // When MotionClassifier is null, InputClassifier will forward all events
- // to the next InputListener, unmodified.
- return;
+void InputClassifier::setMotionClassifierEnabled(bool enabled) {
+ if (enabled) {
+ ALOGI("Enabling motion classifier");
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+ mInitializeMotionClassifierThread = std::thread(
+ [this] { setMotionClassifier(MotionClassifier::create(mHalDeathRecipient)); });
+#if defined(__linux__)
+ // Set the thread name for debugging
+ pthread_setname_np(mInitializeMotionClassifierThread.native_handle(),
+ "Create MotionClassifier");
+#endif
+ } else {
+ ALOGI("Disabling motion classifier");
+ setMotionClassifier(nullptr);
}
- std::scoped_lock lock(mLock);
- mMotionClassifier = std::make_unique<MotionClassifier>(this);
}
void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) {
@@ -441,21 +405,15 @@
mListener->notifyDeviceReset(args);
}
-void InputClassifier::serviceDied(uint64_t /*cookie*/,
- const wp<android::hidl::base::V1_0::IBase>& who) {
+void InputClassifier::setMotionClassifier(
+ std::unique_ptr<MotionClassifierInterface> motionClassifier) {
std::scoped_lock lock(mLock);
- ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null");
- mMotionClassifier = nullptr;
- sp<android::hidl::base::V1_0::IBase> service = who.promote();
- if (service) {
- service->unlinkToDeath(this);
- }
+ mMotionClassifier = std::move(motionClassifier);
}
void InputClassifier::dump(std::string& dump) {
std::scoped_lock lock(mLock);
dump += "Input Classifier State:\n";
-
dump += INDENT1 "Motion Classifier:\n";
if (mMotionClassifier) {
mMotionClassifier->dump(dump);
@@ -465,4 +423,10 @@
dump += "\n";
}
+InputClassifier::~InputClassifier() {
+ if (mInitializeMotionClassifierThread.joinable()) {
+ mInitializeMotionClassifierThread.join();
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h
index 47e20db..03510a6 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -19,8 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/RefBase.h>
-#include <unordered_map>
#include <thread>
+#include <unordered_map>
#include "BlockingQueue.h"
#include "InputListener.h"
@@ -90,6 +90,7 @@
*/
class InputClassifierInterface : public virtual RefBase, public InputListenerInterface {
public:
+ virtual void setMotionClassifierEnabled(bool enabled) = 0;
/**
* Dump the state of the input classifier.
* This method may be called on any thread (usually by the input manager).
@@ -113,23 +114,23 @@
*/
class MotionClassifier final : public MotionClassifierInterface {
public:
- /**
- * The deathRecipient will be subscribed to the HAL death. If the death recipient
- * owns MotionClassifier and receives HAL death, it should delete its copy of it.
- * The callback serviceDied will also be sent if the MotionClassifier itself fails
- * to initialize. If the MotionClassifier fails to initialize, it is not useful, and
- * should be deleted.
- * If no death recipient is supplied, then the registration step will be skipped, so there will
- * be no listeners registered for the HAL death. This is useful for testing
- * MotionClassifier in isolation.
+ /*
+ * Create an instance of MotionClassifier.
+ * The death recipient, if provided, will be subscribed to the HAL death.
+ * The death recipient could be used to destroy MotionClassifier.
+ *
+ * This function should be called asynchronously, because getService takes a long time.
*/
- explicit MotionClassifier(sp<android::hardware::hidl_death_recipient> deathRecipient = nullptr);
+ static std::unique_ptr<MotionClassifierInterface> create(
+ sp<android::hardware::hidl_death_recipient> deathRecipient);
+
~MotionClassifier();
/**
* Classifies events asynchronously; that is, it doesn't block events on a classification,
- * but instead sends them over to the classifier HAL and after a classification is
- * determined, it then marks the next event it sees in the stream with it.
+ * but instead sends them over to the classifier HAL. After a classification of a specific
+ * event is determined, MotionClassifier then marks the next event in the stream with this
+ * classification.
*
* Therefore, it is acceptable to have the classifications be delayed by 1-2 events
* in a particular gesture.
@@ -141,15 +142,9 @@
virtual void dump(std::string& dump) override;
private:
- /**
- * Initialize MotionClassifier.
- * Return true if initializaion is successful.
- */
- bool init();
- /**
- * Entity that will be notified of the HAL death (most likely InputClassifier).
- */
- wp<android::hardware::hidl_death_recipient> mDeathRecipient;
+ friend class MotionClassifierTest; // to create MotionClassifier with a test HAL implementation
+ explicit MotionClassifier(
+ sp<android::hardware::input::classifier::V1_0::IInputClassifier> service);
// The events that need to be sent to the HAL.
BlockingQueue<ClassifierEvent> mEvents;
@@ -164,14 +159,9 @@
*/
std::thread mHalThread;
/**
- * Print an error message if the caller is not on the InputClassifier thread.
- * Caller must supply the name of the calling function as __func__
+ * Process events and call the InputClassifier HAL
*/
- void ensureHalThread(const char* function);
- /**
- * Call the InputClassifier HAL
- */
- void callInputClassifierHal();
+ void processEvents();
/**
* Access to the InputClassifier HAL. May be null if init() hasn't completed yet.
* When init() successfully completes, mService is guaranteed to remain non-null and to not
@@ -212,6 +202,8 @@
void updateLastDownTime(int32_t deviceId, nsecs_t downTime);
+ void clearDeviceState(int32_t deviceId);
+
/**
* Exit the InputClassifier HAL thread.
* Useful for tests to ensure proper cleanup.
@@ -223,19 +215,15 @@
const char* getServiceStatus() REQUIRES(mLock);
};
-
/**
* Implementation of the InputClassifierInterface.
* Represents a separate stage of input processing. All of the input events go through this stage.
* Acts as a passthrough for all input events except for motion events.
* The events of motion type are sent to MotionClassifier.
*/
-class InputClassifier : public InputClassifierInterface,
- public android::hardware::hidl_death_recipient {
+class InputClassifier : public InputClassifierInterface {
public:
explicit InputClassifier(const sp<InputListenerInterface>& listener);
- // Some of the constructor logic is finished in onFirstRef
- virtual void onFirstRef() override;
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -243,17 +231,47 @@
virtual void notifySwitch(const NotifySwitchArgs* args) override;
virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override;
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override;
-
virtual void dump(std::string& dump) override;
+ ~InputClassifier();
+
+ // Called from InputManager
+ virtual void setMotionClassifierEnabled(bool enabled) override;
+
private:
// Protect access to mMotionClassifier, since it may become null via a hidl callback
std::mutex mLock;
- std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
// The next stage to pass input events to
sp<InputListenerInterface> mListener;
+
+ std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock);
+ std::thread mInitializeMotionClassifierThread;
+ /**
+ * Set the value of mMotionClassifier.
+ * This is called from 2 different threads:
+ * 1) mInitializeMotionClassifierThread, when we have constructed a MotionClassifier
+ * 2) A binder thread of the HalDeathRecipient, which is created when HAL dies. This would cause
+ * mMotionClassifier to become nullptr.
+ */
+ void setMotionClassifier(std::unique_ptr<MotionClassifierInterface> motionClassifier);
+
+ /**
+ * The deathRecipient will call setMotionClassifier(null) when the HAL dies.
+ */
+ class HalDeathRecipient : public android::hardware::hidl_death_recipient {
+ public:
+ explicit HalDeathRecipient(InputClassifier& parent);
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+ InputClassifier& mParent;
+ };
+ // We retain a reference to death recipient, because the death recipient will be calling
+ // ~MotionClassifier if the HAL dies.
+ // If we don't retain a reference, and MotionClassifier is the only owner of the death
+ // recipient, the serviceDied call will cause death recipient to call its own destructor.
+ sp<HalDeathRecipient> mHalDeathRecipient;
};
} // namespace android
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index c7c61cf..fc771a2 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -132,4 +132,8 @@
mDispatcher->unregisterInputChannel(channel);
}
+void InputManager::setMotionClassifierEnabled(bool enabled) {
+ mClassifier->setMotionClassifierEnabled(enabled);
+}
+
} // namespace android
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 586097f..0158441 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -100,6 +100,8 @@
virtual void registerInputChannel(const sp<InputChannel>& channel);
virtual void unregisterInputChannel(const sp<InputChannel>& channel);
+ void setMotionClassifierEnabled(bool enabled);
+
private:
sp<InputReaderInterface> mReader;
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 40086ef..ab74a04 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -22,6 +22,9 @@
#include <android/hardware/input/classifier/1.0/IInputClassifier.h>
using namespace android::hardware::input;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::input::common::V1_0::Classification;
namespace android {
@@ -132,6 +135,49 @@
ASSERT_EQ(args, outArgs);
}
+TEST_F(InputClassifierTest, SetMotionClassifier_Enabled) {
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+TEST_F(InputClassifierTest, SetMotionClassifier_Disabled) {
+ mClassifier->setMotionClassifierEnabled(false);
+}
+
+/**
+ * Try to break it by calling setMotionClassifierEnabled multiple times.
+ */
+TEST_F(InputClassifierTest, SetMotionClassifier_Multiple) {
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(false);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+ mClassifier->setMotionClassifierEnabled(true);
+}
+
+/**
+ * A minimal implementation of IInputClassifier.
+ */
+struct TestHal : public android::hardware::input::classifier::V1_0::IInputClassifier {
+ Return<Classification> classify(
+ const android::hardware::input::common::V1_0::MotionEvent& event) override {
+ return Classification::NONE;
+ };
+ Return<void> reset() override { return Void(); };
+ Return<void> resetDevice(int32_t deviceId) override { return Void(); };
+};
+
+/**
+ * An entity that will be subscribed to the HAL death.
+ */
+class TestDeathRecipient : public android::hardware::hidl_death_recipient {
+public:
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override{};
+};
+
// --- MotionClassifierTest ---
class MotionClassifierTest : public testing::Test {
@@ -139,7 +185,14 @@
std::unique_ptr<MotionClassifierInterface> mMotionClassifier;
virtual void SetUp() override {
- mMotionClassifier = std::make_unique<MotionClassifier>();
+ mMotionClassifier = MotionClassifier::create(new TestDeathRecipient());
+ if (mMotionClassifier == nullptr) {
+ // If the device running this test does not have IInputClassifier service,
+ // use the test HAL instead.
+ // Using 'new' to access non-public constructor
+ mMotionClassifier =
+ std::unique_ptr<MotionClassifier>(new MotionClassifier(new TestHal()));
+ }
}
};
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d30c4f1..1f283b1 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1445,7 +1445,6 @@
ASSERT_EQ(keyArgs.action, verifiedKey.action);
ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
- ASSERT_EQ(keyArgs.eventTime, verifiedKey.eventTimeNanos);
ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
@@ -1453,6 +1452,43 @@
ASSERT_EQ(0, verifiedKey.repeatCount);
}
+TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ InputEvent* event = window->consume();
+ ASSERT_NE(event, nullptr);
+
+ std::unique_ptr<VerifiedInputEvent> verified = mDispatcher->verifyInputEvent(*event);
+ ASSERT_NE(verified, nullptr);
+ ASSERT_EQ(verified->type, VerifiedInputEvent::Type::MOTION);
+
+ EXPECT_EQ(motionArgs.eventTime, verified->eventTimeNanos);
+ EXPECT_EQ(motionArgs.deviceId, verified->deviceId);
+ EXPECT_EQ(motionArgs.source, verified->source);
+ EXPECT_EQ(motionArgs.displayId, verified->displayId);
+
+ const VerifiedMotionEvent& verifiedMotion = static_cast<const VerifiedMotionEvent&>(*verified);
+
+ EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX);
+ EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY);
+ EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
+ EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
+ EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+ EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
+ EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 3ae8b56..618aefc 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1748,7 +1748,7 @@
virtual void SetUp() override {
mFakePolicy = new FakeInputReaderPolicy();
- mTestListener = new TestInputListener();
+ mTestListener = new TestInputListener(50ms);
mReader = new InputReader(std::make_shared<EventHub>(), mFakePolicy, mTestListener);
ASSERT_EQ(mReader->start(), OK);
@@ -1847,6 +1847,135 @@
ASSERT_LE(prevTimestamp, keyArgs.eventTime);
}
+// --- TouchProcessTest ---
+class TouchIntegrationTest : public InputReaderIntegrationTest {
+protected:
+ static const int32_t FIRST_SLOT = 0;
+ static const int32_t SECOND_SLOT = 1;
+ static const int32_t FIRST_TRACKING_ID = 0;
+ static const int32_t SECOND_TRACKING_ID = 1;
+ const std::string UNIQUE_ID = "local:0";
+
+ virtual void SetUp() override {
+ InputReaderIntegrationTest::SetUp();
+ // At least add an internal display.
+ setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+ DISPLAY_ORIENTATION_0, UNIQUE_ID, NO_PORT,
+ ViewportType::VIEWPORT_INTERNAL);
+
+ mDevice = createUinputDevice<UinputTouchScreen>(Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertInputDevicesChanged());
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyConfigurationChangedWasCalled());
+ }
+
+ void setDisplayInfoAndReconfigure(int32_t displayId, int32_t width, int32_t height,
+ int32_t orientation, const std::string& uniqueId,
+ std::optional<uint8_t> physicalPort,
+ ViewportType viewportType) {
+ mFakePolicy->addDisplayViewport(displayId, width, height, orientation, uniqueId,
+ physicalPort, viewportType);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
+ }
+
+ std::unique_ptr<UinputTouchScreen> mDevice;
+};
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessSingleTouch) {
+ NotifyMotionArgs args;
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // ACTION_DOWN
+ mDevice->sendDown(centerPoint);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+ // ACTION_MOVE
+ mDevice->sendMove(centerPoint + Point(1, 1));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+ // ACTION_UP
+ mDevice->sendUp();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessMultiTouch) {
+ NotifyMotionArgs args;
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // ACTION_DOWN
+ mDevice->sendDown(centerPoint);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+ // ACTION_POINTER_DOWN (Second slot)
+ const Point secondPoint = centerPoint + Point(100, 100);
+ mDevice->sendSlot(SECOND_SLOT);
+ mDevice->sendTrackingId(SECOND_TRACKING_ID);
+ mDevice->sendDown(secondPoint + Point(1, 1));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+
+ // ACTION_MOVE (Second slot)
+ mDevice->sendMove(secondPoint);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+ // ACTION_POINTER_UP (Second slot)
+ mDevice->sendUp();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+
+ // ACTION_UP
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendUp();
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
+}
+
+TEST_F(TouchIntegrationTest, InputEvent_ProcessPalm) {
+ NotifyMotionArgs args;
+ const Point centerPoint = mDevice->getCenterPoint();
+
+ // ACTION_DOWN
+ mDevice->sendDown(centerPoint);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
+
+ // ACTION_POINTER_DOWN (Second slot)
+ const Point secondPoint = centerPoint + Point(100, 100);
+ mDevice->sendSlot(SECOND_SLOT);
+ mDevice->sendTrackingId(SECOND_TRACKING_ID);
+ mDevice->sendDown(secondPoint);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ args.action);
+
+ // ACTION_MOVE (Second slot)
+ mDevice->sendMove(secondPoint + Point(1, 1));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
+
+ // Send MT_TOOL_PALM, which indicates that the touch IC has determined this to be a grip event.
+ // Expect to receive ACTION_CANCEL, to abort the entire gesture.
+ mDevice->sendToolType(MT_TOOL_PALM);
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(&args));
+ ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, args.action);
+
+ // ACTION_POINTER_UP (Second slot)
+ mDevice->sendUp();
+
+ // ACTION_UP
+ mDevice->sendSlot(FIRST_SLOT);
+ mDevice->sendUp();
+
+ // Expect no event received after abort the entire gesture.
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled());
+}
+
// --- InputDeviceTest ---
class InputDeviceTest : public testing::Test {
protected:
@@ -7032,5 +7161,4 @@
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
ASSERT_EQ(SECONDARY_DISPLAY_ID, motionArgs.displayId);
}
-
} // namespace android
diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp
index 06b05ac..86ff3b1 100644
--- a/services/inputflinger/tests/TestInputListener.cpp
+++ b/services/inputflinger/tests/TestInputListener.cpp
@@ -19,20 +19,11 @@
#include "TestInputListener.h"
-namespace {
-
-using std::chrono_literals::operator""ms;
-
-// Timeout for waiting for an expected event
-static constexpr std::chrono::duration WAIT_TIMEOUT = 5ms;
-
-} // namespace
-
namespace android {
// --- TestInputListener ---
-TestInputListener::TestInputListener() { }
+TestInputListener::TestInputListener(const std::chrono::milliseconds timeout) : mTimeout(timeout) {}
TestInputListener::~TestInputListener() { }
@@ -95,9 +86,9 @@
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
if (queue.empty()) {
- const bool eventReceived =
- mCondition.wait_for(lock, WAIT_TIMEOUT,
- [&queue]() REQUIRES(mLock) { return !queue.empty(); });
+ const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
+ return !queue.empty();
+ });
if (!eventReceived) {
FAIL() << "Timed out waiting for event: " << message.c_str();
}
@@ -114,7 +105,7 @@
base::ScopedLockAssertion assumeLocked(mLock);
std::vector<NotifyArgsType>& queue = std::get<std::vector<NotifyArgsType>>(mQueues);
- const bool eventReceived = mCondition.wait_for(lock, WAIT_TIMEOUT, [&queue]() REQUIRES(mLock) {
+ const bool eventReceived = mCondition.wait_for(lock, mTimeout, [&queue]() REQUIRES(mLock) {
return !queue.empty();
});
if (eventReceived) {
diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h
index 945e2ea..4262f5a 100644
--- a/services/inputflinger/tests/TestInputListener.h
+++ b/services/inputflinger/tests/TestInputListener.h
@@ -21,6 +21,8 @@
#include <gtest/gtest.h>
#include "InputListener.h"
+using std::chrono_literals::operator""ms;
+
namespace android {
// --- TestInputListener ---
@@ -30,7 +32,7 @@
virtual ~TestInputListener();
public:
- TestInputListener();
+ TestInputListener(const std::chrono::milliseconds timeout = 5ms);
void assertNotifyConfigurationChangedWasCalled(
NotifyConfigurationChangedArgs* outEventArgs = nullptr);
@@ -73,6 +75,7 @@
std::mutex mLock;
std::condition_variable mCondition;
+ const std::chrono::milliseconds mTimeout;
std::tuple<std::vector<NotifyConfigurationChangedArgs>, //
std::vector<NotifyDeviceResetArgs>, //
diff --git a/services/inputflinger/tests/UinputDevice.cpp b/services/inputflinger/tests/UinputDevice.cpp
index 2775d21..99480b7 100644
--- a/services/inputflinger/tests/UinputDevice.cpp
+++ b/services/inputflinger/tests/UinputDevice.cpp
@@ -119,7 +119,7 @@
EXPECT_NO_FATAL_FAILURE(releaseKey(key));
}
-// --- UinputHomeKey---
+// --- UinputHomeKey ---
UinputHomeKey::UinputHomeKey() : UinputKeyboard({KEY_HOME}) {}
@@ -127,4 +127,71 @@
EXPECT_NO_FATAL_FAILURE(pressAndReleaseKey(KEY_HOME));
}
+// --- UinputTouchScreen ---
+UinputTouchScreen::UinputTouchScreen(const Rect* size)
+ : UinputDevice(UinputTouchScreen::DEVICE_NAME), mSize(*size) {}
+
+void UinputTouchScreen::configureDevice(int fd, uinput_user_dev* device) {
+ // Setup the touch screen device
+ ioctl(fd, UI_SET_EVBIT, EV_KEY);
+ ioctl(fd, UI_SET_EVBIT, EV_REL);
+ ioctl(fd, UI_SET_EVBIT, EV_ABS);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_SLOT);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_X);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID);
+ ioctl(fd, UI_SET_ABSBIT, ABS_MT_TOOL_TYPE);
+ ioctl(fd, UI_SET_PROPBIT, INPUT_PROP_DIRECT);
+ ioctl(fd, UI_SET_KEYBIT, BTN_TOUCH);
+
+ device->absmin[ABS_MT_SLOT] = RAW_SLOT_MIN;
+ device->absmax[ABS_MT_SLOT] = RAW_SLOT_MAX;
+ device->absmin[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MIN;
+ device->absmax[ABS_MT_TOUCH_MAJOR] = RAW_TOUCH_MAX;
+ device->absmin[ABS_MT_POSITION_X] = mSize.left;
+ device->absmax[ABS_MT_POSITION_X] = mSize.right - 1;
+ device->absmin[ABS_MT_POSITION_Y] = mSize.top;
+ device->absmax[ABS_MT_POSITION_Y] = mSize.bottom - 1;
+ device->absmin[ABS_MT_TRACKING_ID] = RAW_ID_MIN;
+ device->absmax[ABS_MT_TRACKING_ID] = RAW_ID_MAX;
+}
+
+void UinputTouchScreen::sendSlot(int32_t slot) {
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_SLOT, slot));
+}
+
+void UinputTouchScreen::sendTrackingId(int32_t trackingId) {
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TRACKING_ID, trackingId));
+}
+
+void UinputTouchScreen::sendDown(const Point& point) {
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 1));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendMove(const Point& point) {
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_X, point.x));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_POSITION_Y, point.y));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendUp() {
+ sendTrackingId(0xffffffff);
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_KEY, BTN_TOUCH, 0));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+void UinputTouchScreen::sendToolType(int32_t toolType) {
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_ABS, ABS_MT_TOOL_TYPE, toolType));
+ EXPECT_NO_FATAL_FAILURE(injectEvent(EV_SYN, SYN_REPORT, 0));
+}
+
+// Get the center x, y base on the range definition.
+const Point UinputTouchScreen::getCenterPoint() {
+ return Point(mSize.left + mSize.width() / 2, mSize.top + mSize.height() / 2);
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/UinputDevice.h b/services/inputflinger/tests/UinputDevice.h
index 57d9011..ec3cd9f 100644
--- a/services/inputflinger/tests/UinputDevice.h
+++ b/services/inputflinger/tests/UinputDevice.h
@@ -22,6 +22,8 @@
#include <inttypes.h>
#include <linux/uinput.h>
#include <log/log.h>
+#include <ui/Point.h>
+#include <ui/Rect.h>
#include <memory>
@@ -106,6 +108,40 @@
UinputHomeKey();
};
+// --- UinputTouchScreen ---
+// A touch screen device with specific size.
+class UinputTouchScreen : public UinputDevice {
+public:
+ static constexpr const char* DEVICE_NAME = "Test Touch Screen";
+ static const int32_t RAW_TOUCH_MIN = 0;
+ static const int32_t RAW_TOUCH_MAX = 31;
+ static const int32_t RAW_ID_MIN = 0;
+ static const int32_t RAW_ID_MAX = 9;
+ static const int32_t RAW_SLOT_MIN = 0;
+ static const int32_t RAW_SLOT_MAX = 9;
+ static const int32_t RAW_PRESSURE_MIN = 0;
+ static const int32_t RAW_PRESSURE_MAX = 255;
+
+ template <class D, class... Ts>
+ friend std::unique_ptr<D> createUinputDevice(Ts... args);
+
+ void sendSlot(int32_t slot);
+ void sendTrackingId(int32_t trackingId);
+ void sendDown(const Point& point);
+ void sendMove(const Point& point);
+ void sendUp();
+ void sendToolType(int32_t toolType);
+
+ const Point getCenterPoint();
+
+protected:
+ UinputTouchScreen(const Rect* size);
+
+private:
+ void configureDevice(int fd, uinput_user_dev* device) override;
+ const Rect mSize;
+};
+
} // namespace android
#endif // _UI_TEST_INPUT_UINPUT_INJECTOR_H
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index 3e0f136..b0d3e3b 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -9,7 +9,7 @@
],
aidl: {
- local_include_dirs: ["."],
+ local_include_dirs: ["include"],
include_dirs: [
"frameworks/base/core/java/android/os",
],
@@ -28,6 +28,11 @@
"-Wunused",
"-Wunreachable-code",
],
+
+ local_include_dirs: ["include"],
+ export_include_dirs: [
+ "include",
+ ],
}
cc_test {
diff --git a/include/android/CoolingDevice.h b/services/powermanager/include/android/CoolingDevice.h
similarity index 100%
rename from include/android/CoolingDevice.h
rename to services/powermanager/include/android/CoolingDevice.h
diff --git a/include/android/Temperature.h b/services/powermanager/include/android/Temperature.h
similarity index 100%
rename from include/android/Temperature.h
rename to services/powermanager/include/android/Temperature.h
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 08d7d3f..c6f1f7e 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -52,6 +52,7 @@
"libpdx_default_transport",
"libprocessgroup",
"libprotobuf-cpp-lite",
+ "libstatslog",
"libsync",
"libtimestats",
"libui",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index d7ec868..e4d754c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -485,6 +485,10 @@
}
}
+ if (recomputeVisibleRegions == true) {
+ maybeDirtyInput();
+ }
+
return true;
}
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 5e04d95..e50a909 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -443,10 +443,7 @@
}
void BufferLayerConsumer::onDisconnect() {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->onDisconnect();
- }
+ mLayer->onDisconnect();
}
void BufferLayerConsumer::onSidebandStreamChanged() {
@@ -480,10 +477,7 @@
void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps,
FrameEventHistoryDelta* outDelta) {
- sp<Layer> l = mLayer.promote();
- if (l.get()) {
- l->addAndGetFrameTimestamps(newTimestamps, outDelta);
- }
+ mLayer->addAndGetFrameTimestamps(newTimestamps, outDelta);
}
void BufferLayerConsumer::abandonLocked() {
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index 39ed370..c71a1d9 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -332,7 +332,7 @@
const uint32_t mTexName;
// The layer for this BufferLayerConsumer
- const wp<Layer> mLayer;
+ Layer* mLayer;
wp<ContentsChangedListener> mContentsChangedListener;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index f5a99ca..4e5c593 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -346,8 +346,6 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer =
mConsumer->getCurrentBuffer(&mBufferInfo.mBufferSlot, &mBufferInfo.mFence);
- auto* layerCompositionState = editCompositionState();
- layerCompositionState->buffer = mBufferInfo.mBuffer;
if (mBufferInfo.mBuffer == nullptr) {
// this can only happen if the very first buffer was rejected.
@@ -441,7 +439,7 @@
mQueueItemCondition.broadcast();
}
- mFlinger->mInterceptor->saveBufferUpdate(this, item.mGraphicBuffer->getWidth(),
+ mFlinger->mInterceptor->saveBufferUpdate(layerId, item.mGraphicBuffer->getWidth(),
item.mGraphicBuffer->getHeight(), item.mFrameNumber);
mFlinger->signalLayerUpdate();
@@ -564,8 +562,6 @@
mBufferInfo.mApi = mConsumer->getCurrentApi();
mBufferInfo.mPixelFormat = mFormat;
mBufferInfo.mTransformToDisplayInverse = mConsumer->getTransformToDisplayInverse();
- float latchedFrameRate;
- mConsumer->getFrameRate(&latchedFrameRate);
}
sp<Layer> BufferQueueLayer::createClone() {
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index de5429b..3ed6889 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -616,7 +616,6 @@
mPreviousBufferId = getCurrentBufferId();
mBufferInfo.mBuffer = s.buffer;
mBufferInfo.mFence = s.acquireFence;
- editCompositionState()->buffer = mBufferInfo.mBuffer;
return NO_ERROR;
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 2792290..fda451b 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -104,6 +104,7 @@
static_libs: [
"libcompositionengine",
"libcompositionengine_mocks",
+ "libgui_mocks",
"librenderengine_mocks",
"libgmock",
"libgtest",
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
index ced4227..6bc677d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplayCreationArgs.h
@@ -18,6 +18,11 @@
#include <cstdint>
#include <optional>
+#include <string>
+
+#include <ui/DisplayInfo.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
@@ -30,47 +35,86 @@
* A parameter object for creating Display instances
*/
struct DisplayCreationArgs {
- // True if this display is a virtual display
- bool isVirtual = false;
+ struct Physical {
+ DisplayId id;
+ DisplayConnectionType type;
+ };
- // Identifies the display to the HWC, if composition is supported by it
- std::optional<DisplayId> displayId;
+ // Required for physical displays. Gives the HWC display id for the existing
+ // display along with the connection type.
+ std::optional<Physical> physical;
+
+ // Size of the display in pixels
+ ui::Size pixels = ui::Size::INVALID;
+
+ // Pixel format of the display
+ ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+
+ // True if virtual displays should be created with the HWC API if possible
+ bool useHwcVirtualDisplays = false;
+
+ // True if this display should be considered secure
+ bool isSecure = false;
+
+ // Gives the initial layer stack id to be used for the display
+ uint32_t layerStackId = ~0u;
// Optional pointer to the power advisor interface, if one is needed for
// this display.
Hwc2::PowerAdvisor* powerAdvisor = nullptr;
+
+ // Debugging. Human readable name for the display.
+ std::string name;
};
/**
* A helper for setting up a DisplayCreationArgs value in-line.
* Prefer this builder over raw structure initialization.
- *
- * Instead of:
- *
- * DisplayCreationArgs{false, false, displayId}
- *
- * Prefer:
- *
- * DisplayCreationArgsBuilder().setIsVirtual(false)
- * .setDisplayId(displayId).build();
*/
class DisplayCreationArgsBuilder {
public:
DisplayCreationArgs build() { return std::move(mArgs); }
- DisplayCreationArgsBuilder& setIsVirtual(bool isVirtual) {
- mArgs.isVirtual = isVirtual;
+ DisplayCreationArgsBuilder& setPhysical(DisplayCreationArgs::Physical physical) {
+ mArgs.physical = physical;
return *this;
}
- DisplayCreationArgsBuilder& setDisplayId(std::optional<DisplayId> displayId) {
- mArgs.displayId = displayId;
+
+ DisplayCreationArgsBuilder& setPixels(ui::Size pixels) {
+ mArgs.pixels = pixels;
return *this;
}
+
+ DisplayCreationArgsBuilder& setPixelFormat(ui::PixelFormat pixelFormat) {
+ mArgs.pixelFormat = pixelFormat;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setUseHwcVirtualDisplays(bool useHwcVirtualDisplays) {
+ mArgs.useHwcVirtualDisplays = useHwcVirtualDisplays;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setIsSecure(bool isSecure) {
+ mArgs.isSecure = isSecure;
+ return *this;
+ }
+
+ DisplayCreationArgsBuilder& setLayerStackId(uint32_t layerStackId) {
+ mArgs.layerStackId = layerStackId;
+ return *this;
+ }
+
DisplayCreationArgsBuilder& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) {
mArgs.powerAdvisor = powerAdvisor;
return *this;
}
+ DisplayCreationArgsBuilder& setName(std::string name) {
+ mArgs.name = std::move(name);
+ return *this;
+ }
+
private:
DisplayCreationArgs mArgs;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index fb597ce..9ca7d2f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -16,12 +16,16 @@
#pragma once
-#include <compositionengine/Display.h>
-#include <compositionengine/DisplayCreationArgs.h>
-#include <compositionengine/impl/Output.h>
-
#include <memory>
+#include <compositionengine/Display.h>
+#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayCreationArgs.h>
+#include <compositionengine/RenderSurface.h>
+#include <compositionengine/impl/Output.h>
+#include <ui/PixelFormat.h>
+#include <ui/Size.h>
+
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/PowerAdvisor.h"
@@ -36,11 +40,11 @@
// actually contain the final display state.
class Display : public compositionengine::impl::Output, public virtual compositionengine::Display {
public:
- explicit Display(const compositionengine::DisplayCreationArgs&);
virtual ~Display();
// compositionengine::Output overrides
std::optional<DisplayId> getDisplayId() const override;
+ bool isValid() const override;
void dump(std::string&) const override;
using compositionengine::impl::Output::setReleasedLayers;
void setReleasedLayers(const CompositionRefreshArgs&) override;
@@ -73,22 +77,33 @@
virtual void applyLayerRequestsToLayers(const LayerRequests&);
// Internal
+ virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
+ virtual std::optional<DisplayId> maybeAllocateDisplayIdForVirtualDisplay(ui::Size,
+ ui::PixelFormat) const;
std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const;
+ // Testing
+ void setDisplayIdForTesting(std::optional<DisplayId> displayId);
+
private:
- const bool mIsVirtual;
+ bool mIsVirtual = false;
std::optional<DisplayId> mId;
- Hwc2::PowerAdvisor* const mPowerAdvisor{nullptr};
+ Hwc2::PowerAdvisor* mPowerAdvisor = nullptr;
};
// This template factory function standardizes the implementation details of the
// final class using the types actually required by the implementation. This is
// not possible to do in the base class as those types may not even be visible
// to the base code.
-template <typename BaseDisplay, typename CompositionEngine, typename DisplayCreationArgs>
-std::shared_ptr<BaseDisplay> createDisplayTemplated(const CompositionEngine& compositionEngine,
- const DisplayCreationArgs& args) {
- return createOutputTemplated<BaseDisplay>(compositionEngine, args);
+template <typename BaseDisplay, typename CompositionEngine>
+std::shared_ptr<BaseDisplay> createDisplayTemplated(
+ const CompositionEngine& compositionEngine,
+ const compositionengine::DisplayCreationArgs& args) {
+ auto display = createOutputTemplated<BaseDisplay>(compositionEngine);
+
+ display->setConfiguration(args);
+
+ return display;
}
std::shared_ptr<Display> createDisplay(const compositionengine::CompositionEngine&,
diff --git a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
index acaaf4e..2d9f01b 100644
--- a/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/ClientCompositionRequestCache.cpp
@@ -35,7 +35,8 @@
return lhs.geometry == rhs.geometry && lhs.alpha == rhs.alpha &&
lhs.sourceDataspace == rhs.sourceDataspace &&
lhs.colorTransform == rhs.colorTransform &&
- lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow &&
+ lhs.backgroundBlurRadius == rhs.backgroundBlurRadius;
}
inline bool equalIgnoringBuffer(const renderengine::Buffer& lhs, const renderengine::Buffer& rhs) {
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 1d8a23f..c345405 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -47,11 +47,36 @@
return createDisplayTemplated<Display>(compositionEngine, args);
}
-Display::Display(const DisplayCreationArgs& args)
- : mIsVirtual(args.isVirtual), mId(args.displayId), mPowerAdvisor(args.powerAdvisor) {}
-
Display::~Display() = default;
+void Display::setConfiguration(const compositionengine::DisplayCreationArgs& args) {
+ mIsVirtual = !args.physical;
+ mId = args.physical ? std::make_optional(args.physical->id) : std::nullopt;
+ mPowerAdvisor = args.powerAdvisor;
+
+ editState().isSecure = args.isSecure;
+
+ setLayerStackFilter(args.layerStackId,
+ args.physical ? args.physical->type == DisplayConnectionType::Internal
+ : false);
+ setName(args.name);
+
+ if (!args.physical && args.useHwcVirtualDisplays) {
+ mId = maybeAllocateDisplayIdForVirtualDisplay(args.pixels, args.pixelFormat);
+ }
+}
+
+std::optional<DisplayId> Display::maybeAllocateDisplayIdForVirtualDisplay(
+ ui::Size pixels, ui::PixelFormat pixelFormat) const {
+ auto& hwc = getCompositionEngine().getHwComposer();
+ return hwc.allocateVirtualDisplay(static_cast<uint32_t>(pixels.width),
+ static_cast<uint32_t>(pixels.height), &pixelFormat);
+}
+
+bool Display::isValid() const {
+ return Output::isValid() && mPowerAdvisor;
+}
+
const std::optional<DisplayId>& Display::getId() const {
return mId;
}
@@ -68,6 +93,10 @@
return mId;
}
+void Display::setDisplayIdForTesting(std::optional<DisplayId> displayId) {
+ mId = displayId;
+}
+
void Display::disconnect() {
if (!mId) {
return;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 16f7a4e..88f2686 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -30,6 +30,8 @@
#include <compositionengine/mock/OutputLayer.h>
#include <compositionengine/mock/RenderSurface.h>
#include <gtest/gtest.h>
+#include <ui/DisplayInfo.h>
+#include <ui/Rect.h>
#include "MockHWC2.h"
#include "MockHWComposer.h"
@@ -40,8 +42,11 @@
using testing::_;
using testing::DoAll;
+using testing::Eq;
using testing::InSequence;
using testing::NiceMock;
+using testing::Pointee;
+using testing::Ref;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
@@ -49,8 +54,10 @@
using testing::StrictMock;
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr DisplayId VIRTUAL_DISPLAY_ID = DisplayId{43};
constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
+constexpr int32_t DEFAULT_LAYER_STACK = 123;
struct Layer {
Layer() {
@@ -73,25 +80,126 @@
StrictMock<mock::OutputLayer>* outputLayer = new StrictMock<mock::OutputLayer>();
};
-struct DisplayTest : public testing::Test {
- class Display : public impl::Display {
+struct DisplayTestCommon : public testing::Test {
+ // Uses the full implementation of a display
+ class FullImplDisplay : public impl::Display {
public:
- explicit Display(const compositionengine::DisplayCreationArgs& args)
- : impl::Display(args) {}
-
using impl::Display::injectOutputLayerForTest;
virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+
+ using impl::Display::maybeAllocateDisplayIdForVirtualDisplay;
};
+ // Uses a special implementation with key internal member functions set up
+ // as mock implementations, to allow for easier testing.
+ struct PartialMockDisplay : public impl::Display {
+ PartialMockDisplay(const compositionengine::CompositionEngine& compositionEngine)
+ : mCompositionEngine(compositionEngine) {}
+
+ // compositionengine::Output overrides
+ const OutputCompositionState& getState() const override { return mState; }
+ OutputCompositionState& editState() override { return mState; }
+
+ // compositionengine::impl::Output overrides
+ const CompositionEngine& getCompositionEngine() const override {
+ return mCompositionEngine;
+ };
+
+ // Mock implementation overrides
+ MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
+ compositionengine::OutputLayer*(size_t));
+ MOCK_METHOD2(ensureOutputLayer,
+ compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ MOCK_METHOD0(clearOutputLayers, void());
+ MOCK_CONST_METHOD1(dumpState, void(std::string&));
+ MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
+ MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
+ MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
+ MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
+ MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
+ MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
+ MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
+
+ const compositionengine::CompositionEngine& mCompositionEngine;
+ impl::OutputCompositionState mState;
+ };
+
+ static std::string getDisplayNameFromCurrentTest() {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+ return std::string("display for ") + test_info->test_case_name() + "." + test_info->name();
+ }
+
+ template <typename Display>
static std::shared_ptr<Display> createDisplay(
const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::DisplayCreationArgs&& args) {
+ compositionengine::DisplayCreationArgs args) {
+ args.name = getDisplayNameFromCurrentTest();
return impl::createDisplayTemplated<Display>(compositionEngine, args);
}
- DisplayTest() {
- EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+ template <typename Display>
+ static std::shared_ptr<StrictMock<Display>> createPartialMockDisplay(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::DisplayCreationArgs args) {
+ args.name = getDisplayNameFromCurrentTest();
+ auto display = std::make_shared<StrictMock<Display>>(compositionEngine);
+ display->setConfiguration(args);
+
+ return display;
+ }
+
+ DisplayTestCommon() {
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+ }
+
+ DisplayCreationArgs getDisplayCreationArgsForPhysicalHWCDisplay() {
+ return DisplayCreationArgsBuilder()
+ .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(true)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build();
+ }
+
+ DisplayCreationArgs getDisplayCreationArgsForNonHWCVirtualDisplay() {
+ return DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build();
+ }
+
+ StrictMock<android::mock::HWComposer> mHwComposer;
+ StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ StrictMock<mock::CompositionEngine> mCompositionEngine;
+ sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
+};
+
+struct PartialMockDisplayTestCommon : public DisplayTestCommon {
+ using Display = DisplayTestCommon::PartialMockDisplay;
+ std::shared_ptr<Display> mDisplay =
+ createPartialMockDisplay<Display>(mCompositionEngine,
+ getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct FullDisplayImplTestCommon : public DisplayTestCommon {
+ using Display = DisplayTestCommon::FullImplDisplay;
+ std::shared_ptr<Display> mDisplay =
+ createDisplay<Display>(mCompositionEngine,
+ getDisplayCreationArgsForPhysicalHWCDisplay());
+};
+
+struct DisplayWithLayersTestCommon : public FullDisplayImplTestCommon {
+ DisplayWithLayersTestCommon() {
mDisplay->injectOutputLayerForTest(
std::unique_ptr<compositionengine::OutputLayer>(mLayer1.outputLayer));
mDisplay->injectOutputLayerForTest(
@@ -100,65 +208,166 @@
std::unique_ptr<compositionengine::OutputLayer>(mLayer3.outputLayer));
}
- StrictMock<android::mock::HWComposer> mHwComposer;
- StrictMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
- StrictMock<mock::CompositionEngine> mCompositionEngine;
- sp<mock::NativeWindow> mNativeWindow = new StrictMock<mock::NativeWindow>();
Layer mLayer1;
Layer mLayer2;
LayerNoHWC2Layer mLayer3;
StrictMock<HWC2::mock::Layer> hwc2LayerUnknown;
-
- std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setDisplayId(DEFAULT_DISPLAY_ID)
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
+ std::shared_ptr<Display> mDisplay =
+ createDisplay<Display>(mCompositionEngine,
+ getDisplayCreationArgsForPhysicalHWCDisplay());
};
/*
* Basic construction
*/
-TEST_F(DisplayTest, canInstantiateDisplay) {
- {
- constexpr DisplayId display1 = DisplayId{123u};
- auto display =
- impl::createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder().setDisplayId(display1).build());
- EXPECT_FALSE(display->isSecure());
- EXPECT_FALSE(display->isVirtual());
- EXPECT_EQ(display1, display->getId());
- }
+struct DisplayCreationTest : public DisplayTestCommon {
+ using Display = DisplayTestCommon::FullImplDisplay;
+};
- {
- constexpr DisplayId display2 = DisplayId{546u};
- auto display =
- impl::createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder().setDisplayId(display2).build());
- EXPECT_FALSE(display->isSecure());
- EXPECT_FALSE(display->isVirtual());
- EXPECT_EQ(display2, display->getId());
- }
+TEST_F(DisplayCreationTest, createPhysicalInternalDisplay) {
+ auto display =
+ impl::createDisplay(mCompositionEngine, getDisplayCreationArgsForPhysicalHWCDisplay());
+ EXPECT_TRUE(display->isSecure());
+ EXPECT_FALSE(display->isVirtual());
+ EXPECT_EQ(DEFAULT_DISPLAY_ID, display->getId());
+}
- {
- constexpr DisplayId display3 = DisplayId{789u};
- auto display = impl::createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setIsVirtual(true)
- .setDisplayId(display3)
- .build());
- EXPECT_FALSE(display->isSecure());
- EXPECT_TRUE(display->isVirtual());
- EXPECT_EQ(display3, display->getId());
- }
+TEST_F(DisplayCreationTest, createNonHwcVirtualDisplay) {
+ auto display = impl::createDisplay(mCompositionEngine,
+ getDisplayCreationArgsForNonHWCVirtualDisplay());
+ EXPECT_FALSE(display->isSecure());
+ EXPECT_TRUE(display->isVirtual());
+ EXPECT_EQ(std::nullopt, display->getId());
+}
+
+/*
+ * Display::setConfiguration()
+ */
+
+using DisplaySetConfigurationTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetConfigurationTest, configuresInternalSecurePhysicalDisplay) {
+ mDisplay->setConfiguration(
+ DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(true)
+ .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(true)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
+
+ EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+ EXPECT_TRUE(mDisplay->isSecure());
+ EXPECT_FALSE(mDisplay->isVirtual());
+ EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+ EXPECT_TRUE(mDisplay->getState().layerStackInternal);
+ EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresExternalInsecurePhysicalDisplay) {
+ mDisplay->setConfiguration(
+ DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(true)
+ .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::External})
+ .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
+
+ EXPECT_EQ(DEFAULT_DISPLAY_ID, mDisplay->getId());
+ EXPECT_FALSE(mDisplay->isSecure());
+ EXPECT_FALSE(mDisplay->isVirtual());
+ EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+ EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+ EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresHwcBackedVirtualDisplay) {
+ EXPECT_CALL(mHwComposer,
+ allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+ Pointee(Eq(static_cast<ui::PixelFormat>(
+ PIXEL_FORMAT_RGBA_8888)))))
+ .WillOnce(Return(VIRTUAL_DISPLAY_ID));
+
+ mDisplay->setConfiguration(
+ DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(true)
+ .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
+
+ EXPECT_EQ(VIRTUAL_DISPLAY_ID, mDisplay->getId());
+ EXPECT_FALSE(mDisplay->isSecure());
+ EXPECT_TRUE(mDisplay->isVirtual());
+ EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+ EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+ EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfHwcAllocationFails) {
+ EXPECT_CALL(mHwComposer,
+ allocateVirtualDisplay(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH,
+ Pointee(Eq(static_cast<ui::PixelFormat>(
+ PIXEL_FORMAT_RGBA_8888)))))
+ .WillOnce(Return(std::nullopt));
+
+ mDisplay->setConfiguration(
+ DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(true)
+ .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
+
+ EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_FALSE(mDisplay->isSecure());
+ EXPECT_TRUE(mDisplay->isVirtual());
+ EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+ EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+ EXPECT_FALSE(mDisplay->isValid());
+}
+
+TEST_F(DisplaySetConfigurationTest, configuresNonHwcBackedVirtualDisplayIfShouldNotUseHwc) {
+ mDisplay->setConfiguration(
+ DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels(ui::Size(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_WIDTH))
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(false)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .setName(getDisplayNameFromCurrentTest())
+ .build());
+
+ EXPECT_EQ(std::nullopt, mDisplay->getId());
+ EXPECT_FALSE(mDisplay->isSecure());
+ EXPECT_TRUE(mDisplay->isVirtual());
+ EXPECT_EQ(DEFAULT_LAYER_STACK, mDisplay->getState().layerStackId);
+ EXPECT_FALSE(mDisplay->getState().layerStackInternal);
+ EXPECT_FALSE(mDisplay->isValid());
}
/*
* Display::disconnect()
*/
-TEST_F(DisplayTest, disconnectDisconnectsDisplay) {
+using DisplayDisconnectTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayDisconnectTest, disconnectsDisplay) {
// The first call to disconnect will disconnect the display with the HWC and
// set mHwcId to -1.
EXPECT_CALL(mHwComposer, disconnectDisplay(DEFAULT_DISPLAY_ID)).Times(1);
@@ -175,7 +384,9 @@
* Display::setColorTransform()
*/
-TEST_F(DisplayTest, setColorTransformSetsTransform) {
+using DisplaySetColorTransformTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetColorTransformTest, setsTransform) {
// No change does nothing
CompositionRefreshArgs refreshArgs;
refreshArgs.colorTransformMatrix = std::nullopt;
@@ -202,7 +413,9 @@
* Display::setColorMode()
*/
-TEST_F(DisplayTest, setColorModeSetsModeUnlessNoChange) {
+using DisplaySetColorModeTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplaySetColorModeTest, setsModeUnlessNoChange) {
using ColorProfile = Output::ColorProfile;
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
@@ -245,11 +458,11 @@
EXPECT_EQ(ui::Dataspace::UNKNOWN, mDisplay->getState().targetDataspace);
}
-TEST_F(DisplayTest, setColorModeDoesNothingForVirtualDisplay) {
+TEST_F(DisplaySetColorModeTest, doesNothingForVirtualDisplay) {
using ColorProfile = Output::ColorProfile;
- std::shared_ptr<impl::Display> virtualDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgs{true, DEFAULT_DISPLAY_ID})};
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<impl::Display> virtualDisplay = impl::createDisplay(mCompositionEngine, args);
mock::DisplayColorProfile* colorProfile = new StrictMock<mock::DisplayColorProfile>();
virtualDisplay->setDisplayColorProfileForTest(
@@ -274,7 +487,9 @@
* Display::createDisplayColorProfile()
*/
-TEST_F(DisplayTest, createDisplayColorProfileSetsDisplayColorProfile) {
+using DisplayCreateColorProfileTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateColorProfileTest, setsDisplayColorProfile) {
EXPECT_TRUE(mDisplay->getDisplayColorProfile() == nullptr);
mDisplay->createDisplayColorProfile(
DisplayColorProfileCreationArgs{false, HdrCapabilities(), 0,
@@ -286,7 +501,9 @@
* Display::createRenderSurface()
*/
-TEST_F(DisplayTest, createRenderSurfaceSetsRenderSurface) {
+using DisplayCreateRenderSurfaceTest = PartialMockDisplayTestCommon;
+
+TEST_F(DisplayCreateRenderSurfaceTest, setsRenderSurface) {
EXPECT_CALL(*mNativeWindow, disconnect(NATIVE_WINDOW_API_EGL)).WillRepeatedly(Return(NO_ERROR));
EXPECT_TRUE(mDisplay->getRenderSurface() == nullptr);
mDisplay->createRenderSurface(RenderSurfaceCreationArgs{640, 480, mNativeWindow, nullptr});
@@ -297,7 +514,9 @@
* Display::createOutputLayer()
*/
-TEST_F(DisplayTest, createOutputLayerSetsHwcLayer) {
+using DisplayCreateOutputLayerTest = FullDisplayImplTestCommon;
+
+TEST_F(DisplayCreateOutputLayerTest, setsHwcLayer) {
sp<mock::LayerFE> layerFE = new StrictMock<mock::LayerFE>();
StrictMock<HWC2::mock::Layer> hwcLayer;
@@ -315,9 +534,11 @@
* Display::setReleasedLayers()
*/
-TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNotHwcDisplay) {
- std::shared_ptr<impl::Display> nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplaySetReleasedLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNotHwcDisplay) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
@@ -336,7 +557,7 @@
ASSERT_EQ(1, releasedLayers.size());
}
-TEST_F(DisplayTest, setReleasedLayersDoesNothingIfNoLayersWithQueuedFrames) {
+TEST_F(DisplaySetReleasedLayersTest, doesNothingIfNoLayersWithQueuedFrames) {
sp<mock::LayerFE> layerXLayerFE = new StrictMock<mock::LayerFE>();
{
@@ -352,7 +573,7 @@
ASSERT_EQ(1, releasedLayers.size());
}
-TEST_F(DisplayTest, setReleasedLayers) {
+TEST_F(DisplaySetReleasedLayersTest, setReleasedLayers) {
sp<mock::LayerFE> unknownLayer = new StrictMock<mock::LayerFE>();
CompositionRefreshArgs refreshArgs;
@@ -372,59 +593,12 @@
* Display::chooseCompositionStrategy()
*/
-struct DisplayChooseCompositionStrategyTest : public testing::Test {
- struct DisplayPartialMock : public impl::Display {
- DisplayPartialMock(const compositionengine::CompositionEngine& compositionEngine,
- const compositionengine::DisplayCreationArgs& args)
- : impl::Display(args), mCompositionEngine(compositionEngine) {}
-
- // Sets up the helper functions called by chooseCompositionStrategy to
- // use a mock implementations.
- MOCK_CONST_METHOD0(anyLayersRequireClientComposition, bool());
- MOCK_CONST_METHOD0(allLayersRequireClientComposition, bool());
- MOCK_METHOD1(applyChangedTypesToLayers, void(const impl::Display::ChangedTypes&));
- MOCK_METHOD1(applyDisplayRequests, void(const impl::Display::DisplayRequests&));
- MOCK_METHOD1(applyLayerRequestsToLayers, void(const impl::Display::LayerRequests&));
-
- // compositionengine::Output overrides
- const OutputCompositionState& getState() const override { return mState; }
- OutputCompositionState& editState() override { return mState; }
-
- // compositionengine::impl::Output overrides
- const CompositionEngine& getCompositionEngine() const override {
- return mCompositionEngine;
- };
-
- // These need implementations though are not expected to be called.
- MOCK_CONST_METHOD0(getOutputLayerCount, size_t());
- MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex,
- compositionengine::OutputLayer*(size_t));
- MOCK_METHOD2(ensureOutputLayer,
- compositionengine::OutputLayer*(std::optional<size_t>, const sp<LayerFE>&));
- MOCK_METHOD0(finalizePendingOutputLayers, void());
- MOCK_METHOD0(clearOutputLayers, void());
- MOCK_CONST_METHOD1(dumpState, void(std::string&));
- MOCK_METHOD1(injectOutputLayerForTest, compositionengine::OutputLayer*(const sp<LayerFE>&));
- MOCK_METHOD1(injectOutputLayerForTest, void(std::unique_ptr<OutputLayer>));
-
- const compositionengine::CompositionEngine& mCompositionEngine;
- impl::OutputCompositionState mState;
- };
-
- DisplayChooseCompositionStrategyTest() {
- EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
- }
-
- StrictMock<android::mock::HWComposer> mHwComposer;
- StrictMock<mock::CompositionEngine> mCompositionEngine;
- StrictMock<DisplayPartialMock>
- mDisplay{mCompositionEngine,
- DisplayCreationArgsBuilder().setDisplayId(DEFAULT_DISPLAY_ID).build()};
-};
+using DisplayChooseCompositionStrategyTest = PartialMockDisplayTestCommon;
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutIfNotAHwcDisplay) {
- std::shared_ptr<impl::Display> nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<Display> nonHwcDisplay =
+ createPartialMockDisplay<Display>(mCompositionEngine, args);
EXPECT_FALSE(nonHwcDisplay->getId());
nonHwcDisplay->chooseCompositionStrategy();
@@ -435,33 +609,36 @@
}
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
- EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, false, _))
.WillOnce(Return(INVALID_OPERATION));
- mDisplay.chooseCompositionStrategy();
+ mDisplay->chooseCompositionStrategy();
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_TRUE(state.usesClientComposition);
EXPECT_FALSE(state.usesDeviceComposition);
}
TEST_F(DisplayChooseCompositionStrategyTest, normalOperation) {
- // Since two calls are made to anyLayersRequireClientComposition with different return values,
- // use a Sequence to control the matching so the values are returned in a known order.
+ // Since two calls are made to anyLayersRequireClientComposition with different return
+ // values, use a Sequence to control the matching so the values are returned in a known
+ // order.
Sequence s;
- EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+ EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
.InSequence(s)
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
.WillOnce(Return(NO_ERROR));
- EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay.chooseCompositionStrategy();
+ mDisplay->chooseCompositionStrategy();
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
EXPECT_TRUE(state.usesDeviceComposition);
}
@@ -473,24 +650,27 @@
{{nullptr, HWC2::LayerRequest::ClearClientTarget}},
};
- // Since two calls are made to anyLayersRequireClientComposition with different return values,
- // use a Sequence to control the matching so the values are returned in a known order.
+ // Since two calls are made to anyLayersRequireClientComposition with different return
+ // values, use a Sequence to control the matching so the values are returned in a known
+ // order.
Sequence s;
- EXPECT_CALL(mDisplay, anyLayersRequireClientComposition()).InSequence(s).WillOnce(Return(true));
- EXPECT_CALL(mDisplay, anyLayersRequireClientComposition())
+ EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
+ .InSequence(s)
+ .WillOnce(Return(true));
+ EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition())
.InSequence(s)
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer, getDeviceCompositionChanges(DEFAULT_DISPLAY_ID, true, _))
.WillOnce(DoAll(SetArgPointee<2>(changes), Return(NO_ERROR)));
- EXPECT_CALL(mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
- EXPECT_CALL(mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
- EXPECT_CALL(mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
- EXPECT_CALL(mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
+ EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
+ EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
- mDisplay.chooseCompositionStrategy();
+ mDisplay->chooseCompositionStrategy();
- auto& state = mDisplay.getState();
+ auto& state = mDisplay->getState();
EXPECT_FALSE(state.usesClientComposition);
EXPECT_TRUE(state.usesDeviceComposition);
}
@@ -499,13 +679,15 @@
* Display::getSkipColorTransform()
*/
-TEST_F(DisplayTest, getSkipColorTransformDoesNothingIfNonHwcDisplay) {
- auto nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplayGetSkipColorTransformTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayGetSkipColorTransformTest, doesNothingIfNonHwcDisplay) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
EXPECT_FALSE(nonHwcDisplay->getSkipColorTransform());
}
-TEST_F(DisplayTest, getSkipColorTransformChecksHwcCapability) {
+TEST_F(DisplayGetSkipColorTransformTest, checksHwcCapability) {
EXPECT_CALL(mHwComposer,
hasDisplayCapability(std::make_optional(DEFAULT_DISPLAY_ID),
HWC2::DisplayCapability::SkipClientColorTransform))
@@ -517,7 +699,9 @@
* Display::anyLayersRequireClientComposition()
*/
-TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsFalse) {
+using DisplayAnyLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsFalse) {
EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -525,7 +709,7 @@
EXPECT_FALSE(mDisplay->anyLayersRequireClientComposition());
}
-TEST_F(DisplayTest, anyLayersRequireClientCompositionReturnsTrue) {
+TEST_F(DisplayAnyLayersRequireClientCompositionTest, returnsTrue) {
EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(false));
EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
@@ -536,7 +720,9 @@
* Display::allLayersRequireClientComposition()
*/
-TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsTrue) {
+using DisplayAllLayersRequireClientCompositionTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsTrue) {
EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(true));
EXPECT_CALL(*mLayer3.outputLayer, requiresClientComposition()).WillOnce(Return(true));
@@ -544,7 +730,7 @@
EXPECT_TRUE(mDisplay->allLayersRequireClientComposition());
}
-TEST_F(DisplayTest, allLayersRequireClientCompositionReturnsFalse) {
+TEST_F(DisplayAllLayersRequireClientCompositionTest, returnsFalse) {
EXPECT_CALL(*mLayer1.outputLayer, requiresClientComposition()).WillOnce(Return(true));
EXPECT_CALL(*mLayer2.outputLayer, requiresClientComposition()).WillOnce(Return(false));
@@ -555,11 +741,13 @@
* Display::applyChangedTypesToLayers()
*/
-TEST_F(DisplayTest, applyChangedTypesToLayersTakesEarlyOutIfNoChangedLayers) {
+using DisplayApplyChangedTypesToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyChangedTypesToLayersTest, takesEarlyOutIfNoChangedLayers) {
mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes());
}
-TEST_F(DisplayTest, applyChangedTypesToLayersAppliesChanges) {
+TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
EXPECT_CALL(*mLayer1.outputLayer,
applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
.Times(1);
@@ -578,28 +766,30 @@
* Display::applyDisplayRequests()
*/
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesNoRequests) {
+using DisplayApplyDisplayRequestsTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyDisplayRequestsTest, handlesNoRequests) {
mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(0));
auto& state = mDisplay->getState();
EXPECT_FALSE(state.flipClientTarget);
}
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesFlipClientTarget) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesFlipClientTarget) {
mDisplay->applyDisplayRequests(HWC2::DisplayRequest::FlipClientTarget);
auto& state = mDisplay->getState();
EXPECT_TRUE(state.flipClientTarget);
}
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesWriteClientTargetToOutput) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesWriteClientTargetToOutput) {
mDisplay->applyDisplayRequests(HWC2::DisplayRequest::WriteClientTargetToOutput);
auto& state = mDisplay->getState();
EXPECT_FALSE(state.flipClientTarget);
}
-TEST_F(DisplayTest, applyDisplayRequestsToLayersHandlesAllRequestFlagsSet) {
+TEST_F(DisplayApplyDisplayRequestsTest, handlesAllRequestFlagsSet) {
mDisplay->applyDisplayRequests(static_cast<HWC2::DisplayRequest>(~0));
auto& state = mDisplay->getState();
@@ -610,7 +800,9 @@
* Display::applyLayerRequestsToLayers()
*/
-TEST_F(DisplayTest, applyLayerRequestsToLayersPreparesAllLayers) {
+using DisplayApplyLayerRequestsToLayersTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayApplyLayerRequestsToLayersTest, preparesAllLayers) {
EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
@@ -618,7 +810,7 @@
mDisplay->applyLayerRequestsToLayers(impl::Display::LayerRequests());
}
-TEST_F(DisplayTest, applyLayerRequestsToLayers2) {
+TEST_F(DisplayApplyLayerRequestsToLayersTest, appliesDeviceLayerRequests) {
EXPECT_CALL(*mLayer1.outputLayer, prepareForDeviceLayerRequests()).Times(1);
EXPECT_CALL(*mLayer2.outputLayer, prepareForDeviceLayerRequests()).Times(1);
EXPECT_CALL(*mLayer3.outputLayer, prepareForDeviceLayerRequests()).Times(1);
@@ -637,9 +829,11 @@
* Display::presentAndGetFrameFences()
*/
-TEST_F(DisplayTest, presentAndGetFrameFencesReturnsNoFencesOnNonHwcDisplay) {
- auto nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+using DisplayPresentAndGetFrameFencesTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsNoFencesOnNonHwcDisplay) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ auto nonHwcDisplay{impl::createDisplay(mCompositionEngine, args)};
auto result = nonHwcDisplay->presentAndGetFrameFences();
@@ -648,7 +842,7 @@
EXPECT_EQ(0u, result.layerFences.size());
}
-TEST_F(DisplayTest, presentAndGetFrameFencesReturnsPresentAndLayerFences) {
+TEST_F(DisplayPresentAndGetFrameFencesTest, returnsPresentAndLayerFences) {
sp<Fence> presentFence = new Fence();
sp<Fence> layer1Fence = new Fence();
sp<Fence> layer2Fence = new Fence();
@@ -676,7 +870,9 @@
* Display::setExpensiveRenderingExpected()
*/
-TEST_F(DisplayTest, setExpensiveRenderingExpectedForwardsToPowerAdvisor) {
+using DisplaySetExpensiveRenderingExpectedTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplaySetExpensiveRenderingExpectedTest, forwardsToPowerAdvisor) {
EXPECT_CALL(mPowerAdvisor, setExpensiveRenderingExpected(DEFAULT_DISPLAY_ID, true)).Times(1);
mDisplay->setExpensiveRenderingExpected(true);
@@ -688,7 +884,9 @@
* Display::finishFrame()
*/
-TEST_F(DisplayTest, finishFrameDoesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
+using DisplayFinishFrameTest = DisplayWithLayersTestCommon;
+
+TEST_F(DisplayFinishFrameTest, doesNotSkipCompositionIfNotDirtyOnHwcDisplay) {
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -709,9 +907,9 @@
mDisplay->finishFrame(refreshArgs);
}
-TEST_F(DisplayTest, finishFrameSkipsCompositionIfNotDirty) {
- std::shared_ptr<impl::Display> nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, skipsCompositionIfNotDirty) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -730,9 +928,9 @@
nonHwcDisplay->finishFrame(refreshArgs);
}
-TEST_F(DisplayTest, finishFramePerformsCompositionIfDirty) {
- std::shared_ptr<impl::Display> nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, performsCompositionIfDirty) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -751,9 +949,9 @@
nonHwcDisplay->finishFrame(refreshArgs);
}
-TEST_F(DisplayTest, finishFramePerformsCompositionIfRepaintEverything) {
- std::shared_ptr<impl::Display> nonHwcDisplay{
- impl::createDisplay(mCompositionEngine, DisplayCreationArgsBuilder().build())};
+TEST_F(DisplayFinishFrameTest, performsCompositionIfRepaintEverything) {
+ auto args = getDisplayCreationArgsForNonHWCVirtualDisplay();
+ std::shared_ptr<impl::Display> nonHwcDisplay = impl::createDisplay(mCompositionEngine, args);
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
nonHwcDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
@@ -779,19 +977,10 @@
struct DisplayFunctionalTest : public testing::Test {
class Display : public impl::Display {
public:
- explicit Display(const compositionengine::DisplayCreationArgs& args)
- : impl::Display(args) {}
-
using impl::Display::injectOutputLayerForTest;
virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
};
- static std::shared_ptr<Display> createDisplay(
- const compositionengine::CompositionEngine& compositionEngine,
- compositionengine::DisplayCreationArgs&& args) {
- return impl::createDisplayTemplated<Display>(compositionEngine, args);
- }
-
DisplayFunctionalTest() {
EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
@@ -803,11 +992,18 @@
NiceMock<mock::CompositionEngine> mCompositionEngine;
sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
- std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
- DisplayCreationArgsBuilder()
- .setDisplayId(DEFAULT_DISPLAY_ID)
- .setPowerAdvisor(&mPowerAdvisor)
- .build());
+ std::shared_ptr<Display> mDisplay = impl::createDisplayTemplated<
+ Display>(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPixelFormat(static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888))
+ .setIsSecure(true)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build()
+
+ );
impl::RenderSurface* mRenderSurface =
new impl::RenderSurface{mCompositionEngine, *mDisplay,
RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
index e740b13..b738096 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index cd6bbd1..b72214d 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -47,19 +47,17 @@
ui::Transform::RotationFlags DisplayDevice::sPrimaryDisplayRotationFlags = ui::Transform::ROT_0;
-DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(const sp<SurfaceFlinger>& flinger,
- const wp<IBinder>& displayToken,
- std::optional<DisplayId> displayId)
- : flinger(flinger), displayToken(displayToken), displayId(displayId) {}
+DisplayDeviceCreationArgs::DisplayDeviceCreationArgs(
+ const sp<SurfaceFlinger>& flinger, const wp<IBinder>& displayToken,
+ std::shared_ptr<compositionengine::Display> compositionDisplay)
+ : flinger(flinger), displayToken(displayToken), compositionDisplay(compositionDisplay) {}
-DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs&& args)
+DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args)
: mFlinger(args.flinger),
mDisplayToken(args.displayToken),
mSequenceId(args.sequenceId),
mConnectionType(args.connectionType),
- mCompositionDisplay{mFlinger->getCompositionEngine().createDisplay(
- compositionengine::DisplayCreationArgs{args.isVirtual(), args.displayId,
- args.powerAdvisor})},
+ mCompositionDisplay{args.compositionDisplay},
mPhysicalOrientation(args.physicalOrientation),
mIsPrimary(args.isPrimary) {
mCompositionDisplay->editState().isSecure = args.isSecure;
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index d970b82..fb6c817 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -64,7 +64,7 @@
constexpr static float sDefaultMinLumiance = 0.0;
constexpr static float sDefaultMaxLumiance = 500.0;
- explicit DisplayDevice(DisplayDeviceCreationArgs&& args);
+ explicit DisplayDevice(DisplayDeviceCreationArgs& args);
virtual ~DisplayDevice();
std::shared_ptr<compositionengine::Display> getCompositionDisplay() const {
@@ -211,13 +211,10 @@
// We use a constructor to ensure some of the values are set, without
// assuming a default value.
DisplayDeviceCreationArgs(const sp<SurfaceFlinger>&, const wp<IBinder>& displayToken,
- std::optional<DisplayId>);
-
- bool isVirtual() const { return !connectionType; }
-
+ std::shared_ptr<compositionengine::Display>);
const sp<SurfaceFlinger> flinger;
const wp<IBinder> displayToken;
- const std::optional<DisplayId> displayId;
+ const std::shared_ptr<compositionengine::Display> compositionDisplay;
int32_t sequenceId{0};
std::optional<DisplayConnectionType> connectionType;
@@ -231,7 +228,6 @@
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes;
int initialPowerMode{HWC_POWER_MODE_NORMAL};
bool isPrimary{false};
- Hwc2::PowerAdvisor* powerAdvisor{nullptr};
};
class DisplayRenderArea : public RenderArea {
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
index 9aaef65..f24f314 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp
@@ -104,9 +104,8 @@
return static_cast<uint16_t>(value >> 40);
}
-DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash) {
- return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(displayNameHash) << 8) |
- port};
+DisplayId DisplayId::fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash) {
+ return {(static_cast<Type>(manufacturerId) << 40) | (static_cast<Type>(modelHash) << 8) | port};
}
bool isEdid(const DisplayIdentificationData& data) {
@@ -209,23 +208,30 @@
view.remove_prefix(kDescriptorLength);
}
- if (displayName.empty()) {
+ std::string_view modelString = displayName;
+
+ if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to serial number due to missing display name.");
- displayName = serialNumber;
+ modelString = serialNumber;
}
- if (displayName.empty()) {
+ if (modelString.empty()) {
ALOGW("Invalid EDID: falling back to ASCII text due to missing serial number.");
- displayName = asciiText;
+ modelString = asciiText;
}
- if (displayName.empty()) {
+ if (modelString.empty()) {
ALOGE("Invalid EDID: display name and fallback descriptors are missing.");
return {};
}
+ // 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.
+ const auto modelHash = static_cast<uint32_t>(std::hash<std::string_view>()(modelString));
+
return Edid{.manufacturerId = manufacturerId,
- .pnpId = *pnpId,
- .displayName = displayName,
.productId = productId,
+ .pnpId = *pnpId,
+ .modelHash = modelHash,
+ .displayName = displayName,
.manufactureWeek = manufactureWeek,
.manufactureOrModelYear = manufactureOrModelYear};
}
@@ -253,10 +259,8 @@
return {};
}
- // Hash display name instead of using product code or serial number, since the latter have been
- // observed to change on some displays with multiple inputs.
- const auto hash = static_cast<uint32_t>(std::hash<std::string_view>()(edid->displayName));
- return DisplayIdentificationInfo{.id = DisplayId::fromEdid(port, edid->manufacturerId, hash),
+ const auto displayId = DisplayId::fromEdid(port, edid->manufacturerId, edid->modelHash);
+ return DisplayIdentificationInfo{.id = displayId,
.name = std::string(edid->displayName),
.deviceProductInfo = buildDeviceProductInfo(*edid)};
}
diff --git a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
index 0a18ba1..d91b957 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayIdentification.h
@@ -34,7 +34,7 @@
uint16_t manufacturerId() const;
- static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t displayNameHash);
+ static DisplayId fromEdid(uint8_t port, uint16_t manufacturerId, uint32_t modelHash);
};
inline bool operator==(DisplayId lhs, DisplayId rhs) {
@@ -61,6 +61,7 @@
uint16_t manufacturerId;
uint16_t productId;
PnpId pnpId;
+ uint32_t modelHash;
std::string_view displayName;
uint8_t manufactureOrModelYear;
uint8_t manufactureWeek;
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index 36544b6..4c3b3e5 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -57,9 +57,12 @@
*/
FramebufferSurface::FramebufferSurface(HWComposer& hwc, DisplayId displayId,
- const sp<IGraphicBufferConsumer>& consumer)
+ const sp<IGraphicBufferConsumer>& consumer,
+ uint32_t maxWidth, uint32_t maxHeight)
: ConsumerBase(consumer),
mDisplayId(displayId),
+ mMaxWidth(maxWidth),
+ mMaxHeight(maxHeight),
mCurrentBufferSlot(-1),
mCurrentBuffer(),
mCurrentFence(Fence::NO_FENCE),
@@ -75,14 +78,16 @@
GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER);
const auto& activeConfig = mHwc.getActiveConfig(displayId);
- mConsumer->setDefaultBufferSize(activeConfig->getWidth(),
- activeConfig->getHeight());
+ ui::Size limitedSize =
+ limitFramebufferSize(activeConfig->getWidth(), activeConfig->getHeight());
+ mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
mConsumer->setMaxAcquiredBufferCount(
SurfaceFlinger::maxFrameBufferAcquiredBuffers - 1);
}
-void FramebufferSurface::resizeBuffers(const uint32_t width, const uint32_t height) {
- mConsumer->setDefaultBufferSize(width, height);
+void FramebufferSurface::resizeBuffers(uint32_t width, uint32_t height) {
+ ui::Size limitedSize = limitFramebufferSize(width, height);
+ mConsumer->setDefaultBufferSize(limitedSize.width, limitedSize.height);
}
status_t FramebufferSurface::beginFrame(bool /*mustRecompose*/) {
@@ -177,6 +182,26 @@
}
}
+ui::Size FramebufferSurface::limitFramebufferSize(uint32_t width, uint32_t height) {
+ ui::Size framebufferSize(width, height);
+ bool wasLimited = true;
+ if (width > mMaxWidth && mMaxWidth != 0) {
+ float aspectRatio = float(width) / float(height);
+ framebufferSize.height = mMaxWidth / aspectRatio;
+ framebufferSize.width = mMaxWidth;
+ wasLimited = true;
+ }
+ if (height > mMaxHeight && mMaxHeight != 0) {
+ float aspectRatio = float(width) / float(height);
+ framebufferSize.height = mMaxHeight;
+ framebufferSize.width = mMaxHeight * aspectRatio;
+ wasLimited = true;
+ }
+ ALOGI_IF(wasLimited, "framebuffer size has been limited to [%dx%d] from [%dx%d]",
+ framebufferSize.width, framebufferSize.height, width, height);
+ return framebufferSize;
+}
+
void FramebufferSurface::dumpAsString(String8& result) const {
Mutex::Autolock lock(mMutex);
result.appendFormat(" FramebufferSurface: dataspace: %s(%d)\n",
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
index 7f451a5..a1859f3 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.h
@@ -23,6 +23,7 @@
#include <compositionengine/DisplaySurface.h>
#include <compositionengine/impl/HwcBufferCache.h>
#include <gui/ConsumerBase.h>
+#include <ui/Size.h>
#include "DisplayIdentification.h"
@@ -39,7 +40,8 @@
class FramebufferSurface : public ConsumerBase, public compositionengine::DisplaySurface {
public:
FramebufferSurface(HWComposer& hwc, DisplayId displayId,
- const sp<IGraphicBufferConsumer>& consumer);
+ const sp<IGraphicBufferConsumer>& consumer, uint32_t maxWidth,
+ uint32_t maxHeight);
virtual status_t beginFrame(bool mustRecompose);
virtual status_t prepareFrame(CompositionType compositionType);
@@ -47,7 +49,7 @@
virtual void onFrameCommitted();
virtual void dumpAsString(String8& result) const;
- virtual void resizeBuffers(const uint32_t width, const uint32_t height);
+ virtual void resizeBuffers(uint32_t width, uint32_t height);
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
@@ -58,6 +60,9 @@
virtual void dumpLocked(String8& result, const char* prefix) const;
+ // Limits the width and height by the maximum width specified in the constructor.
+ ui::Size limitFramebufferSize(uint32_t width, uint32_t height);
+
// nextBuffer waits for and then latches the next buffer from the
// BufferQueue and releases the previously latched buffer to the
// BufferQueue. The new buffer is returned in the 'buffer' argument.
@@ -66,6 +71,14 @@
const DisplayId mDisplayId;
+ // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+ // the device.
+ const uint32_t mMaxWidth;
+
+ // Framebuffer size has a dimension limitation in pixels based on the graphics capabilities of
+ // the device.
+ const uint32_t mMaxHeight;
+
// mCurrentBufferIndex is the slot index of the current buffer or
// INVALID_BUFFER_SLOT to indicate that either there is no current buffer
// or the buffer is not associated with a slot.
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
index 550ec61..1d8179c 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp
@@ -72,6 +72,10 @@
}
}
+void PowerAdvisor::onBootFinished() {
+ mBootFinished.store(true);
+}
+
void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) {
if (expected) {
mExpensiveDisplays.insert(displayId);
@@ -97,6 +101,12 @@
}
void PowerAdvisor::notifyDisplayUpdateImminent() {
+ // Only start sending this notification once the system has booted so we don't introduce an
+ // early-boot dependency on Power HAL
+ if (!mBootFinished.load()) {
+ return;
+ }
+
if (mSendUpdateImminent.load()) {
HalWrapper* const halWrapper = getPowerHal();
if (halWrapper == nullptr) {
@@ -124,24 +134,20 @@
static std::unique_ptr<HalWrapper> connect() {
// Power HAL 1.3 is not guaranteed to be available, thus we need to query
// Power HAL 1.0 first and try to cast it to Power HAL 1.3.
- // Power HAL 1.0 is always available, thus if we fail to query it, it means
- // Power HAL is not available temporarily and we should retry later. However,
- // if Power HAL 1.0 is available and we can't cast it to Power HAL 1.3,
- // it means Power HAL 1.3 is not available at all, so we should stop trying.
sp<V1_3::IPower> powerHal = nullptr;
- if (sHasPowerHal_1_3) {
- sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
- if (powerHal_1_0 != nullptr) {
- // Try to cast to Power HAL 1.3
- powerHal = V1_3::IPower::castFrom(powerHal_1_0);
- if (powerHal == nullptr) {
- ALOGW("No Power HAL 1.3 service in system");
- sHasPowerHal_1_3 = false;
- } else {
- ALOGI("Loaded Power HAL 1.3 service");
- }
+ sp<V1_0::IPower> powerHal_1_0 = V1_0::IPower::getService();
+ if (powerHal_1_0 != nullptr) {
+ // Try to cast to Power HAL 1.3
+ powerHal = V1_3::IPower::castFrom(powerHal_1_0);
+ if (powerHal == nullptr) {
+ ALOGW("No Power HAL 1.3 service in system, disabling PowerAdvisor");
+ } else {
+ ALOGI("Loaded Power HAL 1.3 service");
}
+ } else {
+ ALOGW("No Power HAL found, disabling PowerAdvisor");
}
+
if (powerHal == nullptr) {
return nullptr;
}
@@ -162,12 +168,9 @@
}
private:
- static bool sHasPowerHal_1_3;
const sp<V1_3::IPower> mPowerHal = nullptr;
};
-bool HidlPowerHalWrapper::sHasPowerHal_1_3 = true;
-
class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper {
public:
AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::move(powerHal)) {
@@ -226,7 +229,13 @@
PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() {
static std::unique_ptr<HalWrapper> sHalWrapper = nullptr;
+ static bool sHasHal = true;
+ if (!sHasHal) {
+ return nullptr;
+ }
+
+ // If we used to have a HAL, but it stopped responding, attempt to reconnect
if (mReconnectPowerHal) {
sHalWrapper = nullptr;
mReconnectPowerHal = false;
@@ -244,6 +253,12 @@
sHalWrapper = HidlPowerHalWrapper::connect();
}
+ // If we make it to this point and still don't have a HAL, it's unlikely we
+ // will, so stop trying
+ if (sHalWrapper == nullptr) {
+ sHasHal = false;
+ }
+
return sHalWrapper.get();
}
diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
index 957d289..4a90acb 100644
--- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
+++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h
@@ -35,6 +35,7 @@
public:
virtual ~PowerAdvisor();
+ virtual void onBootFinished() = 0;
virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0;
virtual void notifyDisplayUpdateImminent() = 0;
};
@@ -56,12 +57,14 @@
PowerAdvisor();
~PowerAdvisor() override;
+ void onBootFinished() override;
void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override;
void notifyDisplayUpdateImminent() override;
private:
HalWrapper* getPowerHal();
+ std::atomic_bool mBootFinished = false;
bool mReconnectPowerHal = false;
std::unordered_set<DisplayId> mExpensiveDisplays;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 766871e..3d67a6b 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -116,6 +116,7 @@
mCurrentState.frameRateSelectionPriority = PRIORITY_UNSET;
mCurrentState.metadata = args.metadata;
mCurrentState.shadowRadius = 0.f;
+ mCurrentState.treeHasFrameRateVote = false;
// drawing state & current state are identical
mDrawingState = mCurrentState;
@@ -219,6 +220,9 @@
}
mFlinger->markLayerPendingRemovalLocked(this);
+ if (hasInput()) {
+ mFlinger->dirtyInput();
+ }
}
void Layer::onRemovedFromCurrentState() {
@@ -719,9 +723,14 @@
Hwc2::IComposerClient::Composition Layer::getCompositionType(
const sp<const DisplayDevice>& display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
- LOG_FATAL_IF(!outputLayer);
- return outputLayer->getState().hwc ? (*outputLayer->getState().hwc).hwcCompositionType
- : Hwc2::IComposerClient::Composition::CLIENT;
+ if (outputLayer == nullptr) {
+ return Hwc2::IComposerClient::Composition::INVALID;
+ }
+ if (outputLayer->getState().hwc) {
+ return (*outputLayer->getState().hwc).hwcCompositionType;
+ } else {
+ return Hwc2::IComposerClient::Composition::CLIENT;
+ }
}
bool Layer::getClearClientTarget(const sp<const DisplayDevice>& display) const {
@@ -988,6 +997,9 @@
commitTransaction(c);
mPendingStatesSnapshot = mPendingStates;
mCurrentState.callbackHandles = {};
+
+ maybeDirtyInput();
+
return flags;
}
@@ -1326,6 +1338,44 @@
return true;
}
+void Layer::updateTreeHasFrameRateVote() {
+ const auto traverseTree = [&](const LayerVector::Visitor& visitor) {
+ auto parent = getParent();
+ while (parent) {
+ visitor(parent.get());
+ parent = parent->getParent();
+ }
+
+ traverse(LayerVector::StateSet::Current, visitor);
+ };
+
+ // update parents and children about the vote
+ // First traverse the tree and count how many layers has votes
+ int layersWithVote = 0;
+ traverseTree([&layersWithVote](Layer* layer) {
+ if (layer->mCurrentState.frameRate.rate > 0 ||
+ layer->mCurrentState.frameRate.type == FrameRateCompatibility::NoVote) {
+ layersWithVote++;
+ }
+ });
+
+ // Now update the other layers
+ bool transactionNeeded = false;
+ traverseTree([layersWithVote, &transactionNeeded](Layer* layer) {
+ if (layer->mCurrentState.treeHasFrameRateVote != layersWithVote > 0) {
+ layer->mCurrentState.sequence++;
+ layer->mCurrentState.treeHasFrameRateVote = layersWithVote > 0;
+ layer->mCurrentState.modified = true;
+ layer->setTransactionFlags(eTransactionNeeded);
+ transactionNeeded = true;
+ }
+ });
+
+ if (transactionNeeded) {
+ mFlinger->setTransactionFlags(eTraversalNeeded);
+ }
+}
+
bool Layer::setFrameRate(FrameRate frameRate) {
if (!mFlinger->useFrameRateApi) {
return false;
@@ -1337,12 +1387,26 @@
mCurrentState.sequence++;
mCurrentState.frameRate = frameRate;
mCurrentState.modified = true;
+
+ updateTreeHasFrameRateVote();
+
setTransactionFlags(eTransactionNeeded);
return true;
}
-Layer::FrameRate Layer::getFrameRate() const {
- return getDrawingState().frameRate;
+Layer::FrameRate Layer::getFrameRateForLayerTree() const {
+ const auto frameRate = getDrawingState().frameRate;
+ if (frameRate.rate > 0 || frameRate.type == FrameRateCompatibility::NoVote) {
+ return frameRate;
+ }
+
+ // This layer doesn't have a frame rate. If one of its ancestors or successors
+ // have a vote, return a NoVote for ancestors/successors to set the vote
+ if (getDrawingState().treeHasFrameRateVote) {
+ return {0, FrameRateCompatibility::NoVote};
+ }
+
+ return frameRate;
}
void Layer::deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber) {
@@ -1600,6 +1664,7 @@
mCurrentChildren.add(layer);
layer->setParent(this);
+ updateTreeHasFrameRateVote();
}
ssize_t Layer::removeChild(const sp<Layer>& layer) {
@@ -1607,7 +1672,24 @@
setTransactionFlags(eTransactionNeeded);
layer->setParent(nullptr);
- return mCurrentChildren.remove(layer);
+ const auto removeResult = mCurrentChildren.remove(layer);
+
+ updateTreeHasFrameRateVote();
+ layer->updateTreeHasFrameRateVote();
+
+ return removeResult;
+}
+
+void Layer::reparentChildren(const sp<Layer>& newParent) {
+ if (attachChildren()) {
+ setTransactionFlags(eTransactionNeeded);
+ }
+
+ for (const sp<Layer>& child : mCurrentChildren) {
+ newParent->addChild(child);
+ }
+ mCurrentChildren.clear();
+ updateTreeHasFrameRateVote();
}
bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) {
@@ -1623,13 +1705,7 @@
return false;
}
- if (attachChildren()) {
- setTransactionFlags(eTransactionNeeded);
- }
- for (const sp<Layer>& child : mCurrentChildren) {
- newParent->addChild(child);
- }
- mCurrentChildren.clear();
+ reparentChildren(newParent);
return true;
}
@@ -2052,13 +2128,20 @@
setTransactionFlags(eTransactionNeeded);
}
-LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags) const {
+LayerProto* Layer::writeToProto(LayersProto& layersProto, uint32_t traceFlags,
+ const sp<const DisplayDevice>& device) const {
LayerProto* layerProto = layersProto.add_layers();
writeToProtoDrawingState(layerProto, traceFlags);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
+ // Only populate for the primary display.
+ if (device) {
+ const Hwc2::IComposerClient::Composition compositionType = getCompositionType(device);
+ layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
+ }
+
for (const sp<Layer>& layer : mDrawingChildren) {
- layer->writeToProto(layersProto, traceFlags);
+ layer->writeToProto(layersProto, traceFlags, device);
}
return layerProto;
@@ -2460,6 +2543,32 @@
}
}
+bool Layer::maybeDirtyInput() {
+ // No sense redirtying input.
+ if (mFlinger->inputDirty()) return true;
+
+ if (hasInput()) {
+ mFlinger->dirtyInput();
+ return true;
+ }
+
+ // If a child or relative dirties the input, no sense continuing to traverse
+ // so we return early and halt the recursion. We traverse ourselves instead
+ // of using traverse() so we can implement this early halt.
+ for (const sp<Layer>& child : mDrawingChildren) {
+ if (child->maybeDirtyInput()) {
+ return true;
+ }
+ }
+ for (const wp<Layer>& weakRelative : mDrawingState.zOrderRelatives) {
+ sp<Layer> relative = weakRelative.promote();
+ if (relative && relative->maybeDirtyInput()) {
+ return true;
+ }
+ }
+ return false;
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5d2144a..be80f78 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -259,6 +259,9 @@
int32_t frameRateSelectionPriority;
FrameRate frameRate;
+
+ // Indicates whether parents / children of this layer had set FrameRate
+ bool treeHasFrameRateVote;
};
explicit Layer(const LayerCreationArgs& args);
@@ -344,7 +347,8 @@
virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber);
virtual bool setOverrideScalingMode(int32_t overrideScalingMode);
virtual bool setMetadata(const LayerMetadata& data);
- virtual bool reparentChildren(const sp<IBinder>& layer);
+ bool reparentChildren(const sp<IBinder>& newParentHandle);
+ void reparentChildren(const sp<Layer>& newParent);
virtual void setChildrenDrawingParent(const sp<Layer>& layer);
virtual bool reparent(const sp<IBinder>& newParentHandle);
virtual bool detachChildren();
@@ -506,7 +510,8 @@
bool isRemovedFromCurrentState() const;
LayerProto* writeToProto(LayersProto& layersProto,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
+ const sp<const DisplayDevice>& device = nullptr) const;
// Write states that are modified by the main thread. This includes drawing
// state as well as buffer data. This should be called in the main or tracing
@@ -800,7 +805,7 @@
Rect getCroppedBufferSize(const Layer::State& s) const;
bool setFrameRate(FrameRate frameRate);
- virtual FrameRate getFrameRate() const;
+ virtual FrameRate getFrameRateForLayerTree() const;
protected:
// constant
@@ -829,6 +834,7 @@
// For unit tests
friend class TestableSurfaceFlinger;
friend class RefreshRateSelectionTest;
+ friend class SetFrameRateTest;
virtual void commitTransaction(const State& stateToCommit);
@@ -996,6 +1002,10 @@
// Window types from WindowManager.LayoutParams
const int mWindowType;
+ // Called when mDrawingState has changed. If we or one of our children/relatives hasInput()
+ // then we will dirty the setInputWindows cache.
+ bool maybeDirtyInput();
+
private:
/**
* Returns an unsorted vector of all layers that are part of this tree.
@@ -1012,6 +1022,8 @@
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
+ void updateTreeHasFrameRateVote();
+
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
ui::Transform mEffectiveTransform;
diff --git a/services/surfaceflinger/LayerRejecter.cpp b/services/surfaceflinger/LayerRejecter.cpp
index d3364a0..e6c8654 100644
--- a/services/surfaceflinger/LayerRejecter.cpp
+++ b/services/surfaceflinger/LayerRejecter.cpp
@@ -120,7 +120,7 @@
// We latch the transparent region here, instead of above where we latch
// the rest of the geometry because it is only content but not necessarily
// resize dependent.
- if (!mFront.activeTransparentRegion_legacy.isTriviallyEqual(
+ if (!mFront.activeTransparentRegion_legacy.hasSameRects(
mFront.requestedTransparentRegion_legacy)) {
mFront.activeTransparentRegion_legacy = mFront.requestedTransparentRegion_legacy;
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 40a63d7..5009e10 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -162,10 +162,6 @@
return mProducer->setAutoPrerotation(autoPrerotation);
}
-status_t MonitoredProducer::setFrameRate(float frameRate) {
- return mProducer->setFrameRate(frameRate);
-}
-
IBinder* MonitoredProducer::onAsBinder() {
return this;
}
diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h
index 4bda831..788919b 100644
--- a/services/surfaceflinger/MonitoredProducer.h
+++ b/services/surfaceflinger/MonitoredProducer.h
@@ -71,7 +71,6 @@
virtual status_t getUniqueId(uint64_t* outId) const override;
virtual status_t getConsumerUsage(uint64_t* outUsage) const override;
virtual status_t setAutoPrerotation(bool autoPrerotation) override;
- virtual status_t setFrameRate(float frameRate) override;
// The Layer which created this producer, and on which queued Buffer's will be displayed.
sp<Layer> getLayer() const;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 682679c..c675971 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -168,9 +168,9 @@
}
void RefreshRateOverlay::primeCache() {
- auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+ auto& allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
if (allRefreshRates.size() == 1) {
- auto fps = allRefreshRates.begin()->second.fps;
+ auto fps = allRefreshRates.begin()->second->fps;
half4 color = {LOW_FPS_COLOR, ALPHA};
mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
return;
@@ -178,8 +178,8 @@
std::vector<uint32_t> supportedFps;
supportedFps.reserve(allRefreshRates.size());
- for (auto [ignored, refreshRate] : allRefreshRates) {
- supportedFps.push_back(refreshRate.fps);
+ for (auto& [ignored, refreshRate] : allRefreshRates) {
+ supportedFps.push_back(refreshRate->fps);
}
std::sort(supportedFps.begin(), supportedFps.end());
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 0031d70..68cd84f 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -201,9 +201,10 @@
void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) {
- wp<Layer> stopLayer = stopLayerHandle != nullptr
- ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner
- : nullptr;
+ wp<Layer> stopLayer;
+ if (stopLayerHandle != nullptr && stopLayerHandle->localBinder() != nullptr) {
+ stopLayer = static_cast<Layer::Handle*>(stopLayerHandle.get())->owner;
+ }
sp<IBinder> asBinder = IInterface::asBinder(listener);
asBinder->linkToDeath(this);
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 776e984..4e3f85f 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -30,6 +30,7 @@
#include "EventThread.h"
namespace android {
+using base::StringAppendF;
DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name)
@@ -107,6 +108,12 @@
}
}
+void DispSyncSource::dump(std::string& result) const {
+ std::lock_guard lock(mVsyncMutex);
+ StringAppendF(&result, "DispSyncSource: %s(%s)\n", mName, mEnabled ? "enabled" : "disabled");
+ mDispSync->dump(result);
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 328b8c1..f278712 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -36,6 +36,8 @@
void setCallback(VSyncSource::Callback* callback) override;
void setPhaseOffset(nsecs_t phaseOffset) override;
+ void dump(std::string&) const override;
+
private:
// The following method is the implementation of the DispSync::Callback.
virtual void onDispSyncEvent(nsecs_t when);
@@ -52,7 +54,7 @@
std::mutex mCallbackMutex;
VSyncSource::Callback* mCallback GUARDED_BY(mCallbackMutex) = nullptr;
- std::mutex mVsyncMutex;
+ mutable std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index acab5a6..0d6a92e 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -425,7 +425,12 @@
// display is off, keep feeding clients at 60 Hz.
const auto timeout = mState == State::SyntheticVSync ? 16ms : 1000ms;
if (mCondition.wait_for(lock, timeout) == std::cv_status::timeout) {
- ALOGW_IF(mState == State::VSync, "Faking VSYNC due to driver stall");
+ if (mState == State::VSync) {
+ ALOGW("Faking VSYNC due to driver stall for thread %s", mThreadName);
+ std::string debugInfo = "VsyncSource debug info:\n";
+ mVSyncSource->dump(debugInfo);
+ ALOGW("%s", debugInfo.c_str());
+ }
LOG_FATAL_IF(!mVSyncState);
mPendingEvents.push_back(makeVSync(mVSyncState->displayId,
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 466234d..98b1876 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -66,6 +66,8 @@
virtual void setVSyncEnabled(bool enable) = 0;
virtual void setCallback(Callback* callback) = 0;
virtual void setPhaseOffset(nsecs_t phaseOffset) = 0;
+
+ virtual void dump(std::string& result) const = 0;
};
class EventThreadConnection : public BnDisplayEventConnection {
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index fa46e6f..31da588 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,6 +45,7 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
+ void dump(std::string&) const override {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index a8e6756..8958d9a 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- if (layer.getFrameRate().rate > 0) {
+ if (layer.getFrameRateForLayerTree().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -109,7 +109,7 @@
// Only use the layer if the reference still exists.
if (layer || CC_UNLIKELY(mTraceEnabled)) {
// Check if frame rate was set on layer.
- const auto frameRate = layer->getFrameRate();
+ const auto frameRate = layer->getFrameRateForLayerTree();
if (frameRate.rate > 0.f) {
const auto voteType = [&]() {
switch (frameRate.type) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
index 6ef6ce4..8b08592 100644
--- a/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistoryV2.cpp
@@ -40,7 +40,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfoV2& info, nsecs_t threshold) {
- if (layer.getFrameRate().rate > 0) {
+ if (layer.getFrameRateForLayerTree().rate > 0) {
return layer.isVisible();
}
return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
@@ -166,7 +166,7 @@
if (const auto layer = weak.promote(); layer && isLayerActive(*layer, *info, threshold)) {
i++;
// Set layer vote if set
- const auto frameRate = layer->getFrameRate();
+ const auto frameRate = layer->getFrameRateForLayerTree();
const auto voteType = [&]() {
switch (frameRate.type) {
case Layer::FrameRateCompatibility::Default:
@@ -212,7 +212,5 @@
for (const auto& [layer, info] : activeLayers()) {
info->clearHistory();
}
-
- mActiveLayersEnd = 0;
}
} // namespace android::scheduler::impl
diff --git a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
index b755798..b4365bf 100644
--- a/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfoV2.cpp
@@ -82,14 +82,14 @@
}
}
- const auto numFrames = std::distance(it, mFrameTimes.end()) - 1;
- if (numFrames <= 0) {
+ const auto numFrames = std::distance(it, mFrameTimes.end());
+ if (numFrames < FREQUENT_LAYER_WINDOW_SIZE) {
return false;
}
// Layer is considered frequent if the average frame rate is higher than the threshold
const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return (1e9f * numFrames) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
+ return (1e9f * (numFrames - 1)) / totalTime >= MIN_FPS_FOR_FREQUENT_LAYER;
}
bool LayerInfoV2::hasEnoughDataForHeuristic() const {
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 63d9c4b..43883fb 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -36,6 +36,19 @@
return std::abs(fpsA - fpsB) <= MARGIN;
}
+std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate->fps);
+ }
+
+ return refreshRates;
+}
+
} // namespace
namespace android::scheduler {
@@ -45,14 +58,21 @@
namespace impl {
PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
- : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max())),
- mOffsets(initializeOffsets(refreshRateConfigs)),
- mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
+ : PhaseOffsets(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ // Below defines the threshold when an offset is considered to be negative,
+ // i.e. targeting for the N+2 vsync instead of N+1. This means that: For offset
+ // < threshold, SF wake up (vsync_duration - offset) before HW vsync. For
+ // offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW
+ // vsync.
+ getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())) {}
+
+PhaseOffsets::PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync)
+ : mThresholdForNextVsync(thresholdForNextVsync),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
void PhaseOffsets::dump(std::string& result) const {
const auto [early, earlyGl, late] = getCurrentOffsets();
@@ -67,19 +87,24 @@
}
std::unordered_map<float, PhaseOffsets::Offsets> PhaseOffsets::initializeOffsets(
- const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
- for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
- if (refreshRate.fps > 65.0f) {
- offsets.emplace(refreshRate.fps, getHighFpsOffsets(refreshRate.vsyncPeriod));
- } else {
- offsets.emplace(refreshRate.fps, getDefaultOffsets(refreshRate.vsyncPeriod));
- }
+ for (const auto& refreshRate : refreshRates) {
+ offsets.emplace(refreshRate,
+ getPhaseOffsets(refreshRate, static_cast<nsecs_t>(1e9f / refreshRate)));
}
return offsets;
}
+PhaseOffsets::Offsets PhaseOffsets::getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const {
+ if (fps > 65.0f) {
+ return getHighFpsOffsets(vsyncPeriod);
+ } else {
+ return getDefaultOffsets(vsyncPeriod);
+ }
+}
+
PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -159,8 +184,15 @@
[&fps](const std::pair<float, Offsets>& candidateFps) {
return fpsEqualsWithMargin(fps, candidateFps.first);
});
- LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
- return iter->second;
+
+ if (iter != mOffsets.end()) {
+ return iter->second;
+ }
+
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
+ // In this case just construct the offset.
+ ALOGW("Can't find offset for %.2f fps", fps);
+ return getPhaseOffsets(fps, static_cast<nsecs_t>(1e9f / fps));
}
static void validateSysprops() {
@@ -231,19 +263,6 @@
};
}
-static std::vector<float> getRefreshRatesFromConfigs(
- const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
- const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
- std::vector<float> refreshRates;
- refreshRates.reserve(allRefreshRates.size());
-
- for (const auto& [ignored, refreshRate] : allRefreshRates) {
- refreshRates.emplace_back(refreshRate.fps);
- }
-
- return refreshRates;
-}
-
std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
const std::vector<float>& refreshRates) const {
std::unordered_map<float, Offsets> offsets;
@@ -288,8 +307,7 @@
return iter->second;
}
- // Unknown refresh rate. This might happen if we get a hotplug event for the default display.
- // This happens only during tests and not during regular device operation.
+ // Unknown refresh rate. This might happen if we get a hotplug event for an external display.
// In this case just construct the offset.
ALOGW("Can't find offset for %.2f fps", fps);
return constructOffsets(static_cast<nsecs_t>(1e9f / fps));
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index b7d4eae..fa8011d 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -66,11 +66,15 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
-private:
+protected:
+ // Used for unit tests
+ PhaseOffsets(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t thresholdForNextVsync);
std::unordered_map<float, Offsets> initializeOffsets(
- const scheduler::RefreshRateConfigs&) const;
- Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
- Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+ const std::vector<float>& refreshRates) const;
+ Offsets getDefaultOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getHighFpsOffsets(nsecs_t vsyncPeriod) const;
+ Offsets getPhaseOffsets(float fps, nsecs_t vsyncPeriod) const;
const nsecs_t mThresholdForNextVsync;
const std::unordered_map<float, Offsets> mOffsets;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index b876ccd..02d0b53 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -23,6 +23,9 @@
#include <chrono>
#include <cmath>
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateConfigs"
+
namespace android::scheduler {
using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
@@ -95,16 +98,19 @@
}
const RefreshRate& RefreshRateConfigs::getRefreshRateForContentV2(
- const std::vector<LayerRequirement>& layers, bool touchActive) const {
+ const std::vector<LayerRequirement>& layers, bool touchActive,
+ bool* touchConsidered) const {
ATRACE_CALL();
ALOGV("getRefreshRateForContent %zu layers", layers.size());
+ *touchConsidered = false;
std::lock_guard lock(mLock);
- // For now if the touch is active return the peak refresh rate
- // This should be optimized to consider other layers as well.
- if (touchActive) {
- return *mAvailableRefreshRates.back();
+ // If there are not layers, there is not content detection, so return the current
+ // refresh rate.
+ if (layers.empty()) {
+ *touchConsidered = touchActive;
+ return touchActive ? *mAvailableRefreshRates.back() : getCurrentRefreshRateByPolicyLocked();
}
int noVoteLayers = 0;
@@ -112,17 +118,30 @@
int maxVoteLayers = 0;
int explicitDefaultVoteLayers = 0;
int explicitExactOrMultipleVoteLayers = 0;
+ float maxExplicitWeight = 0;
for (const auto& layer : layers) {
- if (layer.vote == LayerVoteType::NoVote)
+ if (layer.vote == LayerVoteType::NoVote) {
noVoteLayers++;
- else if (layer.vote == LayerVoteType::Min)
+ } else if (layer.vote == LayerVoteType::Min) {
minVoteLayers++;
- else if (layer.vote == LayerVoteType::Max)
+ } else if (layer.vote == LayerVoteType::Max) {
maxVoteLayers++;
- else if (layer.vote == LayerVoteType::ExplicitDefault)
+ } else if (layer.vote == LayerVoteType::ExplicitDefault) {
explicitDefaultVoteLayers++;
- else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple)
+ maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ } else if (layer.vote == LayerVoteType::ExplicitExactOrMultiple) {
explicitExactOrMultipleVoteLayers++;
+ maxExplicitWeight = std::max(maxExplicitWeight, layer.weight);
+ }
+ }
+
+ // Consider the touch event if there are no ExplicitDefault layers.
+ // ExplicitDefault are mostly interactive (as opposed to ExplicitExactOrMultiple)
+ // and therefore if those posted an explicit vote we should not change it
+ // if get get a touch event.
+ if (touchActive && explicitDefaultVoteLayers == 0) {
+ *touchConsidered = true;
+ return *mAvailableRefreshRates.back();
}
// Only if all layers want Min we should return Min
@@ -162,16 +181,17 @@
const auto layerPeriod = round<nsecs_t>(1e9f / layer.desiredRefreshRate);
if (layer.vote == LayerVoteType::ExplicitDefault) {
const auto layerScore = [&]() {
- const auto [displayFramesQuot, displayFramesRem] =
- getDisplayFrames(layerPeriod, displayPeriod);
- if (displayFramesQuot == 0) {
- // Layer desired refresh rate is higher the display rate.
- return static_cast<float>(layerPeriod) / static_cast<float>(displayPeriod);
+ // Find the actual rate the layer will render, assuming
+ // that layerPeriod is the minimal time to render a frame
+ auto actualLayerPeriod = displayPeriod;
+ int multiplier = 1;
+ while (layerPeriod > actualLayerPeriod + MARGIN_FOR_PERIOD_CALCULATION) {
+ multiplier++;
+ actualLayerPeriod = displayPeriod * multiplier;
}
-
- return 1.0f -
- (static_cast<float>(displayFramesRem) /
- static_cast<float>(layerPeriod));
+ return std::min(1.0f,
+ static_cast<float>(layerPeriod) /
+ static_cast<float>(actualLayerPeriod));
}();
ALOGV("%s (ExplicitDefault, weight %.2f) %.2fHz gives %s score of %.2f",
@@ -229,20 +249,21 @@
? getBestRefreshRate(scores.rbegin(), scores.rend())
: getBestRefreshRate(scores.begin(), scores.end());
- return bestRefreshRate == nullptr ? *mCurrentRefreshRate : *bestRefreshRate;
+ return *bestRefreshRate;
}
template <typename Iter>
const RefreshRate* RefreshRateConfigs::getBestRefreshRate(Iter begin, Iter end) const {
- const RefreshRate* bestRefreshRate = nullptr;
- float max = 0;
+ constexpr auto EPSILON = 0.001f;
+ const RefreshRate* bestRefreshRate = begin->first;
+ float max = begin->second;
for (auto i = begin; i != end; ++i) {
const auto [refreshRate, score] = *i;
ALOGV("%s scores %.2f", refreshRate->name.c_str(), score);
ATRACE_INT(refreshRate->name.c_str(), round<int>(score * 100));
- if (score > max) {
+ if (score > max * (1 + EPSILON)) {
max = score;
bestRefreshRate = refreshRate;
}
@@ -272,16 +293,20 @@
const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicy() const {
std::lock_guard lock(mLock);
+ return getCurrentRefreshRateByPolicyLocked();
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRateByPolicyLocked() const {
if (std::find(mAvailableRefreshRates.begin(), mAvailableRefreshRates.end(),
mCurrentRefreshRate) != mAvailableRefreshRates.end()) {
return *mCurrentRefreshRate;
}
- return mRefreshRates.at(mDefaultConfig);
+ return *mRefreshRates.at(mDefaultConfig);
}
void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
std::lock_guard lock(mLock);
- mCurrentRefreshRate = &mRefreshRates.at(configId);
+ mCurrentRefreshRate = mRefreshRates.at(configId).get();
}
RefreshRateConfigs::RefreshRateConfigs(const std::vector<InputConfig>& configs,
@@ -316,7 +341,7 @@
if (mRefreshRates.count(defaultConfigId) == 0) {
return BAD_VALUE;
}
- const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
+ const RefreshRate& refreshRate = *mRefreshRates.at(defaultConfigId);
if (!refreshRate.inPolicy(minRefreshRate, maxRefreshRate)) {
return BAD_VALUE;
}
@@ -351,10 +376,10 @@
outRefreshRates->clear();
outRefreshRates->reserve(mRefreshRates.size());
for (const auto& [type, refreshRate] : mRefreshRates) {
- if (shouldAddRefreshRate(refreshRate)) {
+ if (shouldAddRefreshRate(*refreshRate)) {
ALOGV("getSortedRefreshRateList: config %d added to list policy",
- refreshRate.configId.value());
- outRefreshRates->push_back(&refreshRate);
+ refreshRate->configId.value());
+ outRefreshRates->push_back(refreshRate.get());
}
}
@@ -366,7 +391,7 @@
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
- HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
+ HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig)->configGroup;
ALOGV("constructAvailableRefreshRates: default %d group %d min %.2f max %.2f",
mDefaultConfig.value(), group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
getSortedRefreshRateList(
@@ -393,16 +418,15 @@
LOG_ALWAYS_FATAL_IF(configs.empty());
LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
- auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
- const float fps = 1e9f / config.vsyncPeriod;
- return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
- base::StringPrintf("%2.ffps", fps), fps);
- };
-
for (const auto& config : configs) {
- mRefreshRates.emplace(config.configId, buildRefreshRate(config));
+ const float fps = 1e9f / config.vsyncPeriod;
+ mRefreshRates.emplace(config.configId,
+ std::make_unique<RefreshRate>(config.configId, config.vsyncPeriod,
+ config.configGroup,
+ base::StringPrintf("%2.ffps", fps),
+ fps));
if (config.configId == currentHwcConfig) {
- mCurrentRefreshRate = &mRefreshRates.at(config.configId);
+ mCurrentRefreshRate = mRefreshRates.at(config.configId).get();
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0b5c73c..87d4389 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -59,6 +59,8 @@
configGroup(configGroup),
name(std::move(name)),
fps(fps) {}
+
+ RefreshRate(const RefreshRate&) = delete;
// This config ID corresponds to the position of the config in the vector that is stored
// on the device.
const HwcConfigIndexType configId;
@@ -85,7 +87,8 @@
bool operator==(const RefreshRate& other) const { return !(*this != other); }
};
- using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
+ using AllRefreshRatesMapType =
+ std::unordered_map<HwcConfigIndexType, std::unique_ptr<const RefreshRate>>;
// Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
// valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
@@ -134,9 +137,11 @@
// Returns the refresh rate that fits best to the given layers. This function also gets a
// boolean flag that indicates whether user touched the screen recently to be factored in when
- // choosing the refresh rate.
+ // choosing the refresh rate and returns whether the refresh rate was chosen as a result of
+ // a touch event.
const RefreshRate& getRefreshRateForContentV2(const std::vector<LayerRequirement>& layers,
- bool touchActive) const EXCLUDES(mLock);
+ bool touchActive, bool* touchConsidered) const
+ EXCLUDES(mLock);
// Returns all the refresh rates supported by the device. This won't change at runtime.
const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
@@ -163,7 +168,7 @@
// Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
// runtime.
const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
- return mRefreshRates.at(configId);
+ return *mRefreshRates.at(configId);
};
// Stores the current configId the device operates at
@@ -199,6 +204,10 @@
// display refresh period.
std::pair<nsecs_t, nsecs_t> getDisplayFrames(nsecs_t layerPeriod, nsecs_t displayPeriod) const;
+ // Returns the current refresh rate, if allowed. Otherwise the default that is allowed by
+ // the policy.
+ const RefreshRate& getCurrentRefreshRateByPolicyLocked() const REQUIRES(mLock);
+
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
AllRefreshRatesMapType mRefreshRates;
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 3a44332..cd6075f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -99,12 +99,14 @@
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
const scheduler::RefreshRateConfigs& refreshRateConfig,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ bool useContentDetection)
: mPrimaryDispSync(createDispSync()),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(refreshRateConfig),
+ mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {
using namespace sysprop;
@@ -147,12 +149,14 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs,
- ISchedulerCallback& schedulerCallback, bool useContentDetectionV2)
+ ISchedulerCallback& schedulerCallback, bool useContentDetectionV2,
+ bool useContentDetection)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs),
+ mUseContentDetection(useContentDetection),
mUseContentDetectionV2(useContentDetectionV2) {}
Scheduler::~Scheduler() {
@@ -381,6 +385,17 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
+ // If the content detection feature is off, all layers are registered at NoVote. We still
+ // keep the layer history, since we use it for other features (like Frame Rate API), so layers
+ // still need to be registered.
+ if (!mUseContentDetection) {
+ mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
+ mRefreshRateConfigs.getMaxRefreshRate().fps,
+ scheduler::LayerHistory::LayerVoteType::NoVote);
+ return;
+ }
+
+ // In V1 of content detection, all layers are registered as Heuristic (unless it's wallpaper).
if (!mUseContentDetectionV2) {
const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
@@ -391,6 +406,7 @@
scheduler::LayerHistory::LayerVoteType::Heuristic);
} else {
if (layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER) {
+ // Running Wallpaper at Min is considered as part of content detection.
mLayerHistory->registerLayer(layer, mRefreshRateConfigs.getMinRefreshRate().fps,
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Min);
@@ -403,20 +419,6 @@
mRefreshRateConfigs.getMaxRefreshRate().fps,
scheduler::LayerHistory::LayerVoteType::Heuristic);
}
-
- // TODO(146935143): Simulate youtube app vote. This should be removed once youtube calls the
- // API to set desired rate
- {
- const auto vote = property_get_int32("experimental.sf.force_youtube_vote", 0);
- if (vote != 0 &&
- layer->getName() ==
- "SurfaceView - "
- "com.google.android.youtube/"
- "com.google.android.apps.youtube.app.WatchWhileActivity#0") {
- layer->setFrameRate(
- Layer::FrameRate(vote, Layer::FrameRateCompatibility::ExactOrMultiple));
- }
- }
}
}
@@ -447,7 +449,7 @@
return;
}
mFeatures.configId = newConfigId;
- auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ auto& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
}
@@ -467,7 +469,7 @@
// that is currently on top. b/142507166 will give us this capability.
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (mLayerHistory) {
- mLayerHistory->clear();
+ // Layer History will be cleared based on RefreshRateConfigs::getRefreshRateForContentV2
mTouchTimer->reset();
@@ -499,7 +501,7 @@
// TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
// magic number
- const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
+ const auto& refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
@@ -512,6 +514,8 @@
// need to update the DispSync model anyway.
disableHardwareVsync(false /* makeUnavailable */);
}
+
+ mSchedulerCallback.kernelTimerChanged(state == TimerState::Expired);
}
void Scheduler::idleTimerCallback(TimerState state) {
@@ -537,8 +541,10 @@
StringAppendF(&result, "+ Idle timer: %s\n",
mIdleTimer ? mIdleTimer->dump().c_str() : states[0]);
- StringAppendF(&result, "+ Touch timer: %s\n\n",
+ StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : states[0]);
+ StringAppendF(&result, "+ Use content detection: %s\n\n",
+ sysprop::use_content_detection_for_refresh_rate(false) ? "on" : "off");
}
template <class T>
@@ -600,10 +606,21 @@
return mRefreshRateConfigs.getRefreshRateForContent(mFeatures.contentRequirements).configId;
}
- return mRefreshRateConfigs
- .getRefreshRateForContentV2(mFeatures.contentRequirements,
- mTouchTimer && mFeatures.touch == TouchState::Active)
- .configId;
+ bool touchConsidered;
+ const auto& ret =
+ mRefreshRateConfigs
+ .getRefreshRateForContentV2(mFeatures.contentRequirements,
+ mTouchTimer &&
+ mFeatures.touch == TouchState::Active,
+ &touchConsidered)
+ .configId;
+ if (touchConsidered) {
+ // Clear layer history if refresh rate was selected based on touch to allow
+ // the hueristic to pick up with the new rate.
+ mLayerHistory->clear();
+ }
+
+ return ret;
}
std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 46d1a5e..04cc96a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -51,6 +51,7 @@
virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
scheduler::RefreshRateConfigEvent) = 0;
virtual void repaintEverythingForHWC() = 0;
+ virtual void kernelTimerChanged(bool expired) = 0;
};
class Scheduler {
@@ -63,7 +64,7 @@
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2);
+ bool useContentDetectionV2, bool useContentDetection);
virtual ~Scheduler();
@@ -159,7 +160,7 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback,
- bool useContentDetectionV2);
+ bool useContentDetectionV2, bool useContentDetection);
std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
@@ -245,6 +246,9 @@
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
+ // This variable indicates whether to use the content detection feature at all.
+ const bool mUseContentDetection;
+ // This variable indicates whether to use V2 version of the content detection.
const bool mUseContentDetectionV2;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index a6fb3a4..2a2d7c5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -108,6 +108,8 @@
*/
virtual CancelResult cancel(CallbackToken token) = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncDispatch() = default;
VSyncDispatch(VSyncDispatch const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index d0f18ab..460d4a5 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -15,6 +15,7 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+#include <android-base/stringprintf.h>
#include <utils/Trace.h>
#include <vector>
@@ -23,6 +24,7 @@
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
VSyncDispatch::~VSyncDispatch() = default;
VSyncTracker::~VSyncTracker() = default;
@@ -122,6 +124,28 @@
mCv.wait(lk, [this]() REQUIRES(mRunningMutex) { return !mRunning; });
}
+void VSyncDispatchTimerQueueEntry::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mRunningMutex);
+ std::string armedInfo;
+ if (mArmedInfo) {
+ StringAppendF(&armedInfo, "[wake up in %.2fms for vsync %.2fms from now]",
+ (mArmedInfo->mActualWakeupTime - systemTime()) / 1e6f,
+ (mArmedInfo->mActualVsyncTime - systemTime()) / 1e6f);
+ }
+
+ StringAppendF(&result, "\t\t%s: %s %s\n", mName.c_str(),
+ mRunning ? "(in callback function)" : "", armedInfo.c_str());
+ StringAppendF(&result, "\t\t\tmWorkDuration: %.2fms mEarliestVsync: %.2fms relative to now\n",
+ mWorkDuration / 1e6f, (mEarliestVsync - systemTime()) / 1e6f);
+
+ if (mLastDispatchTime) {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime: %.2fms ago\n",
+ (systemTime() - *mLastDispatchTime) / 1e6f);
+ } else {
+ StringAppendF(&result, "\t\t\tmLastDispatchTime unknown\n");
+ }
+}
+
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
VSyncTracker& tracker, nsecs_t timerSlack,
nsecs_t minVsyncDistance)
@@ -296,6 +320,18 @@
return CancelResult::TooLate;
}
+void VSyncDispatchTimerQueue::dump(std::string& result) const {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ StringAppendF(&result, "\tmTimerSlack: %.2fms mMinVsyncDistance: %.2fms\n", mTimerSlack / 1e6f,
+ mMinVsyncDistance / 1e6f);
+ StringAppendF(&result, "\tmIntendedWakeupTime: %.2fms from now\n",
+ (mIntendedWakeupTime - systemTime()) / 1e6f);
+ StringAppendF(&result, "\tCallbacks:\n");
+ for (const auto& [token, entry] : mCallbacks) {
+ entry->dump(result);
+ }
+}
+
VSyncCallbackRegistration::VSyncCallbackRegistration(VSyncDispatch& dispatch,
VSyncDispatch::Callback const& callbackFn,
std::string const& callbackName)
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fd0a034..390e0c4 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -71,6 +71,8 @@
// Block calling thread while the callback is executing.
void ensureNotRunning();
+ void dump(std::string& result) const;
+
private:
std::string const mName;
VSyncDispatch::Callback const mCallback;
@@ -86,7 +88,7 @@
std::optional<ArmingInfo> mArmedInfo;
std::optional<nsecs_t> mLastDispatchTime;
- std::mutex mRunningMutex;
+ mutable std::mutex mRunningMutex;
std::condition_variable mCv;
bool mRunning GUARDED_BY(mRunningMutex) = false;
};
@@ -112,6 +114,7 @@
void unregisterCallback(CallbackToken token) final;
ScheduleResult schedule(CallbackToken token, nsecs_t workDuration, nsecs_t earliestVsync) final;
CancelResult cancel(CallbackToken token) final;
+ void dump(std::string& result) const final;
private:
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 399da19..a3cb772 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -22,6 +22,7 @@
//#define LOG_NDEBUG 0
#include "VSyncPredictor.h"
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <utils/Log.h>
@@ -31,6 +32,7 @@
#include <sstream>
namespace android::scheduler {
+using base::StringAppendF;
static auto constexpr kMaxPercent = 100u;
@@ -115,10 +117,10 @@
auto it = mRateMap.find(mIdealPeriod);
auto const currentPeriod = std::get<0>(it->second);
// TODO (b/144707443): its important that there's some precision in the mean of the ordinals
- // for the intercept calculation, so scale the ordinals by 10 to continue
+ // for the intercept calculation, so scale the ordinals by 1000 to continue
// fixed point calculation. Explore expanding
// scheduler::utils::calculate_mean to have a fixed point fractional part.
- static constexpr int kScalingFactor = 10;
+ static constexpr int64_t kScalingFactor = 1000;
for (auto i = 0u; i < mTimestamps.size(); i++) {
traceInt64If("VSP-ts", mTimestamps[i]);
@@ -147,7 +149,7 @@
return false;
}
- nsecs_t const anticipatedPeriod = top / bottom * kScalingFactor;
+ nsecs_t const anticipatedPeriod = top * kScalingFactor / bottom;
nsecs_t const intercept = meanTS - (anticipatedPeriod * meanOrdinal / kScalingFactor);
auto const percent = std::abs(anticipatedPeriod - mIdealPeriod) * kMaxPercent / mIdealPeriod;
@@ -263,6 +265,18 @@
clearTimestamps();
}
+void VSyncPredictor::dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\tmIdealPeriod=%.2f\n", mIdealPeriod / 1e6f);
+ StringAppendF(&result, "\tRefresh Rate Map:\n");
+ for (const auto& [idealPeriod, periodInterceptTuple] : mRateMap) {
+ StringAppendF(&result,
+ "\t\tFor ideal period %.2fms: period = %.2fms, intercept = %" PRId64 "\n",
+ idealPeriod / 1e6f, std::get<0>(periodInterceptTuple) / 1e6f,
+ std::get<1>(periodInterceptTuple));
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index ef1d88a..3ca878d 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -60,6 +60,8 @@
std::tuple<nsecs_t /* slope */, nsecs_t /* intercept */> getVSyncPredictionModel() const;
+ void dump(std::string& result) const final;
+
private:
VSyncPredictor(VSyncPredictor const&) = delete;
VSyncPredictor& operator=(VSyncPredictor const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index 949ba4c..892ae62 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -19,25 +19,51 @@
#define LOG_TAG "VSyncReactor"
//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
+#include <cutils/properties.h>
#include <log/log.h>
#include <utils/Trace.h>
+#include "../TracedOrdinal.h"
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
namespace android::scheduler {
+using base::StringAppendF;
Clock::~Clock() = default;
nsecs_t SystemClock::now() const {
return systemTime(SYSTEM_TIME_MONOTONIC);
}
+class PredictedVsyncTracer {
+public:
+ PredictedVsyncTracer(VSyncDispatch& dispatch)
+ : mRegistration(dispatch,
+ std::bind(&PredictedVsyncTracer::callback, this, std::placeholders::_1,
+ std::placeholders::_2),
+ "PredictedVsyncTracer") {
+ mRegistration.schedule(0, 0);
+ }
+
+private:
+ TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+ VSyncCallbackRegistration mRegistration;
+
+ void callback(nsecs_t /*vsyncTime*/, nsecs_t /*targetWakeupTim*/) {
+ mParity = !mParity;
+ mRegistration.schedule(0, 0);
+ }
+};
+
VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
: mClock(std::move(clock)),
mTracker(std::move(tracker)),
mDispatch(std::move(dispatch)),
- mPendingLimit(pendingFenceLimit) {}
+ mPendingLimit(pendingFenceLimit),
+ mPredictedVsyncTracer(property_get_bool("debug.sf.show_predicted_vsync", false)
+ ? std::make_unique<PredictedVsyncTracer>(*mDispatch)
+ : nullptr) {}
VSyncReactor::~VSyncReactor() = default;
@@ -48,11 +74,12 @@
public:
CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
nsecs_t period, nsecs_t offset, nsecs_t notBefore)
- : mCallback(cb),
+ : mName(name),
+ mCallback(cb),
mRegistration(dispatch,
std::bind(&CallbackRepeater::callback, this, std::placeholders::_1,
std::placeholders::_2),
- std::string(name)),
+ mName),
mPeriod(period),
mOffset(offset),
mLastCallTime(notBefore) {}
@@ -87,6 +114,13 @@
mRegistration.cancel();
}
+ void dump(std::string& result) const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "\t%s: mPeriod=%.2f last vsync time %.2fms relative to now (%s)\n",
+ mName.c_str(), mPeriod / 1e6f, (mLastCallTime - systemTime()) / 1e6f,
+ mStopped ? "stopped" : "running");
+ }
+
private:
void callback(nsecs_t vsynctime, nsecs_t wakeupTime) {
{
@@ -112,6 +146,7 @@
// Note change in sign between the two defnitions.
nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+ const std::string mName;
DispSync::Callback* const mCallback;
std::mutex mutable mMutex;
@@ -287,7 +322,7 @@
auto it = mCallbacks.find(callback);
if (it == mCallbacks.end()) {
// TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
- static auto constexpr maxListeners = 3;
+ static auto constexpr maxListeners = 4;
if (mCallbacks.size() >= maxListeners) {
ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
maxListeners, mCallbacks.size());
@@ -324,7 +359,35 @@
}
void VSyncReactor::dump(std::string& result) const {
- result += "VsyncReactor in use\n"; // TODO (b/144927823): add more information!
+ std::lock_guard<std::mutex> lk(mMutex);
+ StringAppendF(&result, "VsyncReactor in use\n");
+ StringAppendF(&result, "Has %zu unfired fences\n", mUnfiredFences.size());
+ StringAppendF(&result, "mInternalIgnoreFences=%d mExternalIgnoreFences=%d\n",
+ mInternalIgnoreFences, mExternalIgnoreFences);
+ StringAppendF(&result, "mMoreSamplesNeeded=%d mPeriodConfirmationInProgress=%d\n",
+ mMoreSamplesNeeded, mPeriodConfirmationInProgress);
+ if (mPeriodTransitioningTo) {
+ StringAppendF(&result, "mPeriodTransitioningTo=%" PRId64 "\n", *mPeriodTransitioningTo);
+ } else {
+ StringAppendF(&result, "mPeriodTransitioningTo=nullptr\n");
+ }
+
+ if (mLastHwVsync) {
+ StringAppendF(&result, "Last HW vsync was %.2fms ago\n",
+ (mClock->now() - *mLastHwVsync) / 1e6f);
+ } else {
+ StringAppendF(&result, "No Last HW vsync\n");
+ }
+
+ StringAppendF(&result, "CallbackRepeaters:\n");
+ for (const auto& [callback, repeater] : mCallbacks) {
+ repeater->dump(result);
+ }
+
+ StringAppendF(&result, "VSyncTracker:\n");
+ mTracker->dump(result);
+ StringAppendF(&result, "VSyncDispatch:\n");
+ mDispatch->dump(result);
}
void VSyncReactor::reset() {}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index aa8a38d..5ee29f8 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -30,6 +30,7 @@
class VSyncDispatch;
class VSyncTracker;
class CallbackRepeater;
+class PredictedVsyncTracer;
// TODO (b/145217110): consider renaming.
class VSyncReactor : public android::DispSync {
@@ -74,7 +75,7 @@
std::unique_ptr<VSyncDispatch> const mDispatch;
size_t const mPendingLimit;
- std::mutex mMutex;
+ mutable std::mutex mMutex;
bool mInternalIgnoreFences GUARDED_BY(mMutex) = false;
bool mExternalIgnoreFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
@@ -86,6 +87,8 @@
std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
GUARDED_BY(mMutex);
+
+ const std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
};
class SystemClock : public Clock {
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index a25b8a9..05a6fc3 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -66,6 +66,8 @@
/* Inform the tracker that the samples it has are not accurate for prediction. */
virtual void resetModel() = 0;
+ virtual void dump(std::string& result) const = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ce0a8a1..2da92b3 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -21,40 +21,34 @@
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <sys/types.h>
-#include <errno.h>
-#include <dlfcn.h>
+#include "SurfaceFlinger.h"
-#include <algorithm>
-#include <cinttypes>
-#include <cmath>
-#include <cstdint>
-#include <functional>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-
+#include <android/configuration.h>
+#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
+#include <android/hardware/configstore/1.1/types.h>
+#include <android/hardware/power/1.0/IPower.h>
#include <android/native_window.h>
-
-#include <cutils/properties.h>
-#include <log/log.h>
-
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
-
#include <compositionengine/CompositionEngine.h>
#include <compositionengine/CompositionRefreshArgs.h>
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/DisplayCreationArgs.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/RenderSurface.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <configstore/Utils.h>
+#include <cutils/compiler.h>
+#include <cutils/properties.h>
+#include <dlfcn.h>
#include <dvr/vr_flinger.h>
+#include <errno.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-
#include <gui/GuiConfig.h>
#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
@@ -63,7 +57,13 @@
#include <gui/LayerState.h>
#include <gui/Surface.h>
#include <input/IInputFlinger.h>
+#include <layerproto/LayerProtoParser.h>
+#include <log/log.h>
+#include <private/android_filesystem_config.h>
+#include <private/gui/SyncFeatures.h>
#include <renderengine/RenderEngine.h>
+#include <statslog.h>
+#include <sys/types.h>
#include <ui/ColorSpace.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayConfig.h>
@@ -80,8 +80,14 @@
#include <utils/Trace.h>
#include <utils/misc.h>
-#include <private/android_filesystem_config.h>
-#include <private/gui/SyncFeatures.h>
+#include <algorithm>
+#include <cinttypes>
+#include <cmath>
+#include <cstdint>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
@@ -90,23 +96,19 @@
#include "Colorizer.h"
#include "ContainerLayer.h"
#include "DisplayDevice.h"
-#include "EffectLayer.h"
-#include "Layer.h"
-#include "LayerVector.h"
-#include "MonitoredProducer.h"
-#include "NativeWindowSurface.h"
-#include "RefreshRateOverlay.h"
-#include "StartPropertySetThread.h"
-#include "SurfaceFlinger.h"
-#include "SurfaceInterceptor.h"
-
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/FramebufferSurface.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/VirtualDisplaySurface.h"
+#include "EffectLayer.h"
#include "Effects/Daltonizer.h"
#include "FrameTracer/FrameTracer.h"
+#include "Layer.h"
+#include "LayerVector.h"
+#include "MonitoredProducer.h"
+#include "NativeWindowSurface.h"
+#include "RefreshRateOverlay.h"
#include "RegionSamplingThread.h"
#include "Scheduler/DispSync.h"
#include "Scheduler/DispSyncSource.h"
@@ -115,23 +117,13 @@
#include "Scheduler/MessageQueue.h"
#include "Scheduler/PhaseOffsets.h"
#include "Scheduler/Scheduler.h"
+#include "StartPropertySetThread.h"
+#include "SurfaceFlingerProperties.h"
+#include "SurfaceInterceptor.h"
#include "TimeStats/TimeStats.h"
-
-#include <cutils/compiler.h>
-
#include "android-base/parseint.h"
#include "android-base/stringprintf.h"
-#include <android/configuration.h>
-#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
-#include <android/hardware/configstore/1.1/types.h>
-#include <android/hardware/power/1.0/IPower.h>
-#include <configstore/Utils.h>
-
-#include <layerproto/LayerProtoParser.h>
-#include "SurfaceFlingerProperties.h"
-
namespace android {
using namespace std::string_literals;
@@ -216,6 +208,7 @@
const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER");
const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER");
const String16 sDump("android.permission.DUMP");
+const char* KERNEL_IDLE_TIMER_PROP = "vendor.display.enable_kernel_idle_timer";
// ---------------------------------------------------------------------------
int64_t SurfaceFlinger::dispSyncPresentTimeOffset;
@@ -224,6 +217,8 @@
bool SurfaceFlinger::hasSyncFramework;
bool SurfaceFlinger::useVrFlinger;
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
+uint32_t SurfaceFlinger::maxGraphicsWidth;
+uint32_t SurfaceFlinger::maxGraphicsHeight;
bool SurfaceFlinger::hasWideColorDisplay;
ui::Rotation SurfaceFlinger::internalDisplayOrientation = ui::ROTATION_0;
bool SurfaceFlinger::useColorManagement;
@@ -290,6 +285,9 @@
maxFrameBufferAcquiredBuffers = max_frame_buffer_acquired_buffers(2);
+ maxGraphicsWidth = std::max(max_graphics_width(0), 0);
+ maxGraphicsHeight = std::max(max_graphics_height(0), 0);
+
hasWideColorDisplay = has_wide_color_display(false);
useColorManagement = use_color_management(false);
@@ -334,6 +332,9 @@
property_get("ro.bq.gpu_to_cpu_unsupported", value, "0");
mGpuToCpuSupported = !atoi(value);
+ property_get("ro.build.type", value, "user");
+ mIsUserBuild = strcmp(value, "user") == 0;
+
property_get("debug.sf.showupdates", value, "0");
mDebugRegion = atoi(value);
@@ -363,15 +364,12 @@
property_get("ro.surface_flinger.supports_background_blur", value, "0");
bool supportsBlurs = atoi(value);
- property_get("debug.sf.disable_blurs", value, "0");
- bool disableBlurs = atoi(value);
- mEnableBlurs = supportsBlurs && !disableBlurs;
- ALOGI_IF(!mEnableBlurs, "Disabling blur effects. supported: %d, disabled: %d", supportsBlurs,
- disableBlurs);
+ mSupportsBlur = supportsBlurs;
+ ALOGI_IF(!mSupportsBlur, "Disabling blur effects, they are not supported.");
property_get("ro.sf.blurs_are_expensive", value, "0");
mBlursAreExpensive = atoi(value);
- const size_t defaultListSize = MAX_LAYERS;
+ const size_t defaultListSize = ISurfaceComposer::MAX_LAYERS;
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
@@ -408,6 +406,7 @@
void SurfaceFlinger::binderDied(const wp<IBinder>& /* who */)
{
// the window manager died on us. prepare its eulogy.
+ mBootFinished = false;
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -524,6 +523,11 @@
void SurfaceFlinger::bootFinished()
{
+ if (mBootFinished == true) {
+ ALOGE("Extra call to bootFinished");
+ return;
+ }
+ mBootFinished = true;
if (mStartPropertySetThread->join() != NO_ERROR) {
ALOGE("Join StartPropertySetThread failed!");
}
@@ -563,6 +567,7 @@
postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS {
readPersistentProperties();
+ mPowerAdvisor.onBootFinished();
mBootStage = BootStage::FINISHED;
if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
@@ -618,7 +623,7 @@
.setUseColorManagerment(useColorManagement)
.setEnableProtectedContext(enable_protected_contents(false))
.setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mEnableBlurs)
+ .setSupportsBackgroundBlur(mSupportsBlur)
.setContextPriority(useContextPriority
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
@@ -700,6 +705,11 @@
property_get("persist.sys.sf.color_mode", value, "0");
mForceColorMode = static_cast<ColorMode>(atoi(value));
+
+ property_get("persist.sys.sf.disable_blurs", value, "0");
+ bool disableBlurs = atoi(value);
+ mDisableBlurs = disableBlurs;
+ ALOGI_IF(disableBlurs, "Disabling blur effects, user preference.");
}
void SurfaceFlinger::startBootAnim() {
@@ -857,6 +867,7 @@
const auto offsets = mPhaseConfiguration->getOffsetsForRefreshRate(config.refreshRate);
config.appVsyncOffset = offsets.late.app;
config.sfVsyncOffset = offsets.late.sf;
+ config.configGroup = hwConfig->getConfigGroup();
// This is how far in advance a buffer must be queued for
// presentation at a given time. If you want a buffer to appear
@@ -906,18 +917,27 @@
void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
ATRACE_CALL();
- auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+ auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
ALOGV("setDesiredActiveConfig(%s)", refreshRate.name.c_str());
- // Don't check against the current mode yet. Worst case we set the desired
- // config twice. However event generation config might have changed so we need to update it
- // accordingly
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
- mDesiredActiveConfig = info;
- mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+ if (mDesiredActiveConfigChanged) {
+ // If a config change is pending, just cache the latest request in
+ // mDesiredActiveConfig
+ const Scheduler::ConfigEvent prevConfig = mDesiredActiveConfig.event;
+ mDesiredActiveConfig = info;
+ mDesiredActiveConfig.event = mDesiredActiveConfig.event | prevConfig;
+ } else {
+ // Check is we are already at the desired config
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display || display->getActiveConfig() == refreshRate.configId) {
+ return;
+ }
- if (!mDesiredActiveConfigChanged) {
+ // Initiate a config change.
+ mDesiredActiveConfigChanged = true;
+ mDesiredActiveConfig = info;
+
// This will trigger HWC refresh without resetting the idle timer.
repaintEverythingForHWC();
// Start receiving vsync samples now, so that we can detect a period
@@ -930,7 +950,6 @@
mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
- mDesiredActiveConfigChanged = true;
if (mRefreshRateOverlay) {
mRefreshRateOverlay->changeRefreshRate(refreshRate);
@@ -979,7 +998,7 @@
mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
display->setActiveConfig(mUpcomingActiveConfig.configId);
- auto refreshRate =
+ auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
@@ -999,7 +1018,7 @@
mDesiredActiveConfig.event = Scheduler::ConfigEvent::None;
mDesiredActiveConfigChanged = false;
- auto const refreshRate =
+ const auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
mScheduler->resyncToHardwareVsync(true, refreshRate.vsyncPeriod);
mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
@@ -1010,7 +1029,7 @@
ATRACE_CALL();
ALOGV("performSetActiveConfig");
if (mCheckPendingFence) {
- if (previousFrameMissed()) {
+ if (previousFramePending()) {
// fence has not signaled yet. wait for the next invalidate
mEventQueue->invalidate();
return true;
@@ -1032,7 +1051,7 @@
desiredActiveConfig = mDesiredActiveConfig;
}
- auto refreshRate =
+ auto& refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig.configId);
ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.configId.value(),
refreshRate.name.c_str());
@@ -1069,7 +1088,9 @@
mUpcomingActiveConfig.configId.value(),
constraints, &outTimeline);
if (status != NO_ERROR) {
- LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+ // setActiveConfigWithConstraints may fail if a hotplug event is just about
+ // to be sent. We just log the error in this case.
+ ALOGW("setActiveConfigWithConstraints failed: %d", status);
return false;
}
@@ -1755,13 +1776,17 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
-bool SurfaceFlinger::previousFrameMissed(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
- ATRACE_CALL();
+sp<Fence> SurfaceFlinger::previousFrameFence() NO_THREAD_SAFETY_ANALYSIS {
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ return mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
+}
+
+bool SurfaceFlinger::previousFramePending(int graceTimeMs) NO_THREAD_SAFETY_ANALYSIS {
+ ATRACE_CALL();
+ const sp<Fence>& fence = previousFrameFence();
if (fence == Fence::NO_FENCE) {
return false;
@@ -1774,6 +1799,16 @@
return (fence->getStatus() == Fence::Status::Unsignaled);
}
+nsecs_t SurfaceFlinger::previousFramePresentTime() NO_THREAD_SAFETY_ANALYSIS {
+ const sp<Fence>& fence = previousFrameFence();
+
+ if (fence == Fence::NO_FENCE) {
+ return Fence::SIGNAL_TIME_INVALID;
+ }
+
+ return fence->getSignalTime();
+}
+
void SurfaceFlinger::populateExpectedPresentTime() {
DisplayStatInfo stats;
mScheduler->getDisplayStatInfo(&stats);
@@ -1791,6 +1826,7 @@
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
+ const nsecs_t lastExpectedPresentTime = mExpectedPresentTime.load();
populateExpectedPresentTime();
// When Backpressure propagation is enabled we want to give a small grace period
@@ -1801,17 +1837,41 @@
(mPropagateBackpressureClientComposition || !mHadClientComposition))
? 1
: 0;
- const TracedOrdinal<bool> frameMissed = {"FrameMissed",
- previousFrameMissed(
- graceTimeForPresentFenceMs)};
- const TracedOrdinal<bool> hwcFrameMissed = {"HwcFrameMissed",
+
+ // Pending frames may trigger backpressure propagation.
+ const TracedOrdinal<bool> framePending = {"PrevFramePending",
+ previousFramePending(
+ graceTimeForPresentFenceMs)};
+
+ // Frame missed counts for metrics tracking.
+ // A frame is missed if the prior frame is still pending. If no longer pending,
+ // then we still count the frame as missed if the predicted present time
+ // was further in the past than when the fence actually fired.
+
+ // Add some slop to correct for drift. This should generally be
+ // smaller than a typical frame duration, but should not be so small
+ // that it reports reasonable drift as a missed frame.
+ DisplayStatInfo stats;
+ mScheduler->getDisplayStatInfo(&stats);
+ const nsecs_t frameMissedSlop = stats.vsyncPeriod / 2;
+ const nsecs_t previousPresentTime = previousFramePresentTime();
+ const TracedOrdinal<bool> frameMissed =
+ {"PrevFrameMissed",
+ framePending ||
+ (previousPresentTime >= 0 &&
+ (lastExpectedPresentTime < previousPresentTime - frameMissedSlop))};
+ const TracedOrdinal<bool> hwcFrameMissed = {"PrevHwcFrameMissed",
mHadDeviceComposition && frameMissed};
- const TracedOrdinal<bool> gpuFrameMissed = {"GpuFrameMissed",
+ const TracedOrdinal<bool> gpuFrameMissed = {"PrevGpuFrameMissed",
mHadClientComposition && frameMissed};
if (frameMissed) {
mFrameMissedCount++;
mTimeStats->incrementMissedFrames();
+ if (mMissedFrameJankCount == 0) {
+ mMissedFrameJankStart = systemTime();
+ }
+ mMissedFrameJankCount++;
}
if (hwcFrameMissed) {
@@ -1822,7 +1882,7 @@
mGpuFrameMissedCount++;
}
- if (frameMissed && mPropagateBackpressure) {
+ if (framePending && mPropagateBackpressure) {
if ((hwcFrameMissed && !gpuFrameMissed) ||
mPropagateBackpressureClientComposition) {
signalLayerUpdate();
@@ -1830,6 +1890,40 @@
}
}
+ // Our jank window is always at least 100ms since we missed a
+ // frame...
+ static constexpr nsecs_t kMinJankyDuration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count();
+ // ...but if it's larger than 1s then we missed the trace cutoff.
+ static constexpr nsecs_t kMaxJankyDuration =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+ // If we're in a user build then don't push any atoms
+ if (!mIsUserBuild && mMissedFrameJankCount > 0) {
+ const auto displayDevice = getDefaultDisplayDeviceLocked();
+ // Only report jank when the display is on, as displays in DOZE
+ // power mode may operate at a different frame rate than is
+ // reported in their config, which causes noticeable (but less
+ // severe) jank.
+ if (displayDevice && displayDevice->getPowerMode() == HWC_POWER_MODE_NORMAL) {
+ const nsecs_t currentTime = systemTime();
+ const nsecs_t jankDuration = currentTime - mMissedFrameJankStart;
+ if (jankDuration > kMinJankyDuration && jankDuration < kMaxJankyDuration) {
+ ATRACE_NAME("Jank detected");
+ ALOGD("Detected janky event. Missed frames: %d", mMissedFrameJankCount);
+ const int32_t jankyDurationMillis = jankDuration / (1000 * 1000);
+ android::util::stats_write(android::util::DISPLAY_JANK_REPORTED,
+ jankyDurationMillis, mMissedFrameJankCount);
+ }
+
+ // We either reported a jank event or we missed the trace
+ // window, so clear counters here.
+ if (jankDuration > kMinJankyDuration) {
+ mMissedFrameJankCount = 0;
+ mMissedFrameJankStart = 0;
+ }
+ }
+ }
+
// Now that we're going to make it to the handleMessageTransaction()
// call below it's safe to call updateVrFlinger(), which will
// potentially trigger a display handoff.
@@ -2325,16 +2419,18 @@
}
sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal(
- const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
- const DisplayDeviceState& state, const sp<compositionengine::DisplaySurface>& dispSurface,
+ const wp<IBinder>& displayToken,
+ std::shared_ptr<compositionengine::Display> compositionDisplay,
+ const DisplayDeviceState& state,
+ const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer) {
- DisplayDeviceCreationArgs creationArgs(this, displayToken, displayId);
+ auto displayId = compositionDisplay->getDisplayId();
+ DisplayDeviceCreationArgs creationArgs(this, displayToken, compositionDisplay);
creationArgs.sequenceId = state.sequenceId;
creationArgs.isSecure = state.isSecure;
- creationArgs.displaySurface = dispSurface;
+ creationArgs.displaySurface = displaySurface;
creationArgs.hasWideColorGamut = false;
creationArgs.supportedPerFrameMetadata = 0;
- creationArgs.powerAdvisor = displayId ? &mPowerAdvisor : nullptr;
if (const auto& physical = state.physical) {
creationArgs.connectionType = physical->type;
@@ -2379,7 +2475,7 @@
// virtual displays are always considered enabled
creationArgs.initialPowerMode = state.isVirtual() ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
- sp<DisplayDevice> display = getFactory().createDisplayDevice(std::move(creationArgs));
+ sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs);
if (maxFrameBufferAcquiredBuffers >= 3) {
nativeWindowSurface->preallocateBuffers();
@@ -2408,6 +2504,140 @@
return display;
}
+void SurfaceFlinger::processDisplayAdded(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& state) {
+ int width = 0;
+ int height = 0;
+ ui::PixelFormat pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_UNKNOWN);
+ if (state.physical) {
+ const auto& activeConfig =
+ getCompositionEngine().getHwComposer().getActiveConfig(state.physical->id);
+ width = activeConfig->getWidth();
+ height = activeConfig->getHeight();
+ pixelFormat = static_cast<ui::PixelFormat>(PIXEL_FORMAT_RGBA_8888);
+ } else if (state.surface != nullptr) {
+ int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
+ ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
+ status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
+ ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
+ int intPixelFormat;
+ status = state.surface->query(NATIVE_WINDOW_FORMAT, &intPixelFormat);
+ ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
+ pixelFormat = static_cast<ui::PixelFormat>(intPixelFormat);
+ } else {
+ // Virtual displays without a surface are dormant:
+ // they have external state (layer stack, projection,
+ // etc.) but no internal state (i.e. a DisplayDevice).
+ return;
+ }
+
+ compositionengine::DisplayCreationArgsBuilder builder;
+ if (const auto& physical = state.physical) {
+ builder.setPhysical({physical->id, physical->type});
+ }
+ builder.setPixels(ui::Size(width, height));
+ builder.setPixelFormat(pixelFormat);
+ builder.setIsSecure(state.isSecure);
+ builder.setLayerStackId(state.layerStack);
+ builder.setPowerAdvisor(&mPowerAdvisor);
+ builder.setUseHwcVirtualDisplays(mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer());
+ builder.setName(state.displayName);
+ const auto compositionDisplay = getCompositionEngine().createDisplay(builder.build());
+
+ sp<compositionengine::DisplaySurface> displaySurface;
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferProducer> bqProducer;
+ sp<IGraphicBufferConsumer> bqConsumer;
+ getFactory().createBufferQueue(&bqProducer, &bqConsumer, /*consumerIsSurfaceFlinger =*/false);
+
+ std::optional<DisplayId> displayId = compositionDisplay->getId();
+
+ if (state.isVirtual()) {
+ sp<VirtualDisplaySurface> vds =
+ new VirtualDisplaySurface(getHwComposer(), displayId, state.surface, bqProducer,
+ bqConsumer, state.displayName);
+
+ displaySurface = vds;
+ producer = vds;
+ } else {
+ ALOGE_IF(state.surface != nullptr,
+ "adding a supported display, but rendering "
+ "surface is provided (%p), ignoring it",
+ state.surface.get());
+
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ displaySurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer,
+ maxGraphicsWidth, maxGraphicsHeight);
+ producer = bqProducer;
+ }
+
+ if (displaySurface != nullptr) {
+ mDisplays.emplace(displayToken,
+ setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer));
+ if (!state.isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, true);
+ }
+
+ const auto displayDevice = mDisplays[displayToken];
+ if (displayDevice->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
+ displayDevice->getHeight());
+ }
+ }
+}
+
+void SurfaceFlinger::processDisplayRemoved(const wp<IBinder>& displayToken) {
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ // Save display ID before disconnecting.
+ const auto displayId = display->getId();
+ display->disconnect();
+
+ if (!display->isVirtual()) {
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ dispatchDisplayHotplugEvent(displayId->value, false);
+ }
+ }
+
+ mDisplays.erase(displayToken);
+}
+
+void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& currentState,
+ const DisplayDeviceState& drawingState) {
+ const sp<IBinder> currentBinder = IInterface::asBinder(currentState.surface);
+ const sp<IBinder> drawingBinder = IInterface::asBinder(drawingState.surface);
+ if (currentBinder != drawingBinder) {
+ // changing the surface is like destroying and recreating the DisplayDevice
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ display->disconnect();
+ }
+ mDisplays.erase(displayToken);
+ processDisplayAdded(displayToken, currentState);
+ return;
+ }
+
+ if (const auto display = getDisplayDeviceLocked(displayToken)) {
+ if (currentState.layerStack != drawingState.layerStack) {
+ display->setLayerStack(currentState.layerStack);
+ }
+ if ((currentState.orientation != drawingState.orientation) ||
+ (currentState.viewport != drawingState.viewport) ||
+ (currentState.frame != drawingState.frame)) {
+ display->setProjection(currentState.orientation, currentState.viewport,
+ currentState.frame);
+ }
+ if (currentState.width != drawingState.width ||
+ currentState.height != drawingState.height) {
+ display->setDisplaySize(currentState.width, currentState.height);
+ if (display->isPrimary()) {
+ mScheduler->onPrimaryDisplayAreaChanged(currentState.width * currentState.height);
+ }
+ }
+ }
+}
+
void SurfaceFlinger::processDisplayChangesLocked() {
// here we take advantage of Vector's copy-on-write semantics to
// improve performance by skipping the transaction entirely when
@@ -2416,142 +2646,31 @@
const KeyedVector<wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
if (!curr.isIdenticalTo(draw)) {
mVisibleRegionsDirty = true;
- const size_t cc = curr.size();
- size_t dc = draw.size();
// find the displays that were removed
// (ie: in drawing state but not in current state)
// also handle displays that changed
// (ie: displays that are in both lists)
- for (size_t i = 0; i < dc;) {
- const ssize_t j = curr.indexOfKey(draw.keyAt(i));
+ for (size_t i = 0; i < draw.size(); i++) {
+ const wp<IBinder>& displayToken = draw.keyAt(i);
+ const ssize_t j = curr.indexOfKey(displayToken);
if (j < 0) {
// in drawing state but not in current state
- if (const auto display = getDisplayDeviceLocked(draw.keyAt(i))) {
- // Save display ID before disconnecting.
- const auto displayId = display->getId();
- display->disconnect();
-
- if (!display->isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, false);
- }
- }
-
- mDisplays.erase(draw.keyAt(i));
+ processDisplayRemoved(displayToken);
} else {
// this display is in both lists. see if something changed.
- const DisplayDeviceState& state(curr[j]);
- const wp<IBinder>& displayToken = curr.keyAt(j);
- const sp<IBinder> state_binder = IInterface::asBinder(state.surface);
- const sp<IBinder> draw_binder = IInterface::asBinder(draw[i].surface);
- if (state_binder != draw_binder) {
- // changing the surface is like destroying and
- // recreating the DisplayDevice, so we just remove it
- // from the drawing state, so that it get re-added
- // below.
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- display->disconnect();
- }
- mDisplays.erase(displayToken);
- mDrawingState.displays.removeItemsAt(i);
- dc--;
- // at this point we must loop to the next item
- continue;
- }
-
- if (const auto display = getDisplayDeviceLocked(displayToken)) {
- if (state.layerStack != draw[i].layerStack) {
- display->setLayerStack(state.layerStack);
- }
- if ((state.orientation != draw[i].orientation) ||
- (state.viewport != draw[i].viewport) || (state.frame != draw[i].frame)) {
- display->setProjection(state.orientation, state.viewport, state.frame);
- }
- if (state.width != draw[i].width || state.height != draw[i].height) {
- display->setDisplaySize(state.width, state.height);
- if (display->isPrimary()) {
- mScheduler->onPrimaryDisplayAreaChanged(state.width * state.height);
- }
- }
- }
+ const DisplayDeviceState& currentState = curr[j];
+ const DisplayDeviceState& drawingState = draw[i];
+ processDisplayChanged(displayToken, currentState, drawingState);
}
- ++i;
}
// find displays that were added
// (ie: in current state but not in drawing state)
- for (size_t i = 0; i < cc; i++) {
- if (draw.indexOfKey(curr.keyAt(i)) < 0) {
- const DisplayDeviceState& state(curr[i]);
-
- sp<compositionengine::DisplaySurface> dispSurface;
- sp<IGraphicBufferProducer> producer;
- sp<IGraphicBufferProducer> bqProducer;
- sp<IGraphicBufferConsumer> bqConsumer;
- getFactory().createBufferQueue(&bqProducer, &bqConsumer, false);
-
- std::optional<DisplayId> displayId;
- if (state.isVirtual()) {
- // Virtual displays without a surface are dormant:
- // they have external state (layer stack, projection,
- // etc.) but no internal state (i.e. a DisplayDevice).
- if (state.surface != nullptr) {
- // Allow VR composer to use virtual displays.
- if (mUseHwcVirtualDisplays || getHwComposer().isUsingVrComposer()) {
- int width = 0;
- int status = state.surface->query(NATIVE_WINDOW_WIDTH, &width);
- ALOGE_IF(status != NO_ERROR, "Unable to query width (%d)", status);
- int height = 0;
- status = state.surface->query(NATIVE_WINDOW_HEIGHT, &height);
- ALOGE_IF(status != NO_ERROR, "Unable to query height (%d)", status);
- int intFormat = 0;
- status = state.surface->query(NATIVE_WINDOW_FORMAT, &intFormat);
- ALOGE_IF(status != NO_ERROR, "Unable to query format (%d)", status);
- auto format = static_cast<ui::PixelFormat>(intFormat);
-
- displayId =
- getHwComposer().allocateVirtualDisplay(width, height, &format);
- }
-
- // TODO: Plumb requested format back up to consumer
-
- sp<VirtualDisplaySurface> vds =
- new VirtualDisplaySurface(getHwComposer(), displayId, state.surface,
- bqProducer, bqConsumer,
- state.displayName);
-
- dispSurface = vds;
- producer = vds;
- }
- } else {
- ALOGE_IF(state.surface != nullptr,
- "adding a supported display, but rendering "
- "surface is provided (%p), ignoring it",
- state.surface.get());
-
- LOG_FATAL_IF(!state.physical);
- displayId = state.physical->id;
- dispSurface = new FramebufferSurface(getHwComposer(), *displayId, bqConsumer);
- producer = bqProducer;
- }
-
- const wp<IBinder>& displayToken = curr.keyAt(i);
- if (dispSurface != nullptr) {
- mDisplays.emplace(displayToken,
- setupNewDisplayDeviceInternal(displayToken, displayId, state,
- dispSurface, producer));
- if (!state.isVirtual()) {
- LOG_ALWAYS_FATAL_IF(!displayId);
- dispatchDisplayHotplugEvent(displayId->value, true);
- }
-
- const auto displayDevice = mDisplays[displayToken];
- if (displayDevice->isPrimary()) {
- mScheduler->onPrimaryDisplayAreaChanged(displayDevice->getWidth() *
- displayDevice->getHeight());
- }
- }
+ for (size_t i = 0; i < curr.size(); i++) {
+ const wp<IBinder>& displayToken = curr.keyAt(i);
+ if (draw.indexOfKey(displayToken) < 0) {
+ processDisplayAdded(displayToken, curr[i]);
}
}
}
@@ -2717,6 +2836,19 @@
void SurfaceFlinger::updateInputWindowInfo() {
std::vector<InputWindowInfo> inputHandles;
+ // We use a simple caching algorithm here. mInputDirty begins as true,
+ // after we call setInputWindows we set it to false, so
+ // in the future we wont call it again.. We set input dirty to true again
+ // when any layer that hasInput() has a transaction performed on it
+ // or when any parent or relative parent of such a layer has a transaction
+ // performed on it. Not all of these transactions will really result in
+ // input changes but all input changes will spring from these transactions
+ // so the cache is safe but not optimal. It seems like it might be annoyingly
+ // costly to cache and comapre the actual InputWindowHandle vector though.
+ if (!mInputDirty && !mInputWindowCommands.syncInputWindows) {
+ return;
+ }
+
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (layer->hasInput()) {
// When calculating the screen bounds we ignore the transparent region since it may
@@ -2728,10 +2860,12 @@
mInputFlinger->setInputWindows(inputHandles,
mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener
: nullptr);
+
+ mInputDirty = false;
}
void SurfaceFlinger::commitInputWindowCommands() {
- mInputWindowCommands = mPendingInputWindowCommands;
+ mInputWindowCommands.merge(mPendingInputWindowCommands);
mPendingInputWindowCommands.clear();
}
@@ -3006,9 +3140,9 @@
parent = parentLayer;
}
- if (mNumLayers >= MAX_LAYERS) {
+ if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
- MAX_LAYERS);
+ ISurfaceComposer::MAX_LAYERS);
return NO_MEMORY;
}
@@ -3498,7 +3632,7 @@
if (layer->setCornerRadius(s.cornerRadius))
flags |= eTraversalNeeded;
}
- if (what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ if (what & layer_state_t::eBackgroundBlurRadiusChanged && !mDisableBlurs) {
if (layer->setBackgroundBlurRadius(s.backgroundBlurRadius)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eLayerStackChanged) {
@@ -3668,10 +3802,6 @@
uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& inputWindowCommands) {
uint32_t flags = 0;
- if (!inputWindowCommands.transferTouchFocusCommands.empty()) {
- flags |= eTraversalNeeded;
- }
-
if (inputWindowCommands.syncInputWindows) {
flags |= eTraversalNeeded;
}
@@ -4397,14 +4527,20 @@
result.append("\n");
}
-LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
+LayersProto SurfaceFlinger::dumpDrawingStateProto(
+ uint32_t traceFlags, const sp<const DisplayDevice>& displayDevice) const {
LayersProto layersProto;
for (const sp<Layer>& layer : mDrawingState.layersSortedByZ) {
- layer->writeToProto(layersProto, traceFlags);
+ layer->writeToProto(layersProto, traceFlags, displayDevice);
}
+
return layersProto;
}
+void SurfaceFlinger::dumpHwc(std::string& result) const {
+ getHwComposer().dump(result);
+}
+
void SurfaceFlinger::dumpOffscreenLayersProto(LayersProto& layersProto, uint32_t traceFlags) const {
// Add a fake invisible root layer to the proto output and parent all the offscreen layers to
// it.
@@ -4419,14 +4555,18 @@
rootProto->add_children(offscreenLayer->sequence);
// Add layer
- LayerProto* layerProto = offscreenLayer->writeToProto(layersProto, traceFlags);
+ LayerProto* layerProto =
+ offscreenLayer->writeToProto(layersProto, traceFlags, nullptr /*device*/);
layerProto->set_parent(offscreenRootLayerId);
}
}
LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
LayersProto layersProto;
- postMessageSync(new LambdaMessage([&]() { layersProto = dumpDrawingStateProto(traceFlags); }));
+ postMessageSync(new LambdaMessage([&]() {
+ const auto& displayDevice = getDefaultDisplayDeviceLocked();
+ layersProto = dumpDrawingStateProto(traceFlags, displayDevice);
+ }));
return layersProto;
}
@@ -5068,7 +5208,7 @@
n = data.readInt32();
if (n == 1 && !mRefreshRateOverlay) {
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
- auto current = mRefreshRateConfigs->getCurrentRefreshRate();
+ auto& current = mRefreshRateConfigs->getCurrentRefreshRate();
mRefreshRateOverlay->changeRefreshRate(current);
} else if (n == 0) {
mRefreshRateOverlay.reset();
@@ -5106,6 +5246,36 @@
mEventQueue->invalidate();
}
+void SurfaceFlinger::kernelTimerChanged(bool expired) {
+ static bool updateOverlay =
+ property_get_bool("debug.sf.kernel_idle_timer_update_overlay", true);
+ if (!updateOverlay || !mRefreshRateOverlay) return;
+
+ // Update the overlay on the main thread to avoid race conditions with
+ // mRefreshRateConfigs->getCurrentRefreshRate()
+ postMessageAsync(new LambdaMessage([this, expired]() NO_THREAD_SAFETY_ANALYSIS {
+ if (mRefreshRateOverlay) {
+ const auto kernelTimerEnabled = property_get_bool(KERNEL_IDLE_TIMER_PROP, false);
+ const bool timerExpired = kernelTimerEnabled && expired;
+ const auto& current = [this]() -> const RefreshRate& {
+ std::lock_guard<std::mutex> lock(mActiveConfigLock);
+ if (mDesiredActiveConfigChanged) {
+ return mRefreshRateConfigs->getRefreshRateFromConfigId(
+ mDesiredActiveConfig.configId);
+ }
+
+ return mRefreshRateConfigs->getCurrentRefreshRate();
+ }();
+ const auto& min = mRefreshRateConfigs->getMinRefreshRate();
+
+ if (current != min) {
+ mRefreshRateOverlay->changeRefreshRate(timerExpired ? min : current);
+ mEventQueue->invalidate();
+ }
+ }
+ }));
+}
+
// A simple RAII class to disconnect from an ANativeWindow* when it goes out of scope
class WindowDisconnector {
public:
@@ -5668,6 +5838,7 @@
Mutex::Autolock _l(mStateLock);
mPendingSyncInputWindows = false;
+
mTransactionCV.broadcast();
}
@@ -5767,7 +5938,7 @@
display->getActiveConfig(), vsyncPeriod);
auto configId = mScheduler->getPreferredConfigId();
- auto preferredRefreshRate = configId
+ auto& preferredRefreshRate = configId
? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
// NOTE: Choose the default config ID, if Scheduler doesn't have one in mind.
: mRefreshRateConfigs->getRefreshRateFromConfigId(defaultConfig);
@@ -5934,18 +6105,22 @@
return BAD_VALUE;
}
- Mutex::Autolock lock(mStateLock);
- if (authenticateSurfaceTextureLocked(surface)) {
- sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
- if (layer->setFrameRate(
- Layer::FrameRate(frameRate,
- Layer::FrameRate::convertCompatibility(compatibility)))) {
- setTransactionFlags(eTraversalNeeded);
+ postMessageAsync(new LambdaMessage([=]() NO_THREAD_SAFETY_ANALYSIS {
+ Mutex::Autolock lock(mStateLock);
+ if (authenticateSurfaceTextureLocked(surface)) {
+ sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
+ if (layer->setFrameRate(
+ Layer::FrameRate(frameRate,
+ Layer::FrameRate::convertCompatibility(compatibility)))) {
+ setTransactionFlags(eTraversalNeeded);
+ }
+ } else {
+ ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
+ return BAD_VALUE;
}
- } else {
- ALOGE("Attempt to set frame rate on an unrecognized IGraphicBufferProducer");
- return BAD_VALUE;
- }
+ return NO_ERROR;
+ }));
+
return NO_ERROR;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index c79621b..6ab1fcf 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -42,6 +42,7 @@
#include <system/graphics.h>
#include <ui/FenceTime.h>
#include <ui/PixelFormat.h>
+#include <ui/Size.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -225,6 +226,11 @@
// FramebufferSurface
static int64_t maxFrameBufferAcquiredBuffers;
+ // Controls the maximum width and height in pixels that the graphics pipeline can support for
+ // GPU fallback composition. For example, 8k devices with 4k GPUs, or 4k devices with 2k GPUs.
+ static uint32_t maxGraphicsWidth;
+ static uint32_t maxGraphicsHeight;
+
// Indicate if a device has wide color gamut display. This is typically
// found on devices with wide color gamut (e.g. Display-P3) display.
static bool hasWideColorDisplay;
@@ -351,7 +357,6 @@
// every half hour.
enum { LOG_FRAME_STATS_PERIOD = 30*60*60 };
- static const size_t MAX_LAYERS = 4096;
static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
protected:
@@ -524,6 +529,8 @@
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
// force full composition on all displays without resetting the scheduler idle timer.
void repaintEverythingForHWC() override;
+ // Called when kernel idle timer has expired. Used to update the refresh rate overlay.
+ void kernelTimerChanged(bool expired) override;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -810,11 +817,17 @@
* Display management
*/
sp<DisplayDevice> setupNewDisplayDeviceInternal(
- const wp<IBinder>& displayToken, const std::optional<DisplayId>& displayId,
+ const wp<IBinder>& displayToken,
+ std::shared_ptr<compositionengine::Display> compositionDisplay,
const DisplayDeviceState& state,
- const sp<compositionengine::DisplaySurface>& dispSurface,
+ const sp<compositionengine::DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer);
void processDisplayChangesLocked();
+ void processDisplayAdded(const wp<IBinder>& displayToken, const DisplayDeviceState& state);
+ void processDisplayRemoved(const wp<IBinder>& displayToken);
+ void processDisplayChanged(const wp<IBinder>& displayToken,
+ const DisplayDeviceState& currentState,
+ const DisplayDeviceState& drawingState);
void processDisplayHotplugEventsLocked();
void dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected);
@@ -831,7 +844,21 @@
bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
- bool previousFrameMissed(int graceTimeMs = 0);
+ // Gets the fence for the previous frame.
+ // Must be called on the main thread.
+ sp<Fence> previousFrameFence();
+
+ // Whether the previous frame has not yet been presented to the display.
+ // If graceTimeMs is positive, this method waits for at most the provided
+ // grace period before reporting if the frame missed.
+ // Must be called on the main thread.
+ bool previousFramePending(int graceTimeMs = 0);
+
+ // Returns the previous time that the frame was presented. If the frame has
+ // not been presented yet, then returns Fence::SIGNAL_TIME_PENDING. If there
+ // is no pending frame, then returns Fence::SIGNAL_TIME_INVALID.
+ // Must be called on the main thread.
+ nsecs_t previousFramePresentTime();
// Populates the expected present time for this frame. For negative offsets, performs a
// correction using the predicted vsync for the next frame instead.
@@ -915,9 +942,12 @@
void dumpDisplayIdentificationData(std::string& result) const;
void dumpRawDisplayIdentificationData(const DumpArgs&, std::string& result) const;
void dumpWideColorInfo(std::string& result) const;
- LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ LayersProto dumpDrawingStateProto(uint32_t traceFlags = SurfaceTracing::TRACE_ALL,
+ const sp<const DisplayDevice>& displayDevice = nullptr) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ // Dumps state from HW Composer
+ void dumpHwc(std::string& result) const;
LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
@@ -971,7 +1001,7 @@
// Can't be unordered_set because wp<> isn't hashable
std::set<wp<IBinder>> mGraphicBufferProducerList;
- size_t mMaxGraphicBufferProducerListSize = MAX_LAYERS;
+ size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
// protected by mStateLock (but we could use another lock)
bool mLayersRemoved = false;
@@ -982,6 +1012,7 @@
// constant members (no synchronization needed for access)
const nsecs_t mBootTime = systemTime();
bool mGpuToCpuSupported = false;
+ bool mIsUserBuild = true;
// Can only accessed from the main thread, these members
// don't need synchronization
@@ -1043,8 +1074,10 @@
const std::shared_ptr<TimeStats> mTimeStats;
const std::unique_ptr<FrameTracer> mFrameTracer;
bool mUseHwcVirtualDisplays = false;
+ // If blurs should be enabled on this device.
+ bool mSupportsBlur = false;
// Disable blurs, for debugging
- bool mEnableBlurs = false;
+ std::atomic<bool> mDisableBlurs = false;
// If blurs are considered expensive and should require high GPU frequency.
bool mBlursAreExpensive = false;
std::atomic<uint32_t> mFrameMissedCount = 0;
@@ -1118,6 +1151,10 @@
// to linkToDeath
sp<IBinder> mWindowManager;
+ // We want to avoid multiple calls to BOOT_FINISHED as they come in on
+ // different threads without a lock and could trigger unsynchronized writes to
+ // to mWindowManager or mInputFlinger
+ std::atomic<bool> mBootFinished = false;
std::unique_ptr<dvr::VrFlinger> mVrFlinger;
std::atomic<bool> mVrFlingerRequestsDisplay = false;
@@ -1225,6 +1262,16 @@
// Flags to capture the state of Vsync in HWC
HWC2::Vsync mHWCVsyncState = HWC2::Vsync::Disable;
HWC2::Vsync mHWCVsyncPendingState = HWC2::Vsync::Disable;
+
+ // Fields tracking the current jank event: when it started and how many
+ // janky frames there are.
+ nsecs_t mMissedFrameJankStart = 0;
+ int32_t mMissedFrameJankCount = 0;
+
+ // See updateInputWindowInfo() for details
+ std::atomic<bool> mInputDirty = true;
+ void dirtyInput() { mInputDirty = true; }
+ bool inputDirty() { return mInputDirty; }
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index d49133d..ddd20a5 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -33,6 +33,7 @@
#include "NativeWindowSurface.h"
#include "StartPropertySetThread.h"
#include "SurfaceFlingerDefaultFactory.h"
+#include "SurfaceFlingerProperties.h"
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
@@ -76,8 +77,8 @@
SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
ISchedulerCallback& schedulerCallback) {
return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback,
- property_get_bool("debug.sf.use_content_detection_v2",
- true));
+ property_get_bool("debug.sf.use_content_detection_v2", true),
+ sysprop::use_content_detection_for_refresh_rate(false));
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
@@ -90,8 +91,8 @@
return new StartPropertySetThread(timestampPropertyValue);
}
-sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) {
- return new DisplayDevice(std::move(creationArgs));
+sp<DisplayDevice> DefaultFactory::createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) {
+ return new DisplayDevice(creationArgs);
}
sp<GraphicBuffer> DefaultFactory::createGraphicBuffer(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 89194c7..bd40cfb 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -37,7 +37,7 @@
ISchedulerCallback&) override;
std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
- sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
+ sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
std::string requestorName) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 209bd0c..6f4fcc6 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -83,7 +83,7 @@
virtual sp<StartPropertySetThread> createStartPropertySetThread(
bool timestampPropertyValue) = 0;
- virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) = 0;
+ virtual sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) = 0;
virtual sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height,
PixelFormat format, uint32_t layerCount,
uint64_t usage, std::string requestorName) = 0;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index f3352a5..9d78702 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -70,6 +70,22 @@
defaultValue);
}
+int32_t max_graphics_width(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_graphics_width();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
+int32_t max_graphics_height(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::max_graphics_height();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
bool has_wide_color_display(bool defaultValue) {
auto temp = SurfaceFlingerProperties::has_wide_color_display();
if (temp.has_value()) {
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 12c711a..c63adfe 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -37,6 +37,9 @@
int64_t max_frame_buffer_acquired_buffers(int64_t defaultValue);
+int32_t max_graphics_width(int32_t defaultValue);
+int32_t max_graphics_height(int32_t defaultValue);
+
bool has_wide_color_display(bool defaultValue);
bool running_without_sync_framework(bool defaultValue);
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 1f9d46c..5b3cd69 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -524,11 +524,11 @@
deletion->set_id(getLayerId(layer));
}
-void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer,
+void SurfaceInterceptor::addBufferUpdateLocked(Increment* increment, int32_t layerId,
uint32_t width, uint32_t height, uint64_t frameNumber)
{
BufferUpdate* update(increment->mutable_buffer_update());
- update->set_id(getLayerId(layer));
+ update->set_id(layerId);
update->set_w(width);
update->set_h(height);
update->set_frame_number(frameNumber);
@@ -644,15 +644,22 @@
addSurfaceDeletionLocked(createTraceIncrementLocked(), layer);
}
-void SurfaceInterceptor::saveBufferUpdate(const sp<const Layer>& layer, uint32_t width,
+/**
+ * Here we pass the layer by ID instead of by sp<> since this is called without
+ * holding the state-lock from a Binder thread. If we required the caller
+ * to pass 'this' by sp<> the temporary sp<> constructed could end up
+ * being the last reference and we might accidentally destroy the Layer
+ * from this binder thread.
+ */
+void SurfaceInterceptor::saveBufferUpdate(int32_t layerId, uint32_t width,
uint32_t height, uint64_t frameNumber)
{
- if (!mEnabled || layer == nullptr) {
+ if (!mEnabled) {
return;
}
ATRACE_CALL();
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- addBufferUpdateLocked(createTraceIncrementLocked(), layer, width, height, frameNumber);
+ addBufferUpdateLocked(createTraceIncrementLocked(), layerId, width, height, frameNumber);
}
void SurfaceInterceptor::saveVSyncEvent(nsecs_t timestamp) {
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index a665f62..896bdcc 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -67,7 +67,7 @@
// Intercept surface data
virtual void saveSurfaceCreation(const sp<const Layer>& layer) = 0;
virtual void saveSurfaceDeletion(const sp<const Layer>& layer) = 0;
- virtual void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+ virtual void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
uint64_t frameNumber) = 0;
// Intercept display data
@@ -102,7 +102,7 @@
// Intercept surface data
void saveSurfaceCreation(const sp<const Layer>& layer) override;
void saveSurfaceDeletion(const sp<const Layer>& layer) override;
- void saveBufferUpdate(const sp<const Layer>& layer, uint32_t width, uint32_t height,
+ void saveBufferUpdate(int32_t layerId, uint32_t width, uint32_t height,
uint64_t frameNumber) override;
// Intercept display data
@@ -130,7 +130,7 @@
Increment* createTraceIncrementLocked();
void addSurfaceCreationLocked(Increment* increment, const sp<const Layer>& layer);
void addSurfaceDeletionLocked(Increment* increment, const sp<const Layer>& layer);
- void addBufferUpdateLocked(Increment* increment, const sp<const Layer>& layer, uint32_t width,
+ void addBufferUpdateLocked(Increment* increment, int32_t layerId, uint32_t width,
uint32_t height, uint64_t frameNumber);
void addVSyncUpdateLocked(Increment* increment, nsecs_t timestamp);
void addDisplayCreationLocked(Increment* increment, const DisplayDeviceState& info);
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index eb5c7de..a9c3332 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -45,19 +45,21 @@
}
void SurfaceTracing::addFirstEntry() {
+ const auto displayDevice = mFlinger.getDefaultDisplayDevice();
LayersTraceProto entry;
{
std::scoped_lock lock(mSfLock);
- entry = traceLayersLocked("tracing.enable");
+ entry = traceLayersLocked("tracing.enable", displayDevice);
}
addTraceToBuffer(entry);
}
LayersTraceProto SurfaceTracing::traceWhenNotified() {
+ const auto displayDevice = mFlinger.getDefaultDisplayDevice();
std::unique_lock<std::mutex> lock(mSfLock);
mCanStartTrace.wait(lock);
android::base::ScopedLockAssertion assumeLock(mSfLock);
- LayersTraceProto entry = traceLayersLocked(mWhere);
+ LayersTraceProto entry = traceLayersLocked(mWhere, displayDevice);
lock.unlock();
return entry;
}
@@ -127,7 +129,12 @@
}
status_t SurfaceTracing::writeToFile() {
- mThread.join();
+ std::thread thread;
+ {
+ std::scoped_lock lock(mTraceLock);
+ thread = std::move(mThread);
+ }
+ thread.join();
return mLastErr;
}
@@ -160,16 +167,23 @@
mTraceFlags = flags;
}
-LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
+LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where,
+ const sp<const DisplayDevice>& displayDevice) {
ATRACE_CALL();
LayersTraceProto entry;
entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags));
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mTraceFlags, displayDevice));
mFlinger.dumpOffscreenLayersProto(layers);
entry.mutable_layers()->Swap(&layers);
+ if (mTraceFlags & SurfaceTracing::TRACE_HWC) {
+ std::string hwcDump;
+ mFlinger.dumpHwc(hwcDump);
+ entry.set_hwc_blob(hwcDump);
+ }
+
return entry;
}
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 18524f0..83872ed 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -16,17 +16,19 @@
#pragma once
+#include <android-base/thread_annotations.h>
#include <layerproto/LayerProtoHeader.h>
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
-#include <android-base/thread_annotations.h>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
+#include "DisplayDevice.h"
+
using namespace android::surfaceflinger;
namespace android {
@@ -56,6 +58,7 @@
TRACE_CRITICAL = 1 << 0,
TRACE_INPUT = 1 << 1,
TRACE_EXTRA = 1 << 2,
+ TRACE_HWC = 1 << 3,
TRACE_ALL = 0xffffffff
};
void setTraceFlags(uint32_t flags);
@@ -84,19 +87,21 @@
void mainLoop();
void addFirstEntry();
LayersTraceProto traceWhenNotified();
- LayersTraceProto traceLayersLocked(const char* where) REQUIRES(mSfLock);
+ LayersTraceProto traceLayersLocked(const char* where,
+ const sp<const DisplayDevice>& displayDevice)
+ REQUIRES(mSfLock);
// Returns true if trace is enabled.
bool addTraceToBuffer(LayersTraceProto& entry);
void writeProtoFileLocked() REQUIRES(mTraceLock);
- const SurfaceFlinger& mFlinger;
+ SurfaceFlinger& mFlinger;
status_t mLastErr = NO_ERROR;
std::thread mThread;
std::condition_variable mCanStartTrace;
std::mutex& mSfLock;
- uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_ALL;
+ uint32_t mTraceFlags GUARDED_BY(mSfLock) = TRACE_CRITICAL | TRACE_INPUT;
const char* mWhere GUARDED_BY(mSfLock) = "";
mutable std::mutex mTraceLock;
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 8038eba..4f59bf2 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -188,17 +188,16 @@
TimeStats::~TimeStats() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->unregisterStatsPullAtomCallback(
- android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
- mStatsDelegate->unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO);
+ mStatsDelegate->clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO);
}
void TimeStats::onBootFinished() {
std::lock_guard<std::mutex> lock(mMutex);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
- mStatsDelegate->registerStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
- TimeStats::pullAtomCallback, nullptr, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
+ mStatsDelegate->setStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO,
+ nullptr, TimeStats::pullAtomCallback, this);
}
void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, std::string& result) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index ddebeb1..f9bd90b 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -165,15 +165,15 @@
virtual AStatsEvent* addStatsEventToPullData(AStatsEventList* data) {
return AStatsEventList_addStatsEvent(data);
}
- virtual void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata* metadata,
- void* cookie) {
- return AStatsManager_registerPullAtomCallback(atom_tag, callback, metadata, cookie);
+ virtual void setStatsPullAtomCallback(int32_t atom_tag,
+ AStatsManager_PullAtomMetadata* metadata,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) {
+ return AStatsManager_setPullAtomCallback(atom_tag, metadata, callback, cookie);
}
- virtual void unregisterStatsPullAtomCallback(int32_t atom_tag) {
- return AStatsManager_unregisterPullAtomCallback(atom_tag);
+ virtual void clearStatsPullAtomCallback(int32_t atom_tag) {
+ return AStatsManager_clearPullAtomCallback(atom_tag);
}
virtual void statsEventSetAtomId(AStatsEvent* event, uint32_t atom_id) {
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index 8afe503..7f1f542 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -9,6 +9,23 @@
repeated LayerProto layers = 1;
}
+// Must match definition in the IComposerClient HAL
+enum HwcCompositionType {
+ // Invalid composition type
+ INVALID = 0;
+ // Layer was composited by the client into the client target buffer
+ CLIENT = 1;
+ // Layer was composited by the device through hardware overlays
+ DEVICE = 2;
+ // Layer was composited by the device using a color
+ SOLID_COLOR = 3;
+ // Similar to DEVICE, but the layer position may have been asynchronously set
+ // through setCursorPosition
+ CURSOR = 4;
+ // Layer was composited by the device via a sideband stream.
+ SIDEBAND = 5;
+}
+
// Information about each layer.
message LayerProto {
// unique id per layer.
@@ -73,7 +90,7 @@
int32 window_type = 33 [deprecated=true];
int32 app_id = 34 [deprecated=true];
// The layer's composition type
- int32 hwc_composition_type = 35;
+ HwcCompositionType hwc_composition_type = 35;
// If it's a buffer layer, indicate if the content is protected
bool is_protected = 36;
// Current frame number being rendered.
@@ -190,4 +207,4 @@
message ColorTransformProto {
// This will be a 4x4 matrix of float values
repeated float val = 1;
-}
\ No newline at end of file
+}
diff --git a/services/surfaceflinger/layerproto/layerstrace.proto b/services/surfaceflinger/layerproto/layerstrace.proto
index bee17d2..ac33a0e 100644
--- a/services/surfaceflinger/layerproto/layerstrace.proto
+++ b/services/surfaceflinger/layerproto/layerstrace.proto
@@ -48,4 +48,7 @@
optional string where = 2;
optional LayersProto layers = 3;
+
+ // Blob for the current HWC information for all layers, reported by dumpsys.
+ optional string hwc_blob = 4;
}
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 155f718..cfc301b 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -67,6 +67,26 @@
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
}
+# Controls the maximum width in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+ api_name: "max_graphics_width"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_graphics_width"
+}
+
+# Controls the maximum height in pixels that the graphics pipeline can support for GPU fallback
+# composition. For example, 8k displays with 4k GPUs, or 4k displays with 2k GPUs.
+prop {
+ api_name: "max_graphics_height"
+ type: Integer
+ scope: System
+ access: Readonly
+ prop_name: "ro.surface_flinger.max_graphics_height"
+}
+
# hasWideColorDisplay indicates that the device has
# or can support a wide-color display, e.g. color space
# greater than sRGB. Typical display may have same
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index e62c127..ba60a7d 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -62,6 +62,16 @@
prop_name: "ro.surface_flinger.max_frame_buffer_acquired_buffers"
}
prop {
+ api_name: "max_graphics_height"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_height"
+ }
+ prop {
+ api_name: "max_graphics_width"
+ type: Integer
+ prop_name: "ro.surface_flinger.max_graphics_width"
+ }
+ prop {
api_name: "max_virtual_display_dimension"
type: Long
prop_name: "ro.surface_flinger.max_virtual_display_dimension"
diff --git a/services/surfaceflinger/tests/AndroidTest.xml b/services/surfaceflinger/tests/AndroidTest.xml
index 8315037..000628f 100644
--- a/services/surfaceflinger/tests/AndroidTest.xml
+++ b/services/surfaceflinger/tests/AndroidTest.xml
@@ -18,6 +18,7 @@
<option name="cleanup" value="true" />
<option name="push" value="SurfaceFlinger_test->/data/local/tmp/SurfaceFlinger_test" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1cd8731..2f2fb20 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -50,6 +50,7 @@
"PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
+ "SetFrameRateTest.cpp",
"RefreshRateConfigsTest.cpp",
"RefreshRateSelectionTest.cpp",
"RefreshRateStatsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 06ef8e7..680b0a0 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -39,6 +39,7 @@
#include "Layer.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockDispSync.h"
#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
@@ -186,6 +187,7 @@
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::TimeStats* mTimeStats = new mock::TimeStats();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -284,8 +286,27 @@
EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
EXPECT_CALL(*test->mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
+
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs =
+ compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical({DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setIsSecure(Derived::IS_SECURE)
+ .setLayerStackId(DEFAULT_LAYER_STACK)
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+
test->mDisplay =
- FakeDisplayDeviceInjector(test->mFlinger, DEFAULT_DISPLAY_ID,
+ FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
DisplayConnectionType::Internal, true /* isPrimary */)
.setDisplaySurface(test->mDisplaySurface)
.setNativeWindow(test->mNativeWindow)
@@ -325,18 +346,17 @@
template <typename Case>
static void setupCommonScreensCaptureCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillRepeatedly(
- [](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
- EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
- EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- displaySettings.physicalDisplay);
- EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- displaySettings.clip);
- return NO_ERROR;
- });
+ .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>&,
+ ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ return NO_ERROR;
+ });
}
static void setupNonEmptyFrameCompositionCallExpectations(CompositionTest* test) {
@@ -375,19 +395,18 @@
.WillOnce(DoAll(SetArgPointee<0>(test->mNativeWindowBuffer), SetArgPointee<1>(-1),
Return(0)));
EXPECT_CALL(*test->mRenderEngine, drawLayers)
- .WillRepeatedly(
- [](const renderengine::DisplaySettings& displaySettings,
- const std::vector<const renderengine::LayerSettings*>&,
- ANativeWindowBuffer*, const bool, base::unique_fd&&,
- base::unique_fd*) -> status_t {
- EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
- EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- displaySettings.physicalDisplay);
- EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
- displaySettings.clip);
- EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
- return NO_ERROR;
- });
+ .WillRepeatedly([](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<const renderengine::LayerSettings*>&,
+ ANativeWindowBuffer*, const bool, base::unique_fd&&,
+ base::unique_fd*) -> status_t {
+ EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.physicalDisplay);
+ EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT),
+ displaySettings.clip);
+ EXPECT_EQ(ui::Dataspace::UNKNOWN, displaySettings.outputDataspace);
+ return NO_ERROR;
+ });
}
template <typename Case>
diff --git a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
index a023367..c2ddfce 100644
--- a/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayIdentificationTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <functional>
+#include <string_view>
+
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -124,6 +127,10 @@
return DisplayIdentificationData(bytes, bytes + N - 1);
}
+uint32_t hash(const char* str) {
+ return static_cast<uint32_t>(std::hash<std::string_view>()(str));
+}
+
} // namespace
const DisplayIdentificationData& getInternalEdid() {
@@ -173,7 +180,8 @@
EXPECT_EQ(0x4ca3u, edid->manufacturerId);
EXPECT_STREQ("SEC", edid->pnpId.data());
// ASCII text should be used as fallback if display name and serial number are missing.
- EXPECT_EQ("121AT11-801", edid->displayName);
+ EXPECT_EQ(hash("121AT11-801"), edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
EXPECT_EQ(12610, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
EXPECT_EQ(0, edid->manufactureWeek);
@@ -182,6 +190,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(0x22f0u, edid->manufacturerId);
EXPECT_STREQ("HWP", edid->pnpId.data());
+ EXPECT_EQ(hash("HP ZR30w"), edid->modelHash);
EXPECT_EQ("HP ZR30w", edid->displayName);
EXPECT_EQ(10348, edid->productId);
EXPECT_EQ(22, edid->manufactureOrModelYear);
@@ -191,6 +200,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(0x4c2du, edid->manufacturerId);
EXPECT_STREQ("SAM", edid->pnpId.data());
+ EXPECT_EQ(hash("SAMSUNG"), edid->modelHash);
EXPECT_EQ("SAMSUNG", edid->displayName);
EXPECT_EQ(2302, edid->productId);
EXPECT_EQ(21, edid->manufactureOrModelYear);
@@ -200,6 +210,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(13481, edid->manufacturerId);
EXPECT_STREQ("MEI", edid->pnpId.data());
+ EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash);
EXPECT_EQ("Panasonic-TV", edid->displayName);
EXPECT_EQ(41622, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -209,6 +220,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(8355, edid->manufacturerId);
EXPECT_STREQ("HEC", edid->pnpId.data());
+ EXPECT_EQ(hash("Hisense"), edid->modelHash);
EXPECT_EQ("Hisense", edid->displayName);
EXPECT_EQ(0, edid->productId);
EXPECT_EQ(29, edid->manufactureOrModelYear);
@@ -218,6 +230,7 @@
ASSERT_TRUE(edid);
EXPECT_EQ(3724, edid->manufacturerId);
EXPECT_STREQ("CTL", edid->pnpId.data());
+ EXPECT_EQ(hash("LP2361"), edid->modelHash);
EXPECT_EQ("LP2361", edid->displayName);
EXPECT_EQ(9373, edid->productId);
EXPECT_EQ(23, edid->manufactureOrModelYear);
@@ -234,13 +247,15 @@
auto edid = parseEdid(data);
ASSERT_TRUE(edid);
// Serial number should be used as fallback if display name is invalid.
- EXPECT_EQ("CN4202137Q", edid->displayName);
+ const auto modelHash = hash("CN4202137Q");
+ EXPECT_EQ(modelHash, edid->modelHash);
+ EXPECT_TRUE(edid->displayName.empty());
// Parsing should succeed even if EDID is truncated.
data.pop_back();
edid = parseEdid(data);
ASSERT_TRUE(edid);
- EXPECT_EQ("CN4202137Q", edid->displayName);
+ EXPECT_EQ(modelHash, edid->modelHash);
}
TEST(DisplayIdentificationTest, getPnpId) {
@@ -278,7 +293,7 @@
ASSERT_TRUE(displayIdInfo);
ASSERT_TRUE(displayIdInfo->deviceProductInfo);
const auto& info = *displayIdInfo->deviceProductInfo;
- EXPECT_STREQ("121AT11-801", info.name.data());
+ EXPECT_STREQ("", info.name.data());
EXPECT_STREQ("SEC", info.manufacturerPnpId.data());
EXPECT_STREQ("12610", info.productId.data());
ASSERT_TRUE(std::holds_alternative<ManufactureYear>(info.manufactureOrModelDate));
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 4da0647..6d00ccc 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -25,8 +25,12 @@
#include <compositionengine/Display.h>
#include <compositionengine/DisplayColorProfile.h>
+#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputCompositionState.h>
+#include <compositionengine/mock/Display.h>
+#include <compositionengine/mock/DisplayColorProfile.h>
#include <compositionengine/mock/DisplaySurface.h>
+#include <compositionengine/mock/RenderSurface.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <gui/mock/GraphicBufferConsumer.h>
@@ -39,6 +43,7 @@
#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockDispSync.h"
#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
@@ -51,11 +56,14 @@
namespace {
using testing::_;
+using testing::AnyNumber;
using testing::DoAll;
using testing::Mock;
using testing::ResultOf;
using testing::Return;
+using testing::ReturnRefOfCopy;
using testing::SetArgPointee;
+using testing::StrictMock;
using android::Hwc2::ColorMode;
using android::Hwc2::Error;
@@ -107,6 +115,7 @@
void injectMockComposer(int virtualDisplayCount);
void injectFakeBufferQueueFactory();
void injectFakeNativeWindowSurfaceFactory();
+ sp<DisplayDevice> injectDefaultInternalDisplay(std::function<void(FakeDisplayDeviceInjector&)>);
// --------------------------------------------------------------------
// Postcondition helpers
@@ -126,6 +135,7 @@
TestableSurfaceFlinger mFlinger;
sp<mock::NativeWindow> mNativeWindow = new mock::NativeWindow();
sp<GraphicBuffer> mBuffer = new GraphicBuffer();
+ Hwc2::mock::PowerAdvisor mPowerAdvisor;
// These mocks are created by the test, but are destroyed by SurfaceFlinger
// by virtue of being stored into a std::unique_ptr. However we still need
@@ -231,6 +241,49 @@
});
}
+sp<DisplayDevice> DisplayTransactionTest::injectDefaultInternalDisplay(
+ std::function<void(FakeDisplayDeviceInjector&)> injectExtra) {
+ constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
+ constexpr int DEFAULT_DISPLAY_WIDTH = 1080;
+ constexpr int DEFAULT_DISPLAY_HEIGHT = 1920;
+
+ // The DisplayDevice is required to have a framebuffer (behind the
+ // ANativeWindow interface) which uses the actual hardware display
+ // size.
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_WIDTH), Return(0)));
+ EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
+ EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(AnyNumber());
+
+ auto compositionDisplay = compositionengine::impl::
+ createDisplay(mFlinger.getCompositionEngine(),
+ compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical(
+ {DEFAULT_DISPLAY_ID, DisplayConnectionType::Internal})
+ .setPixels({DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT})
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+
+ auto injector =
+ FakeDisplayDeviceInjector(mFlinger, compositionDisplay, DisplayConnectionType::Internal,
+ true /* isPrimary */);
+
+ injector.setNativeWindow(mNativeWindow);
+ if (injectExtra) {
+ injectExtra(injector);
+ }
+
+ auto displayDevice = injector.inject();
+
+ Mock::VerifyAndClear(mNativeWindow.get());
+
+ return displayDevice;
+}
+
bool DisplayTransactionTest::hasPhysicalHwcDisplay(hwc2_display_t hwcDisplayId) {
return mFlinger.mutableHwcPhysicalDisplayIdMap().count(hwcDisplayId) == 1;
}
@@ -353,9 +406,21 @@
static constexpr Primary PRIMARY = primary;
static auto makeFakeExistingDisplayInjector(DisplayTransactionTest* test) {
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder();
+ if (auto displayId = DISPLAY_ID::get()) {
+ ceDisplayArgs.setPhysical({*displayId, DisplayConnectionType::Internal});
+ } else {
+ ceDisplayArgs.setUseHwcVirtualDisplays(false);
+ }
+ ceDisplayArgs.setPixels({WIDTH, HEIGHT}).setPowerAdvisor(&test->mPowerAdvisor).build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs.build());
+
auto injector =
- FakeDisplayDeviceInjector(test->mFlinger, DISPLAY_ID::get(), CONNECTION_TYPE::value,
- static_cast<bool>(PRIMARY));
+ FakeDisplayDeviceInjector(test->mFlinger, compositionDisplay,
+ CONNECTION_TYPE::value, static_cast<bool>(PRIMARY));
injector.setSecure(static_cast<bool>(SECURE));
injector.setNativeWindow(test->mNativeWindow);
@@ -458,6 +523,25 @@
injectHwcDisplayWithNoDefaultCapabilities(test);
}
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPhysical({*DisplayVariant::DISPLAY_ID::get(),
+ PhysicalDisplay::CONNECTION_TYPE})
+ .setPixels({DisplayVariant::WIDTH, DisplayVariant::HEIGHT})
+ .setIsSecure(static_cast<bool>(DisplayVariant::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
static void setupHwcHotplugCallExpectations(DisplayTransactionTest* test) {
constexpr auto CONNECTION_TYPE =
PhysicalDisplay::CONNECTION_TYPE == DisplayConnectionType::Internal
@@ -577,6 +661,23 @@
static void injectHwcDisplay(DisplayTransactionTest*) {}
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ return compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ }
+
static void setupHwcGetActiveConfigCallExpectations(DisplayTransactionTest* test) {
EXPECT_CALL(*test->mComposer, getActiveConfig(_, _)).Times(0);
}
@@ -602,6 +703,33 @@
secure, Primary::FALSE, GRALLOC_USAGE_HW_COMPOSER>;
using Self = HwcVirtualDisplayVariant<width, height, secure>;
+ static std::shared_ptr<compositionengine::Display> injectCompositionDisplay(
+ DisplayTransactionTest* test) {
+ const ::testing::TestInfo* const test_info =
+ ::testing::UnitTest::GetInstance()->current_test_info();
+
+ auto ceDisplayArgs = compositionengine::DisplayCreationArgsBuilder()
+ .setUseHwcVirtualDisplays(false)
+ .setPixels({Base::WIDTH, Base::HEIGHT})
+ .setIsSecure(static_cast<bool>(Base::SECURE))
+ .setPowerAdvisor(&test->mPowerAdvisor)
+ .setName(std::string("Injected display for ") +
+ test_info->test_case_name() + "." + test_info->name())
+ .build();
+
+ auto compositionDisplay =
+ compositionengine::impl::createDisplay(test->mFlinger.getCompositionEngine(),
+ ceDisplayArgs);
+ compositionDisplay->setDisplayIdForTesting(Base::DISPLAY_ID::get());
+
+ // Insert display data so that the HWC thinks it created the virtual display.
+ if (const auto displayId = Base::DISPLAY_ID::get()) {
+ test->mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
+ }
+
+ return compositionDisplay;
+ }
+
static void setupNativeWindowSurfaceCreationCallExpectations(DisplayTransactionTest* test) {
Base::setupNativeWindowSurfaceCreationCallExpectations(test);
EXPECT_CALL(*test->mNativeWindow, setSwapInterval(0)).Times(1);
@@ -1199,14 +1327,6 @@
*/
class GetBestColorModeTest : public DisplayTransactionTest {
public:
- static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
-
- GetBestColorModeTest()
- : DisplayTransactionTest(),
- mInjector(FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
- DisplayConnectionType::Internal,
- true /* isPrimary */)) {}
-
void setHasWideColorGamut(bool hasWideColorGamut) { mHasWideColorGamut = hasWideColorGamut; }
void addHwcColorModesMapping(ui::ColorMode colorMode,
@@ -1219,21 +1339,12 @@
void setInputRenderIntent(ui::RenderIntent renderIntent) { mInputRenderIntent = renderIntent; }
void getBestColorMode() {
- mInjector.setHwcColorModes(mHwcColorModes);
- mInjector.setHasWideColorGamut(mHasWideColorGamut);
- mInjector.setNativeWindow(mNativeWindow);
-
- // Creating a DisplayDevice requires getting default dimensions from the
- // native window.
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(1080 /* arbitrary */), Return(0)));
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(1920 /* arbitrary */), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT)).Times(1);
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT)).Times(1);
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64)).Times(1);
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT)).Times(1);
- auto displayDevice = mInjector.inject();
+ auto displayDevice =
+ injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setHwcColorModes(mHwcColorModes);
+ injector.setHasWideColorGamut(mHasWideColorGamut);
+ injector.setNativeWindow(mNativeWindow);
+ });
displayDevice->getCompositionDisplay()
->getDisplayColorProfile()
@@ -1250,7 +1361,6 @@
ui::RenderIntent mInputRenderIntent;
bool mHasWideColorGamut = false;
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> mHwcColorModes;
- FakeDisplayDeviceInjector mInjector;
};
TEST_F(GetBestColorModeTest, DataspaceDisplayP3_ColorModeSRGB) {
@@ -1305,7 +1415,6 @@
class DisplayDeviceSetProjectionTest : public DisplayTransactionTest {
public:
- static constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{777};
static constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1080; // arbitrary
static constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1920; // arbitrary
@@ -1323,23 +1432,9 @@
mDisplayDevice(createDisplayDevice()) {}
sp<DisplayDevice> createDisplayDevice() {
- // The DisplayDevice is required to have a framebuffer (behind the
- // ANativeWindow interface) which uses the actual hardware display
- // size.
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.width), Return(0)));
- EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_HEIGHT, _))
- .WillRepeatedly(DoAll(SetArgPointee<1>(mHardwareDisplaySize.height), Return(0)));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_BUFFERS_FORMAT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_CONNECT));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_SET_USAGE64));
- EXPECT_CALL(*mNativeWindow, perform(NATIVE_WINDOW_API_DISCONNECT));
-
- return FakeDisplayDeviceInjector(mFlinger, DEFAULT_DISPLAY_ID,
- DisplayConnectionType::Internal, true /* isPrimary */)
- .setNativeWindow(mNativeWindow)
- .setPhysicalOrientation(mPhysicalOrientation)
- .inject();
+ return injectDefaultInternalDisplay([this](FakeDisplayDeviceInjector& injector) {
+ injector.setPhysicalOrientation(mPhysicalOrientation);
+ });
}
ui::Size SwapWH(const ui::Size size) const { return ui::Size(size.height, size.width); }
@@ -1652,6 +1747,9 @@
// surfaces.
injectFakeNativeWindowSurfaceFactory();
+ // A compositionengine::Display has already been created
+ auto compositionDisplay = Case::Display::injectCompositionDisplay(this);
+
// --------------------------------------------------------------------
// Call Expectations
@@ -1674,9 +1772,8 @@
state.isSecure = static_cast<bool>(Case::Display::SECURE);
- auto device =
- mFlinger.setupNewDisplayDeviceInternal(displayToken, Case::Display::DISPLAY_ID::get(),
- state, displaySurface, producer);
+ auto device = mFlinger.setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ displaySurface, producer);
// --------------------------------------------------------------------
// Postconditions
@@ -1715,14 +1812,7 @@
}
TEST_F(SetupNewDisplayDeviceInternalTest, createHwcVirtualDisplay) {
- using Case = HwcVirtualDisplayCase;
-
- // Insert display data so that the HWC thinks it created the virtual display.
- const auto displayId = Case::Display::DISPLAY_ID::get();
- ASSERT_TRUE(displayId);
- mFlinger.mutableHwcDisplayData().try_emplace(*displayId);
-
- setupNewDisplayDeviceInternalTest<Case>();
+ setupNewDisplayDeviceInternalTest<HwcVirtualDisplayCase>();
}
TEST_F(SetupNewDisplayDeviceInternalTest, createWideColorP3Display) {
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 65b3e35..ba5c0c2 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -48,6 +48,7 @@
MOCK_METHOD1(setCallback, void(VSyncSource::Callback*));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t));
MOCK_METHOD1(pauseVsyncCallback, void(bool));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 18e9941..7557faf 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -84,7 +84,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -113,7 +113,7 @@
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -137,15 +137,15 @@
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer3, getFrameSelectionPriority()).WillRepeatedly(Return(1));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = mTime;
EXPECT_EQ(3, layerCount());
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
index 959c256..8559a5e 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTestV2.cpp
@@ -36,6 +36,7 @@
protected:
static constexpr auto PRESENT_TIME_HISTORY_SIZE = LayerInfoV2::HISTORY_SIZE;
static constexpr auto MAX_FREQUENT_LAYER_PERIOD_NS = LayerInfoV2::MAX_FREQUENT_LAYER_PERIOD_NS;
+ static constexpr auto FREQUENT_LAYER_WINDOW_SIZE = LayerInfoV2::FREQUENT_LAYER_WINDOW_SIZE;
static constexpr float LO_FPS = 30.f;
static constexpr auto LO_FPS_PERIOD = static_cast<nsecs_t>(1e9f / LO_FPS);
@@ -90,7 +91,7 @@
TEST_F(LayerHistoryTestV2, oneLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -121,7 +122,7 @@
TEST_F(LayerHistoryTestV2, oneInvisibleLayer) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -145,7 +146,7 @@
TEST_F(LayerHistoryTestV2, explicitTimestamp) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -166,7 +167,7 @@
TEST_F(LayerHistoryTestV2, oneLayerNoVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::NoVote);
@@ -193,7 +194,7 @@
TEST_F(LayerHistoryTestV2, oneLayerMinVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Min);
@@ -221,7 +222,7 @@
TEST_F(LayerHistoryTestV2, oneLayerMaxVote) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Max);
@@ -249,7 +250,7 @@
TEST_F(LayerHistoryTestV2, oneLayerExplicitVote) {
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate())
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(
Return(Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::Default)));
@@ -272,7 +273,8 @@
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_TRUE(history().summarize(time).empty());
- // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+ // value > 0
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -280,7 +282,7 @@
TEST_F(LayerHistoryTestV2, oneLayerExplicitExactVote) {
auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer, getFrameRate())
+ EXPECT_CALL(*layer, getFrameRateForLayerTree())
.WillRepeatedly(Return(
Layer::FrameRate(73.4f, Layer::FrameRateCompatibility::ExactOrMultiple)));
@@ -304,7 +306,8 @@
setLayerInfoVote(layer.get(), LayerHistory::LayerVoteType::Heuristic);
time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
ASSERT_TRUE(history().summarize(time).empty());
- // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRate() returns a value > 0
+ // TODO: activeLayerCount() should be 0 but it is 1 since getFrameRateForLayerTree() returns a
+ // value > 0
EXPECT_EQ(1, activeLayerCount());
EXPECT_EQ(0, frequentLayerCount(time));
}
@@ -315,13 +318,13 @@
auto layer3 = createLayer();
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer1, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer2, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer2, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
EXPECT_CALL(*layer3, isVisible()).WillRepeatedly(Return(true));
- EXPECT_CALL(*layer3, getFrameRate()).WillRepeatedly(Return(Layer::FrameRate()));
+ EXPECT_CALL(*layer3, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
nsecs_t time = systemTime();
@@ -451,5 +454,61 @@
EXPECT_EQ(0, frequentLayerCount(time));
}
+TEST_F(LayerHistoryTestV2, inactiveLayers) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ // the very first updates makes the layer frequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+ }
+
+ // the next update with the MAX_FREQUENT_LAYER_PERIOD_NS will get us to infrequent
+ history().record(layer.get(), time, time);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+
+ // advance the time for the previous frame to be inactive
+ time += MAX_ACTIVE_LAYER_PERIOD_NS.count();
+
+ // Now event if we post a quick few frame we should stay infrequent
+ for (int i = 0; i < FREQUENT_LAYER_WINDOW_SIZE - 1; i++) {
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Min, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ }
+
+ // More quick frames will get us to frequent again
+ history().record(layer.get(), time, time);
+ time += HI_FPS_PERIOD;
+
+ EXPECT_EQ(1, layerCount());
+ ASSERT_EQ(1, history().summarize(time).size());
+ EXPECT_EQ(LayerHistory::LayerVoteType::Max, history().summarize(time)[0].vote);
+ EXPECT_EQ(1, activeLayerCount());
+ EXPECT_EQ(1, frequentLayerCount(time));
+}
+
} // namespace
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
index 910e73b..8d49201 100644
--- a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -42,25 +42,25 @@
appEarlyGlDuration) {}
};
-class PhaseOffsetsTest : public testing::Test {
+class PhaseDurationTest : public testing::Test {
protected:
- PhaseOffsetsTest()
- : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
- 38'000'000) {}
+ PhaseDurationTest()
+ : mPhaseDurations(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
- ~PhaseOffsetsTest() = default;
+ ~PhaseDurationTest() = default;
- TestablePhaseOffsetsAsDurations mPhaseOffsets;
+ TestablePhaseOffsetsAsDurations mPhaseDurations;
};
namespace {
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
- mPhaseOffsets.setRefreshRateFps(60.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseDurations.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(60.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 6'166'667);
@@ -76,10 +76,10 @@
EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
- mPhaseOffsets.setRefreshRateFps(90.0f);
- auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
- auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseDurations.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseDurations.getCurrentOffsets();
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(90.0f);
EXPECT_EQ(currentOffsets, offsets);
EXPECT_EQ(offsets.late.sf, 611'111);
@@ -95,7 +95,7 @@
EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
}
-TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_DefaultOffsets) {
TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
auto validateOffsets = [](auto& offsets) {
@@ -125,6 +125,54 @@
validateOffsets(offsets);
}
+TEST_F(PhaseDurationTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseDurations.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 57'527'208);
+
+ EXPECT_EQ(offsets.late.app, 37'027'208);
+
+ EXPECT_EQ(offsets.early.sf, 52'027'208);
+
+ EXPECT_EQ(offsets.early.app, 18'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 54'527'208);
+
+ EXPECT_EQ(offsets.earlyGl.app, 16'527'208);
+}
+
+} // namespace
+
+class TestablePhaseOffsets : public impl::PhaseOffsets {
+public:
+ TestablePhaseOffsets() : impl::PhaseOffsets({60.0f, 90.0f}, 60.0f, 10'000'000) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest() = default;
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsets mPhaseOffsets;
+};
+
+namespace {
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_unknownRefreshRate) {
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(14.7f);
+
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 7e62513..dd04076 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,6 +21,7 @@
#include <log/log.h>
#include <thread>
+#include "../../Scheduler/RefreshRateConfigs.h"
#include "DisplayHardware/HWC2.h"
#include "Scheduler/RefreshRateConfigs.h"
@@ -93,8 +94,8 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto minRate = refreshRateConfigs->getMinRefreshRate();
- const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto& minRate = refreshRateConfigs->getMinRefreshRate();
+ const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -102,8 +103,8 @@
90};
ASSERT_EQ(expectedPerformanceConfig, performanceRate);
- const auto minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ const auto& minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(minRateByPolicy, minRate);
ASSERT_EQ(performanceRateByPolicy, performanceRate);
}
@@ -115,10 +116,10 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
- const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
- const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ const auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -128,8 +129,8 @@
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
- const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
- const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ const auto& minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto& performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_1, "90fps",
90};
@@ -145,8 +146,8 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
- auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto& minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto& performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
ASSERT_EQ(expectedDefaultConfig, minRate);
@@ -156,8 +157,8 @@
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
- auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
- auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ auto& minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto& performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
ASSERT_EQ(expectedDefaultConfig, minRate60);
ASSERT_EQ(expectedDefaultConfig, performanceRate60);
}
@@ -169,19 +170,19 @@
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
{
- auto current = refreshRateConfigs->getCurrentRefreshRate();
+ auto& current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
}
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
{
- auto current = refreshRateConfigs->getCurrentRefreshRate();
+ auto& current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
}
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
{
- auto current = refreshRateConfigs->getCurrentRefreshRate();
+ auto& current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
}
}
@@ -247,7 +248,34 @@
refreshRateConfigs->getRefreshRateForContent(makeLayerRequirements(24.0f)));
}
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_noLayers) {
+ bool ignored;
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/
+ HWC_CONFIG_ID_72);
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected72Config = {HWC_CONFIG_ID_72, VSYNC_72, HWC_GROUP_ID_0, "72fps", 72};
+
+ // If there are not layers, there is not content detection, so return the current
+ // refresh rate.
+ auto layers = std::vector<LayerRequirement>{};
+ EXPECT_EQ(expected72Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+ false, &ignored));
+
+ // Current refresh rate can always be changed.
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_60);
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/
+ false, &ignored));
+}
+
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_90) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -263,134 +291,163 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.name = "";
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_60_72_90) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
@@ -407,35 +464,43 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90_120) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -461,24 +526,28 @@
lr2.desiredRefreshRate = 60.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected120Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 48.0f;
lr2.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_90_120_DifferentTypes) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -506,7 +575,8 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(expected120Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -515,7 +585,8 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "60Hz Heuristic";
EXPECT_EQ(expected120Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -524,7 +595,8 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "60Hz ExplicitDefault";
EXPECT_EQ(expected120Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -533,7 +605,8 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -542,7 +615,8 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -551,7 +625,8 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::Heuristic;
@@ -560,7 +635,8 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -569,7 +645,8 @@
lr2.vote = LayerVoteType::ExplicitDefault;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.desiredRefreshRate = 24.0f;
lr1.vote = LayerVoteType::ExplicitDefault;
@@ -578,10 +655,12 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.name = "90Hz ExplicitExactOrMultiple";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30}}};
@@ -596,35 +675,43 @@
lr.vote = LayerVoteType::Min;
EXPECT_EQ(expected30Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
EXPECT_EQ(expected30Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_30_60_72_90) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -644,57 +731,71 @@
lr.vote = LayerVoteType::Min;
lr.name = "Min";
EXPECT_EQ(expected30Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.vote = LayerVoteType::Max;
lr.name = "Max";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 90.0f;
lr.vote = LayerVoteType::Heuristic;
lr.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr.desiredRefreshRate = 60.0f;
lr.name = "60Hz Heuristic";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+ &ignored));
lr.desiredRefreshRate = 45.0f;
lr.name = "45Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+ &ignored));
lr.desiredRefreshRate = 30.0f;
lr.name = "30Hz Heuristic";
EXPECT_EQ(expected30Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
lr.name = "24Hz Heuristic";
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+ &ignored));
lr.desiredRefreshRate = 24.0f;
lr.vote = LayerVoteType::ExplicitExactOrMultiple;
lr.name = "24Hz ExplicitExactOrMultiple";
EXPECT_EQ(expected72Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ true,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_PriorityTest) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_30, HWC_GROUP_ID_0, VSYNC_30},
{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -714,48 +815,56 @@
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Max;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Min;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 24.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Max;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 15.0f;
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 30.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 45.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_24FpsVideo) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -773,7 +882,8 @@
for (float fps = 23.0f; fps < 25.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false);
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored);
printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
EXPECT_EQ(expected60Config, refreshRate);
}
@@ -808,6 +918,7 @@
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContentV2_Explicit) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -827,21 +938,24 @@
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 90.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::ExplicitDefault;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::Heuristic;
lr1.desiredRefreshRate = 90.0f;
lr2.vote = LayerVoteType::ExplicitExactOrMultiple;
lr2.desiredRefreshRate = 60.0f;
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, testInPolicy) {
@@ -855,6 +969,7 @@
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_75HzContent) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -872,13 +987,15 @@
for (float fps = 75.0f; fps < 100.0f; fps += 0.1f) {
lr.desiredRefreshRate = fps;
const auto& refreshRate =
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false);
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored);
printf("%.2fHz chooses %s\n", fps, refreshRate.name.c_str());
EXPECT_EQ(expected90Config, refreshRate);
}
}
TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_Multiples) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -900,7 +1017,8 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -909,7 +1027,8 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz ExplicitDefault";
EXPECT_EQ(expected60Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
@@ -917,7 +1036,8 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -926,7 +1046,8 @@
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 30.0f;
@@ -934,10 +1055,12 @@
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
EXPECT_EQ(expected90Config,
- refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false));
+ refreshRateConfigs->getRefreshRateForContentV2(layers, /*touchActive*/ false,
+ &ignored));
}
TEST_F(RefreshRateConfigsTest, scrollWhileWatching60fps_60_90) {
+ bool ignored;
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
{HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
@@ -957,28 +1080,32 @@
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+ EXPECT_EQ(expected60Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::NoVote;
lr2.name = "NoVote";
- EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, true));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, true));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &ignored));
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
lr1.desiredRefreshRate = 60.0f;
lr1.name = "60Hz ExplicitExactOrMultiple";
lr2.vote = LayerVoteType::Max;
lr2.name = "Max";
- EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
// The other layer starts to provide buffers
lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
@@ -987,7 +1114,108 @@
lr2.vote = LayerVoteType::Heuristic;
lr2.desiredRefreshRate = 90.0f;
lr2.name = "90Hz Heuristic";
- EXPECT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContentV2(layers, false));
+ EXPECT_EQ(expected90Config,
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored));
+}
+
+TEST_F(RefreshRateConfigsTest, touchConsidered) {
+ bool touchConsidered;
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ refreshRateConfigs->getRefreshRateForContentV2({}, false, &touchConsidered);
+ EXPECT_EQ(false, touchConsidered);
+
+ refreshRateConfigs->getRefreshRateForContentV2({}, true, &touchConsidered);
+ EXPECT_EQ(true, touchConsidered);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f},
+ LayerRequirement{.weight = 1.0f}};
+ auto& lr1 = layers[0];
+ auto& lr2 = layers[1];
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60.0f;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "NoVote";
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+ EXPECT_EQ(true, touchConsidered);
+
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.desiredRefreshRate = 60.0f;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "NoVote";
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+ EXPECT_EQ(false, touchConsidered);
+
+ lr1.vote = LayerVoteType::ExplicitExactOrMultiple;
+ lr1.desiredRefreshRate = 60.0f;
+ lr1.name = "60Hz ExplicitExactOrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "NoVote";
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+ EXPECT_EQ(true, touchConsidered);
+
+ lr1.vote = LayerVoteType::ExplicitDefault;
+ lr1.desiredRefreshRate = 60.0f;
+ lr1.name = "60Hz ExplicitExactrMultiple";
+ lr2.vote = LayerVoteType::Heuristic;
+ lr2.name = "NoVote";
+ refreshRateConfigs->getRefreshRateForContentV2(layers, true, &touchConsidered);
+ EXPECT_EQ(false, touchConsidered);
+}
+
+TEST_F(RefreshRateConfigsTest, getRefreshRateForContentV2_ExplicitDefault) {
+ bool ignored;
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_72, HWC_GROUP_ID_0, VSYNC_72},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90},
+ {HWC_CONFIG_ID_120, HWC_GROUP_ID_0, VSYNC_120}}};
+
+ auto refreshRateConfigs = std::make_unique<RefreshRateConfigs>(configs, /*currentConfigId=*/
+ HWC_CONFIG_ID_60);
+
+ auto layers = std::vector<LayerRequirement>{LayerRequirement{.weight = 1.0f}};
+ auto& lr = layers[0];
+
+ // Prepare a table with the vote and the expected refresh rate
+ const std::vector<std::pair<float, float>> testCases = {
+ {130, 120}, {120, 120}, {119, 120}, {110, 120},
+
+ {100, 90}, {90, 90}, {89, 90},
+
+ {80, 72}, {73, 72}, {72, 72}, {71, 72}, {70, 72},
+
+ {65, 60}, {60, 60}, {59, 60}, {58, 60},
+
+ {55, 90}, {50, 90}, {45, 90},
+
+ {42, 120}, {40, 120}, {39, 120},
+
+ {37, 72}, {36, 72}, {35, 72},
+
+ {30, 60},
+ };
+
+ for (const auto& test : testCases) {
+ lr.vote = LayerVoteType::ExplicitDefault;
+ lr.desiredRefreshRate = test.first;
+
+ std::stringstream ss;
+ ss << "ExplicitDefault " << test.first << " fps";
+ lr.name = ss.str();
+
+ const auto& refreshRate =
+ refreshRateConfigs->getRefreshRateForContentV2(layers, false, &ignored);
+ EXPECT_FLOAT_EQ(refreshRate.fps, test.second)
+ << "Expecting " << test.first << "fps => " << test.second << "Hz";
+ }
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
new file mode 100644
index 0000000..b069085
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wconversion"
+#include "BufferQueueLayer.h"
+#include "BufferStateLayer.h"
+#include "EffectLayer.h"
+#include "Layer.h"
+// TODO(b/129481165): remove the #pragma below and fix conversion issues
+#pragma clang diagnostic pop // ignored "-Wconversion"
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockDispSync.h"
+#include "mock/MockEventControlThread.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockMessageQueue.h"
+
+namespace android {
+
+using testing::_;
+using testing::DoAll;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+using FrameRate = Layer::FrameRate;
+using FrameRateCompatibility = Layer::FrameRateCompatibility;
+
+class LayerFactory {
+public:
+ virtual ~LayerFactory() = default;
+
+ virtual std::string name() = 0;
+ virtual sp<Layer> createLayer(TestableSurfaceFlinger& flinger) = 0;
+
+protected:
+ static constexpr uint32_t WIDTH = 100;
+ static constexpr uint32_t HEIGHT = 100;
+ static constexpr uint32_t LAYER_FLAGS = 0;
+};
+
+class BufferQueueLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "BufferQueueLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferQueueLayer(args);
+ }
+};
+
+class BufferStateLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "BufferStateLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
+ LAYER_FLAGS, LayerMetadata());
+ return new BufferStateLayer(args);
+ }
+};
+
+class EffectLayerFactory : public LayerFactory {
+public:
+ std::string name() override { return "EffectLayer"; }
+ sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
+ sp<Client> client;
+ LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+ LayerMetadata());
+ return new EffectLayer(args);
+ }
+};
+
+std::string PrintToStringParamName(
+ const ::testing::TestParamInfo<std::shared_ptr<LayerFactory>>& info) {
+ return info.param->name();
+}
+
+/**
+ * This class tests the behaviour of Layer::SetFrameRate and Layer::GetFrameRate
+ */
+class SetFrameRateTest : public ::testing::TestWithParam<std::shared_ptr<LayerFactory>> {
+protected:
+ const FrameRate FRAME_RATE_VOTE1 = FrameRate(67.f, FrameRateCompatibility::Default);
+ const FrameRate FRAME_RATE_VOTE2 = FrameRate(14.f, FrameRateCompatibility::ExactOrMultiple);
+ const FrameRate FRAME_RATE_VOTE3 = FrameRate(99.f, FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_TREE = FrameRate(0, FrameRateCompatibility::NoVote);
+ const FrameRate FRAME_RATE_NO_VOTE = FrameRate(0, FrameRateCompatibility::Default);
+
+ SetFrameRateTest();
+
+ void setupScheduler();
+ void setupComposer(uint32_t virtualDisplayCount);
+
+ void addChild(sp<Layer> layer, sp<Layer> child);
+ void removeChild(sp<Layer> layer, sp<Layer> child);
+ void reparentChildren(sp<Layer> layer, sp<Layer> child);
+ void commitTransaction();
+
+ TestableSurfaceFlinger mFlinger;
+ Hwc2::mock::Composer* mComposer = nullptr;
+ mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+
+ std::vector<sp<Layer>> mLayers;
+};
+
+SetFrameRateTest::SetFrameRateTest() {
+ 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());
+
+ mFlinger.mutableUseFrameRateApi() = true;
+
+ setupScheduler();
+ setupComposer(0);
+
+ mFlinger.mutableEventQueue().reset(mMessageQueue);
+}
+void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
+ layer.get()->addChild(child.get());
+}
+
+void SetFrameRateTest::removeChild(sp<Layer> layer, sp<Layer> child) {
+ layer.get()->removeChild(child.get());
+}
+
+void SetFrameRateTest::reparentChildren(sp<Layer> parent, sp<Layer> newParent) {
+ parent.get()->reparentChildren(newParent);
+}
+
+void SetFrameRateTest::commitTransaction() {
+ for (auto layer : mLayers) {
+ layer.get()->commitTransaction(layer.get()->getCurrentState());
+ }
+}
+
+void SetFrameRateTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), ResyncCallback(),
+ ISurfaceComposer::eConfigChangedSuppress)));
+
+ auto primaryDispSync = std::make_unique<mock::DispSync>();
+
+ EXPECT_CALL(*primaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*primaryDispSync, getPeriod())
+ .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE));
+ EXPECT_CALL(*primaryDispSync, expectedPresentTime()).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(primaryDispSync),
+ std::make_unique<mock::EventControlThread>(), std::move(eventThread),
+ std::move(sfEventThread));
+}
+
+void SetFrameRateTest::setupComposer(uint32_t virtualDisplayCount) {
+ mComposer = new Hwc2::mock::Composer();
+ EXPECT_CALL(*mComposer, getMaxVirtualDisplayCount()).WillOnce(Return(virtualDisplayCount));
+ mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+ Mock::VerifyAndClear(mComposer);
+}
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_P(SetFrameRateTest, SetAndGet) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto layer = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ layer->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, layer->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParent) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ child1->setFrameRate(FRAME_RATE_VOTE2);
+ parent->setFrameRate(FRAME_RATE_VOTE3);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChild) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ child1->setFrameRate(FRAME_RATE_VOTE2);
+ parent->setFrameRate(FRAME_RATE_VOTE3);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE3, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE2, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child1->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+ addChild(child1, child2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ parent->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child2->getFrameRateForLayerTree());
+
+ removeChild(child1, child2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_VOTE1, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+
+ parent->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2_1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+ addChild(child1, child2_1);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2_1->getFrameRateForLayerTree());
+}
+
+TEST_P(SetFrameRateTest, SetAndGetRearentChildren) {
+ EXPECT_CALL(*mMessageQueue, invalidate()).Times(1);
+
+ const auto& layerFactory = GetParam();
+
+ auto parent = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto parent2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child1 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+ auto child2 = mLayers.emplace_back(layerFactory->createLayer(mFlinger));
+
+ addChild(parent, child1);
+ addChild(child1, child2);
+
+ child2->setFrameRate(FRAME_RATE_VOTE1);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_TREE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ reparentChildren(parent, parent2);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_TREE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_VOTE1, child2->getFrameRateForLayerTree());
+
+ child2->setFrameRate(FRAME_RATE_NO_VOTE);
+ commitTransaction();
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, parent2->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child1->getFrameRateForLayerTree());
+ EXPECT_EQ(FRAME_RATE_NO_VOTE, child2->getFrameRateForLayerTree());
+}
+
+INSTANTIATE_TEST_SUITE_P(PerLayerType, SetFrameRateTest,
+ testing::Values(std::make_shared<BufferQueueLayerFactory>(),
+ std::make_shared<BufferStateLayerFactory>(),
+ std::make_shared<EffectLayerFactory>()),
+ PrintToStringParamName);
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 52da34b..41b5d49 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -29,7 +29,7 @@
class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
TestableScheduler(const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
- : Scheduler([](bool) {}, configs, *this, useContentDetectionV2) {
+ : Scheduler([](bool) {}, configs, *this, useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
} else {
@@ -41,7 +41,7 @@
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs, bool useContentDetectionV2)
: Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this,
- useContentDetectionV2) {
+ useContentDetectionV2, true) {
if (mUseContentDetectionV2) {
mLayerHistory = std::make_unique<scheduler::impl::LayerHistoryV2>();
} else {
@@ -92,6 +92,7 @@
private:
void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
void repaintEverythingForHWC() override {}
+ void kernelTimerChanged(bool /*expired*/) override {}
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 3a4f349..414085c 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -20,6 +20,7 @@
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/OutputLayer.h>
#include <compositionengine/impl/CompositionEngine.h>
+#include <compositionengine/impl/Display.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
@@ -97,8 +98,8 @@
return new StartPropertySetThread(timestampPropertyValue);
}
- sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&& creationArgs) override {
- return new DisplayDevice(std::move(creationArgs));
+ sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs& creationArgs) override {
+ return new DisplayDevice(creationArgs);
}
sp<GraphicBuffer> createGraphicBuffer(uint32_t width, uint32_t height, PixelFormat format,
@@ -277,13 +278,14 @@
auto resetDisplayState() { return mFlinger->resetDisplayState(); }
- auto setupNewDisplayDeviceInternal(const wp<IBinder>& displayToken,
- const std::optional<DisplayId>& displayId,
- const DisplayDeviceState& state,
- const sp<compositionengine::DisplaySurface>& dispSurface,
- const sp<IGraphicBufferProducer>& producer) {
- return mFlinger->setupNewDisplayDeviceInternal(displayToken, displayId, state, dispSurface,
- producer);
+ auto setupNewDisplayDeviceInternal(
+ const wp<IBinder>& displayToken,
+ std::shared_ptr<compositionengine::Display> compositionDisplay,
+ const DisplayDeviceState& state,
+ const sp<compositionengine::DisplaySurface>& dispSurface,
+ const sp<IGraphicBufferProducer>& producer) {
+ return mFlinger->setupNewDisplayDeviceInternal(displayToken, compositionDisplay, state,
+ dispSurface, producer);
}
auto handleTransactionLocked(uint32_t transactionFlags) {
@@ -359,6 +361,7 @@
auto& getHwComposer() const {
return static_cast<impl::HWComposer&>(mFlinger->getHwComposer());
}
+ auto& getCompositionEngine() const { return mFlinger->getCompositionEngine(); }
const auto& getCompositorTiming() const { return mFlinger->getBE().mCompositorTiming; }
@@ -391,6 +394,7 @@
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+ auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
auto fromHandle(const sp<IBinder>& handle) {
Mutex::Autolock _l(mFlinger->mStateLock);
@@ -543,10 +547,11 @@
class FakeDisplayDeviceInjector {
public:
FakeDisplayDeviceInjector(TestableSurfaceFlinger& flinger,
- std::optional<DisplayId> displayId,
+ std::shared_ptr<compositionengine::Display> compositionDisplay,
std::optional<DisplayConnectionType> connectionType,
bool isPrimary)
- : mFlinger(flinger), mCreationArgs(flinger.mFlinger.get(), mDisplayToken, displayId) {
+ : mFlinger(flinger),
+ mCreationArgs(flinger.mFlinger.get(), mDisplayToken, compositionDisplay) {
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
}
@@ -609,16 +614,17 @@
}
sp<DisplayDevice> inject() {
+ const auto displayId = mCreationArgs.compositionDisplay->getDisplayId();
+
DisplayDeviceState state;
if (const auto type = mCreationArgs.connectionType) {
- const auto id = mCreationArgs.displayId;
- LOG_ALWAYS_FATAL_IF(!id);
- state.physical = {*id, *type};
+ LOG_ALWAYS_FATAL_IF(!displayId);
+ state.physical = {*displayId, *type};
}
state.isSecure = mCreationArgs.isSecure;
- sp<DisplayDevice> device = new DisplayDevice(std::move(mCreationArgs));
+ sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
mFlinger.mutableDisplays().emplace(mDisplayToken, device);
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
mFlinger.mutableDrawingState().displays.add(mDisplayToken, state);
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index a7a4d48..4f65aee 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -152,9 +152,9 @@
struct AStatsEvent* addStatsEventToPullData(AStatsEventList*) override {
return mEvent;
}
- void registerStatsPullAtomCallback(int32_t atom_tag,
- AStatsManager_PullAtomCallback callback,
- AStatsManager_PullAtomMetadata*, void* cookie) override {
+ void setStatsPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata*,
+ AStatsManager_PullAtomCallback callback,
+ void* cookie) override {
mAtomTags.push_back(atom_tag);
mCallback = callback;
mCookie = cookie;
@@ -164,7 +164,7 @@
return (*mCallback)(atom_tag, nullptr, cookie);
}
- MOCK_METHOD1(unregisterStatsPullAtomCallback, void(int32_t));
+ MOCK_METHOD1(clearStatsPullAtomCallback, void(int32_t));
MOCK_METHOD2(statsEventSetAtomId, void(AStatsEvent*, uint32_t));
MOCK_METHOD2(statsEventWriteInt32, void(AStatsEvent*, int32_t));
MOCK_METHOD2(statsEventWriteInt64, void(AStatsEvent*, int64_t));
@@ -261,18 +261,18 @@
ASSERT_FALSE(mTimeStats->isEnabled());
}
-TEST_F(TimeStatsTest, registersCallbacksAfterBoot) {
+TEST_F(TimeStatsTest, setsCallbacksAfterBoot) {
mTimeStats->onBootFinished();
EXPECT_THAT(mDelegate->mAtomTags,
UnorderedElementsAre(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO,
android::util::SURFACEFLINGER_STATS_LAYER_INFO));
}
-TEST_F(TimeStatsTest, unregistersCallbacksOnDestruction) {
+TEST_F(TimeStatsTest, clearsCallbacksOnDestruction) {
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_GLOBAL_INFO));
EXPECT_CALL(*mDelegate,
- unregisterStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
+ clearStatsPullAtomCallback(android::util::SURFACEFLINGER_STATS_LAYER_INFO));
mTimeStats.reset();
}
@@ -1039,8 +1039,7 @@
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 4000000);
insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 5000000);
- // Now make sure that TimeStats flushes global stats to register the
- // callback.
+ // Now make sure that TimeStats flushes global stats to set the callback.
mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(3000000));
mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(5000000));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index caac61d..be49ef3 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -51,6 +51,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
nsecs_t const mPeriod;
@@ -85,6 +86,7 @@
void setPeriod(nsecs_t) final {}
void resetModel() final {}
+ void dump(std::string&) const final {}
private:
std::mutex mutable mMutex;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 3ab38e4..3543361 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -47,6 +47,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index f834af8..bf2a889 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -204,7 +204,7 @@
};
auto idealPeriod = 2000000;
auto expectedPeriod = 1999892;
- auto expectedIntercept = 175409;
+ auto expectedIntercept = 86342;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -335,8 +335,8 @@
158929706370359,
};
auto const idealPeriod = 11111111;
- auto const expectedPeriod = 11113500;
- auto const expectedIntercept = -395335;
+ auto const expectedPeriod = 11113919;
+ auto const expectedIntercept = -1195945;
tracker.setPeriod(idealPeriod);
for (auto const& timestamp : simulatedVsyncs) {
@@ -355,6 +355,32 @@
EXPECT_THAT(prediction, Ge(timePoint));
}
+// See b/151146131
+TEST_F(VSyncPredictorTest, hasEnoughPrecision) {
+ VSyncPredictor tracker{mPeriod, 20, kMinimumSamplesForPrediction, kOutlierTolerancePercent};
+ std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675,
+ 840923581635, 840940161584, 840956868096,
+ 840973702473, 840990256277, 841007116851,
+ 841023722530, 841040452167, 841057073002,
+ 841073800920, 841090474360, 841107278632,
+ 841123898634, 841140750875, 841157287127,
+ 841591357014, 840856664232
+
+ };
+ auto const idealPeriod = 16666666;
+ auto const expectedPeriod = 16698426;
+ auto const expectedIntercept = 58055;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+}
+
TEST_F(VSyncPredictorTest, resetsWhenInstructed) {
auto const idealPeriod = 10000;
auto const realPeriod = 10500;
@@ -390,6 +416,22 @@
}
}
+constexpr nsecs_t operator""_years(unsigned long long years) noexcept {
+ using namespace std::chrono_literals;
+ return years * 365 * 24 * 3600 *
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count();
+}
+TEST_F(VSyncPredictorTest, aPhoneThatHasBeenAroundAWhileCanStillComputePeriod) {
+ constexpr nsecs_t timeBase = 100_years;
+
+ for (auto i = 0; i < kHistorySize; i++) {
+ tracker.addVsyncTimestamp(timeBase + i * mPeriod);
+ }
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(mPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, Eq(0));
+}
+
} // 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 ac95938..4f150ef 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -41,6 +41,7 @@
MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
MOCK_METHOD1(setPeriod, void(nsecs_t));
MOCK_METHOD0(resetModel, void());
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncTrackerWrapper : public VSyncTracker {
@@ -56,6 +57,7 @@
nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
void setPeriod(nsecs_t period) final { mTracker->setPeriod(period); }
void resetModel() final { mTracker->resetModel(); }
+ void dump(std::string& result) const final { mTracker->dump(result); }
private:
std::shared_ptr<VSyncTracker> const mTracker;
@@ -83,6 +85,7 @@
MOCK_METHOD1(unregisterCallback, void(CallbackToken));
MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+ MOCK_CONST_METHOD1(dump, void(std::string&));
};
class VSyncDispatchWrapper : public VSyncDispatch {
@@ -102,6 +105,8 @@
CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+ void dump(std::string& result) const final { return mDispatch->dump(result); }
+
private:
std::shared_ptr<VSyncDispatch> const mDispatch;
};
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
index fe57c98..e22d0cf 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h
@@ -29,6 +29,7 @@
PowerAdvisor();
~PowerAdvisor() override;
+ MOCK_METHOD0(onBootFinished, void());
MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected));
MOCK_METHOD0(notifyDisplayUpdateImminent, void());
};
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index e2f6abd..119f580 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -31,7 +31,7 @@
MOCK_METHOD0(getFrameSelectionPriority, int32_t());
MOCK_CONST_METHOD0(isVisible, bool());
MOCK_METHOD0(createClone, sp<Layer>());
- MOCK_CONST_METHOD0(getFrameRate, FrameRate());
+ MOCK_CONST_METHOD0(getFrameRateForLayerTree, FrameRate());
};
} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
index 458b2f3..5beee1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSurfaceInterceptor.h
@@ -39,7 +39,7 @@
const Vector<DisplayState>&, uint32_t));
MOCK_METHOD1(saveSurfaceCreation, void(const sp<const Layer>&));
MOCK_METHOD1(saveSurfaceDeletion, void(const sp<const Layer>&));
- MOCK_METHOD4(saveBufferUpdate, void(const sp<const Layer>&, uint32_t, uint32_t, uint64_t));
+ MOCK_METHOD4(saveBufferUpdate, void(int32_t, uint32_t, uint32_t, uint64_t));
MOCK_METHOD1(saveDisplayCreation, void(const DisplayDeviceState&));
MOCK_METHOD1(saveDisplayDeletion, void(int32_t));
MOCK_METHOD2(savePowerModeUpdate, void(int32_t, int32_t));
diff --git a/vulkan/TEST_MAPPING b/vulkan/TEST_MAPPING
new file mode 100644
index 0000000..d391dce
--- /dev/null
+++ b/vulkan/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsGpuToolsHostTestCases"
+ }
+ ]
+}
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index aae72db..5b9affd 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -29,6 +29,8 @@
#include <algorithm>
#include <mutex>
#include <new>
+#include <string>
+#include <unordered_set>
#include <utility>
#include <android-base/strings.h>
@@ -1194,6 +1196,23 @@
return initialized;
}
+template <typename Functor>
+void ForEachLayerFromSettings(Functor functor) {
+ const std::string layersSetting =
+ android::GraphicsEnv::getInstance().getDebugLayers();
+ if (!layersSetting.empty()) {
+ std::vector<std::string> layers =
+ android::base::Split(layersSetting, ":");
+ for (uint32_t i = 0; i < layers.size(); i++) {
+ const Layer* layer = FindLayer(layers[i].c_str());
+ if (!layer) {
+ continue;
+ }
+ functor(layer);
+ }
+ }
+}
+
} // anonymous namespace
VkResult CreateInstance(const VkInstanceCreateInfo* pCreateInfo,
@@ -1280,9 +1299,56 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- return vulkan::driver::EnumerateInstanceExtensionProperties(
- nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all instance extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerInstanceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ uint32_t count = 0;
+ VkResult result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = vulkan::driver::EnumerateInstanceExtensionProperties(
+ nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateDeviceLayerProperties(VkPhysicalDevice physicalDevice,
@@ -1334,10 +1400,57 @@
return *pPropertyCount < count ? VK_INCOMPLETE : VK_SUCCESS;
}
- // TODO(b/143293104): expose extensions from implicitly enabled layers
- const InstanceData& data = GetData(physicalDevice);
- return data.dispatch.EnumerateDeviceExtensionProperties(
- physicalDevice, nullptr, pPropertyCount, pProperties);
+ // If the pLayerName is nullptr, we must advertise all device extensions
+ // from all implicitly enabled layers and the driver implementation. If
+ // there are duplicates among layers and the driver implementation, always
+ // only preserve the top layer closest to the application regardless of the
+ // spec version.
+ std::vector<VkExtensionProperties> properties;
+ std::unordered_set<std::string> extensionNames;
+
+ // Expose extensions from implicitly enabled layers.
+ ForEachLayerFromSettings([&](const Layer* layer) {
+ uint32_t count = 0;
+ const VkExtensionProperties* props =
+ GetLayerDeviceExtensions(*layer, count);
+ if (count > 0) {
+ for (uint32_t i = 0; i < count; ++i) {
+ if (extensionNames.emplace(props[i].extensionName).second) {
+ properties.push_back(props[i]);
+ }
+ }
+ }
+ });
+
+ // TODO(b/143293104): Parse debug.vulkan.layers properties
+
+ // Expose extensions from driver implementation.
+ {
+ const InstanceData& data = GetData(physicalDevice);
+ uint32_t count = 0;
+ VkResult result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, nullptr);
+ if (result == VK_SUCCESS && count > 0) {
+ std::vector<VkExtensionProperties> props(count);
+ result = data.dispatch.EnumerateDeviceExtensionProperties(
+ physicalDevice, nullptr, &count, props.data());
+ for (auto prop : props) {
+ if (extensionNames.emplace(prop.extensionName).second) {
+ properties.push_back(prop);
+ }
+ }
+ }
+ }
+
+ uint32_t totalCount = properties.size();
+ if (!pProperties || *pPropertyCount > totalCount) {
+ *pPropertyCount = totalCount;
+ }
+ if (pProperties) {
+ std::copy(properties.data(), properties.data() + *pPropertyCount,
+ pProperties);
+ }
+ return *pPropertyCount < totalCount ? VK_INCOMPLETE : VK_SUCCESS;
}
VkResult EnumerateInstanceVersion(uint32_t* pApiVersion) {
diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc
index 8f714d8..b0b466c 100644
--- a/vulkan/vkjson/vkjson.cc
+++ b/vulkan/vkjson/vkjson.cc
@@ -591,6 +591,13 @@
}
template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
+ VkJsonExtShaderFloat16Int8Features* features) {
+ return visitor->Visit("shaderFloat16Int8FeaturesKHR",
+ &features->shader_float16_int8_features_khr);
+}
+
+template <typename Visitor>
inline bool Iterate(Visitor* visitor, VkMemoryType* type) {
return
visitor->Visit("propertyFlags", &type->propertyFlags) &&
@@ -692,6 +699,13 @@
template <typename Visitor>
inline bool Iterate(Visitor* visitor,
+ VkPhysicalDeviceShaderFloat16Int8FeaturesKHR* features) {
+ return visitor->Visit("shaderFloat16", &features->shaderFloat16) &&
+ visitor->Visit("shaderInt8", &features->shaderInt8);
+}
+
+template <typename Visitor>
+inline bool Iterate(Visitor* visitor,
VkPhysicalDeviceProtectedMemoryFeatures* features) {
return visitor->Visit("protectedMemory", &features->protectedMemory);
}
@@ -824,6 +838,10 @@
ret &= visitor->Visit("VK_KHR_variable_pointers",
&device->ext_variable_pointer_features);
}
+ if (device->ext_shader_float16_int8_features.reported) {
+ ret &= visitor->Visit("VK_KHR_shader_float16_int8",
+ &device->ext_shader_float16_int8_features);
+ }
}
return ret;
}
diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h
index 450fb24..a283b83 100644
--- a/vulkan/vkjson/vkjson.h
+++ b/vulkan/vkjson/vkjson.h
@@ -72,6 +72,16 @@
VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointer_features_khr;
};
+struct VkJsonExtShaderFloat16Int8Features {
+ VkJsonExtShaderFloat16Int8Features() {
+ reported = false;
+ memset(&shader_float16_int8_features_khr, 0,
+ sizeof(VkPhysicalDeviceShaderFloat16Int8FeaturesKHR));
+ }
+ bool reported;
+ VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_float16_int8_features_khr;
+};
+
struct VkJsonDevice {
VkJsonDevice() {
memset(&properties, 0, sizeof(VkPhysicalDeviceProperties));
@@ -101,6 +111,7 @@
VkPhysicalDeviceFeatures features;
VkJsonExtDriverProperties ext_driver_properties;
VkJsonExtVariablePointerFeatures ext_variable_pointer_features;
+ VkJsonExtShaderFloat16Int8Features ext_shader_float16_int8_features;
VkPhysicalDeviceMemoryProperties memory;
std::vector<VkQueueFamilyProperties> queues;
std::vector<VkExtensionProperties> extensions;
diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc
index 05d4dfe..84cfe5e 100644
--- a/vulkan/vkjson/vkjson_instance.cc
+++ b/vulkan/vkjson/vkjson_instance.cc
@@ -136,6 +136,16 @@
features.pNext =
&device.ext_variable_pointer_features.variable_pointer_features_khr;
}
+ if (HasExtension("VK_KHR_shader_float16_int8", device.extensions)) {
+ device.ext_shader_float16_int8_features.reported = true;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR;
+ device.ext_shader_float16_int8_features.shader_float16_int8_features_khr
+ .pNext = features.pNext;
+ features.pNext = &device.ext_shader_float16_int8_features
+ .shader_float16_int8_features_khr;
+ }
vkpGetPhysicalDeviceFeatures2KHR(physical_device, &features);
device.features = features.features;
} else {