Merge "CachedSet::dump: show the PixelFormat"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index f054596..6d837c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
cmds/idlcli/
+ cmds/installd/
cmds/servicemanager/
include/input/
include/powermanager/
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 9a8ec32..bb1d206 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -239,7 +239,7 @@
} },
{ "memory", "Memory", 0, {
{ OPT, "events/mm_event/mm_event_record/enable" },
- { OPT, "events/kmem/rss_stat/enable" },
+ { OPT, "events/synthetic/rss_stat_throttled/enable" },
{ OPT, "events/kmem/ion_heap_grow/enable" },
{ OPT, "events/kmem/ion_heap_shrink/enable" },
{ OPT, "events/ion/ion_stat/enable" },
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp
index 74dbf4b..a2491e5 100644
--- a/cmds/dumpstate/Android.bp
+++ b/cmds/dumpstate/Android.bp
@@ -86,9 +86,11 @@
shared_libs: [
"android.hardware.dumpstate@1.0",
"android.hardware.dumpstate@1.1",
+ "android.hardware.dumpstate-V1-ndk",
"libziparchive",
"libbase",
"libbinder",
+ "libbinder_ndk",
"libcrypto",
"libcutils",
"libdebuggerd_client",
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ba25a5a..77915d5 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -192,7 +192,7 @@
dprintf(fd, "progress:\n");
ds_->progress_->Dump(fd, " ");
dprintf(fd, "args: %s\n", ds_->options_->args.c_str());
- dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode.c_str());
+ dprintf(fd, "bugreport_mode: %s\n", ds_->options_->bugreport_mode_string.c_str());
dprintf(fd, "version: %s\n", ds_->version_.c_str());
dprintf(fd, "bugreport_dir: %s\n", destination.c_str());
dprintf(fd, "screenshot_path: %s\n", ds_->screenshot_path_.c_str());
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index eab72f4..32e680d 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -57,12 +57,15 @@
#include <utility>
#include <vector>
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android/content/pm/IPackageManagerNative.h>
#include <android/hardware/dumpstate/1.0/IDumpstateDevice.h>
#include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
@@ -89,11 +92,10 @@
#include "DumpstateService.h"
#include "dumpstate.h"
-using IDumpstateDevice_1_0 = ::android::hardware::dumpstate::V1_0::IDumpstateDevice;
-using IDumpstateDevice_1_1 = ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
-using ::android::hardware::dumpstate::V1_1::DumpstateMode;
-using ::android::hardware::dumpstate::V1_1::DumpstateStatus;
-using ::android::hardware::dumpstate::V1_1::toString;
+namespace dumpstate_hal_hidl_1_0 = android::hardware::dumpstate::V1_0;
+namespace dumpstate_hal_hidl = android::hardware::dumpstate::V1_1;
+namespace dumpstate_hal_aidl = aidl::android::hardware::dumpstate;
+
using ::std::literals::chrono_literals::operator""ms;
using ::std::literals::chrono_literals::operator""s;
using ::std::placeholders::_1;
@@ -807,7 +809,7 @@
printf("Bugreport format version: %s\n", version_.c_str());
printf("Dumpstate info: id=%d pid=%d dry_run=%d parallel_run=%d args=%s bugreport_mode=%s\n",
id_, pid_, PropertiesHelper::IsDryRun(), PropertiesHelper::IsParallelRun(),
- options_->args.c_str(), options_->bugreport_mode.c_str());
+ options_->args.c_str(), options_->bugreport_mode_string.c_str());
printf("\n");
}
@@ -2199,6 +2201,194 @@
return RunStatus::OK;
}
+static dumpstate_hal_hidl::DumpstateMode GetDumpstateHalModeHidl(
+ const Dumpstate::BugreportMode bugreport_mode) {
+ switch (bugreport_mode) {
+ case Dumpstate::BugreportMode::BUGREPORT_FULL:
+ return dumpstate_hal_hidl::DumpstateMode::FULL;
+ case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
+ return dumpstate_hal_hidl::DumpstateMode::INTERACTIVE;
+ case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
+ return dumpstate_hal_hidl::DumpstateMode::REMOTE;
+ case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+ return dumpstate_hal_hidl::DumpstateMode::WEAR;
+ case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
+ return dumpstate_hal_hidl::DumpstateMode::CONNECTIVITY;
+ case Dumpstate::BugreportMode::BUGREPORT_WIFI:
+ return dumpstate_hal_hidl::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
+ return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
+ }
+ return dumpstate_hal_hidl::DumpstateMode::DEFAULT;
+}
+
+static dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode GetDumpstateHalModeAidl(
+ const Dumpstate::BugreportMode bugreport_mode) {
+ switch (bugreport_mode) {
+ case Dumpstate::BugreportMode::BUGREPORT_FULL:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::FULL;
+ case Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::INTERACTIVE;
+ case Dumpstate::BugreportMode::BUGREPORT_REMOTE:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::REMOTE;
+ case Dumpstate::BugreportMode::BUGREPORT_WEAR:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WEAR;
+ case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::CONNECTIVITY;
+ case Dumpstate::BugreportMode::BUGREPORT_WIFI:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::WIFI;
+ case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
+ }
+ return dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode::DEFAULT;
+}
+
+static void DoDumpstateBoardHidl(
+ const sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_1_0,
+ const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds,
+ const Dumpstate::BugreportMode bugreport_mode,
+ const size_t timeout_sec) {
+
+ using ScopedNativeHandle =
+ std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>;
+ ScopedNativeHandle handle(native_handle_create(static_cast<int>(dumpstate_fds.size()), 0),
+ [](native_handle_t* handle) {
+ // we don't close file handle's here
+ // via native_handle_close(handle)
+ // instead we let dumpstate_fds close the file handles when
+ // dumpstate_fds gets destroyed
+ native_handle_delete(handle);
+ });
+ if (handle == nullptr) {
+ MYLOGE("Could not create native_handle for dumpstate HAL\n");
+ return;
+ }
+
+ for (size_t i = 0; i < dumpstate_fds.size(); i++) {
+ handle.get()->data[i] = dumpstate_fds[i].get();
+ }
+
+ // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
+ // implement just 1.0.
+ const char* descriptor_to_kill;
+ using DumpstateBoardTask = std::packaged_task<bool()>;
+ DumpstateBoardTask dumpstate_board_task;
+ sp<dumpstate_hal_hidl::IDumpstateDevice> dumpstate_hal(
+ dumpstate_hal_hidl::IDumpstateDevice::castFrom(dumpstate_hal_1_0));
+ if (dumpstate_hal != nullptr) {
+ MYLOGI("Using IDumpstateDevice v1.1 HIDL HAL");
+
+ dumpstate_hal_hidl::DumpstateMode dumpstate_hal_mode =
+ GetDumpstateHalModeHidl(bugreport_mode);
+
+ descriptor_to_kill = dumpstate_hal_hidl::IDumpstateDevice::descriptor;
+ dumpstate_board_task =
+ DumpstateBoardTask([timeout_sec, dumpstate_hal_mode, dumpstate_hal, &handle]() -> bool {
+ ::android::hardware::Return<dumpstate_hal_hidl::DumpstateStatus> status =
+ dumpstate_hal->dumpstateBoard_1_1(handle.get(), dumpstate_hal_mode,
+ SEC_TO_MSEC(timeout_sec));
+ if (!status.isOk()) {
+ MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+ return false;
+ } else if (status != dumpstate_hal_hidl::DumpstateStatus::OK) {
+ MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n",
+ dumpstate_hal_hidl::toString(status).c_str());
+ return false;
+ }
+ return true;
+ });
+ } else {
+ MYLOGI("Using IDumpstateDevice v1.0 HIDL HAL");
+
+ descriptor_to_kill = dumpstate_hal_hidl_1_0::IDumpstateDevice::descriptor;
+ dumpstate_board_task = DumpstateBoardTask([dumpstate_hal_1_0, &handle]() -> bool {
+ ::android::hardware::Return<void> status =
+ dumpstate_hal_1_0->dumpstateBoard(handle.get());
+ if (!status.isOk()) {
+ MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
+ return false;
+ }
+ return true;
+ });
+ }
+ auto result = dumpstate_board_task.get_future();
+ std::thread(std::move(dumpstate_board_task)).detach();
+
+ if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
+ MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec);
+ if (!android::base::SetProperty(
+ "ctl.interface_restart",
+ android::base::StringPrintf("%s/default", descriptor_to_kill))) {
+ MYLOGE("Couldn't restart dumpstate HAL\n");
+ }
+ }
+ // Wait some time for init to kill dumpstate vendor HAL
+ constexpr size_t killing_timeout_sec = 10;
+ if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
+ MYLOGE(
+ "killing dumpstateBoard timed out after %zus, continue and "
+ "there might be racing in content\n",
+ killing_timeout_sec);
+ }
+}
+
+static void DoDumpstateBoardAidl(
+ const std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal,
+ const std::vector<::ndk::ScopedFileDescriptor>& dumpstate_fds,
+ const Dumpstate::BugreportMode bugreport_mode, const size_t timeout_sec) {
+ MYLOGI("Using IDumpstateDevice AIDL HAL");
+
+ const char* descriptor_to_kill;
+ using DumpstateBoardTask = std::packaged_task<bool()>;
+ DumpstateBoardTask dumpstate_board_task;
+ dumpstate_hal_aidl::IDumpstateDevice::DumpstateMode dumpstate_hal_mode =
+ GetDumpstateHalModeAidl(bugreport_mode);
+
+ descriptor_to_kill = dumpstate_hal_aidl::IDumpstateDevice::descriptor;
+ dumpstate_board_task = DumpstateBoardTask([dumpstate_hal, &dumpstate_fds, dumpstate_hal_mode,
+ timeout_sec]() -> bool {
+ auto status = dumpstate_hal->dumpstateBoard(dumpstate_fds, dumpstate_hal_mode, timeout_sec);
+
+ if (!status.isOk()) {
+ MYLOGE("dumpstateBoard failed: %s\n", status.getDescription().c_str());
+ return false;
+ }
+ return true;
+ });
+ auto result = dumpstate_board_task.get_future();
+ std::thread(std::move(dumpstate_board_task)).detach();
+
+ if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
+ MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate HAL\n", timeout_sec);
+ if (!android::base::SetProperty(
+ "ctl.interface_restart",
+ android::base::StringPrintf("%s/default", descriptor_to_kill))) {
+ MYLOGE("Couldn't restart dumpstate HAL\n");
+ }
+ }
+ // Wait some time for init to kill dumpstate vendor HAL
+ constexpr size_t killing_timeout_sec = 10;
+ if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
+ MYLOGE(
+ "killing dumpstateBoard timed out after %zus, continue and "
+ "there might be racing in content\n",
+ killing_timeout_sec);
+ }
+}
+
+static std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> GetDumpstateBoardAidlService() {
+ const std::string aidl_instance_name =
+ std::string(dumpstate_hal_aidl::IDumpstateDevice::descriptor) + "/default";
+
+ if (!AServiceManager_isDeclared(aidl_instance_name.c_str())) {
+ return nullptr;
+ }
+
+ ndk::SpAIBinder dumpstateBinder(AServiceManager_waitForService(aidl_instance_name.c_str()));
+
+ return dumpstate_hal_aidl::IDumpstateDevice::fromBinder(dumpstateBinder);
+}
+
void Dumpstate::DumpstateBoard(int out_fd) {
dprintf(out_fd, "========================================================\n");
dprintf(out_fd, "== Board\n");
@@ -2220,8 +2410,7 @@
if (mount_debugfs) {
RunCommand("mount debugfs", {"mount", "-t", "debugfs", "debugfs", "/sys/kernel/debug"},
AS_ROOT_20);
- RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"},
- AS_ROOT_20);
+ RunCommand("chmod debugfs", {"chmod", "0755", "/sys/kernel/debug"}, AS_ROOT_20);
}
std::vector<std::string> paths;
@@ -2233,23 +2422,31 @@
std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
}
- sp<IDumpstateDevice_1_0> dumpstate_device_1_0(IDumpstateDevice_1_0::getService());
- if (dumpstate_device_1_0 == nullptr) {
- MYLOGE("No IDumpstateDevice implementation\n");
+ // get dumpstate HAL AIDL implementation
+ std::shared_ptr<dumpstate_hal_aidl::IDumpstateDevice> dumpstate_hal_handle_aidl(
+ GetDumpstateBoardAidlService());
+ if (dumpstate_hal_handle_aidl == nullptr) {
+ MYLOGI("No IDumpstateDevice AIDL implementation\n");
+ }
+
+ // get dumpstate HAL HIDL implementation, only if AIDL HAL implementation not found
+ sp<dumpstate_hal_hidl_1_0::IDumpstateDevice> dumpstate_hal_handle_hidl_1_0 = nullptr;
+ if (dumpstate_hal_handle_aidl == nullptr) {
+ dumpstate_hal_handle_hidl_1_0 = dumpstate_hal_hidl_1_0::IDumpstateDevice::getService();
+ if (dumpstate_hal_handle_hidl_1_0 == nullptr) {
+ MYLOGI("No IDumpstateDevice HIDL implementation\n");
+ }
+ }
+
+ // if neither HIDL nor AIDL implementation found, then return
+ if (dumpstate_hal_handle_hidl_1_0 == nullptr && dumpstate_hal_handle_aidl == nullptr) {
+ MYLOGE("Could not find IDumpstateDevice implementation\n");
return;
}
- using ScopedNativeHandle =
- std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>;
- ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0),
- [](native_handle_t* handle) {
- native_handle_close(handle);
- native_handle_delete(handle);
- });
- if (handle == nullptr) {
- MYLOGE("Could not create native_handle\n");
- return;
- }
+ // this is used to hold the file descriptors and when this variable goes out of scope
+ // the file descriptors are closed
+ std::vector<::ndk::ScopedFileDescriptor> dumpstate_fds;
// TODO(128270426): Check for consent in between?
for (size_t i = 0; i < paths.size(); i++) {
@@ -2262,65 +2459,26 @@
MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno));
return;
}
- handle.get()->data[i] = fd.release();
+
+ dumpstate_fds.emplace_back(fd.release());
+ // we call fd.release() here to make sure "fd" does not get closed
+ // after "fd" goes out of scope after this block.
+ // "fd" will be closed when "dumpstate_fds" goes out of scope
+ // i.e. when we exit this function
}
// Given that bugreport is required to diagnose failures, it's better to set an arbitrary amount
// of timeout for IDumpstateDevice than to block the rest of bugreport. In the timeout case, we
// will kill the HAL and grab whatever it dumped in time.
constexpr size_t timeout_sec = 30;
- // Prefer version 1.1 if available. New devices launching with R are no longer allowed to
- // implement just 1.0.
- const char* descriptor_to_kill;
- using DumpstateBoardTask = std::packaged_task<bool()>;
- DumpstateBoardTask dumpstate_board_task;
- sp<IDumpstateDevice_1_1> dumpstate_device_1_1(
- IDumpstateDevice_1_1::castFrom(dumpstate_device_1_0));
- if (dumpstate_device_1_1 != nullptr) {
- MYLOGI("Using IDumpstateDevice v1.1");
- descriptor_to_kill = IDumpstateDevice_1_1::descriptor;
- dumpstate_board_task = DumpstateBoardTask([this, dumpstate_device_1_1, &handle]() -> bool {
- ::android::hardware::Return<DumpstateStatus> status =
- dumpstate_device_1_1->dumpstateBoard_1_1(handle.get(), options_->dumpstate_hal_mode,
- SEC_TO_MSEC(timeout_sec));
- if (!status.isOk()) {
- MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
- return false;
- } else if (status != DumpstateStatus::OK) {
- MYLOGE("dumpstateBoard failed with DumpstateStatus::%s\n", toString(status).c_str());
- return false;
- }
- return true;
- });
- } else {
- MYLOGI("Using IDumpstateDevice v1.0");
- descriptor_to_kill = IDumpstateDevice_1_0::descriptor;
- dumpstate_board_task = DumpstateBoardTask([dumpstate_device_1_0, &handle]() -> bool {
- ::android::hardware::Return<void> status =
- dumpstate_device_1_0->dumpstateBoard(handle.get());
- if (!status.isOk()) {
- MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str());
- return false;
- }
- return true;
- });
- }
- auto result = dumpstate_board_task.get_future();
- std::thread(std::move(dumpstate_board_task)).detach();
- if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) {
- MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec);
- if (!android::base::SetProperty(
- "ctl.interface_restart",
- android::base::StringPrintf("%s/default", descriptor_to_kill))) {
- MYLOGE("Couldn't restart dumpstate HAL\n");
- }
- }
- // Wait some time for init to kill dumpstate vendor HAL
- constexpr size_t killing_timeout_sec = 10;
- if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) {
- MYLOGE("killing dumpstateBoard timed out after %zus, continue and "
- "there might be racing in content\n", killing_timeout_sec);
+ if (dumpstate_hal_handle_aidl != nullptr) {
+ DoDumpstateBoardAidl(dumpstate_hal_handle_aidl, dumpstate_fds, options_->bugreport_mode,
+ timeout_sec);
+ } else if (dumpstate_hal_handle_hidl_1_0 != nullptr) {
+ // run HIDL HAL only if AIDL HAL not found
+ DoDumpstateBoardHidl(dumpstate_hal_handle_hidl_1_0, dumpstate_fds, options_->bugreport_mode,
+ timeout_sec);
}
if (mount_debugfs) {
@@ -2333,9 +2491,8 @@
auto file_sizes = std::make_unique<ssize_t[]>(paths.size());
for (size_t i = 0; i < paths.size(); i++) {
struct stat s;
- if (fstat(handle.get()->data[i], &s) == -1) {
- MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(),
- strerror(errno));
+ if (fstat(dumpstate_fds[i].get(), &s) == -1) {
+ MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), strerror(errno));
file_sizes[i] = -1;
continue;
}
@@ -2574,40 +2731,35 @@
bool is_screenshot_requested) {
// Modify com.android.shell.BugreportProgressService#isDefaultScreenshotRequired as well for
// default system screenshots.
- options->bugreport_mode = ModeToString(mode);
+ options->bugreport_mode = mode;
+ options->bugreport_mode_string = ModeToString(mode);
switch (mode) {
case Dumpstate::BugreportMode::BUGREPORT_FULL:
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_progress_updates = true;
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_screenshot = false;
- options->dumpstate_hal_mode = DumpstateMode::REMOTE;
break;
case Dumpstate::BugreportMode::BUGREPORT_WEAR:
options->do_progress_updates = 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_screenshot = false;
- options->dumpstate_hal_mode = DumpstateMode::CONNECTIVITY;
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
options->wifi_only = true;
options->do_screenshot = false;
- options->dumpstate_hal_mode = DumpstateMode::WIFI;
break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
break;
@@ -2618,13 +2770,14 @@
MYLOGI(
"do_vibrate: %d stream_to_socket: %d progress_updates_to_socket: %d do_screenshot: %d "
"is_remote_mode: %d show_header_only: %d telephony_only: %d "
- "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s dumpstate_hal_mode: %s "
+ "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s "
"limited_only: %d args: %s\n",
options.do_vibrate, options.stream_to_socket, options.progress_updates_to_socket,
options.do_screenshot, options.is_remote_mode, options.show_header_only,
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.limited_only, options.args.c_str());
+ options.do_progress_updates, options.bugreport_fd.get(),
+ options.bugreport_mode_string.c_str(),
+ options.limited_only, options.args.c_str());
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2838,7 +2991,7 @@
}
MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
- id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
+ id_, options_->args.c_str(), options_->bugreport_mode_string.c_str(), version_.c_str());
do_early_screenshot_ = options_->do_progress_updates;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3722383..773e292 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -25,6 +25,7 @@
#include <string>
#include <vector>
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
#include <android-base/macros.h>
#include <android-base/unique_fd.h>
#include <android/hardware/dumpstate/1.1/types.h>
@@ -400,19 +401,18 @@
bool limited_only = false;
// Whether progress updates should be published.
bool do_progress_updates = false;
- // The mode we'll use when calling IDumpstateDevice::dumpstateBoard.
+ // this is used to derive dumpstate HAL bug report mode
// TODO(b/148168577) get rid of the AIDL values, replace them with the HAL values instead.
// The HAL is actually an API surface that can be validated, while the AIDL is not (@hide).
- ::android::hardware::dumpstate::V1_1::DumpstateMode dumpstate_hal_mode =
- ::android::hardware::dumpstate::V1_1::DumpstateMode::DEFAULT;
+ BugreportMode bugreport_mode = Dumpstate::BugreportMode::BUGREPORT_DEFAULT;
// File descriptor to output zip file. Takes precedence over out_dir.
android::base::unique_fd bugreport_fd;
// File descriptor to screenshot file.
android::base::unique_fd screenshot_fd;
// Custom output directory.
std::string out_dir;
- // Bugreport mode of the bugreport.
- std::string bugreport_mode;
+ // Bugreport mode of the bugreport as a string
+ std::string bugreport_mode_string;
// Command-line arguments as string
std::string args;
// Notification title and description
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index db508b5..42beb2b 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -33,6 +33,7 @@
#include <unistd.h>
#include <thread>
+#include <aidl/android/hardware/dumpstate/IDumpstateDevice.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -47,6 +48,7 @@
namespace os {
namespace dumpstate {
+using DumpstateDeviceAidl = ::aidl::android::hardware::dumpstate::IDumpstateDevice;
using ::android::hardware::dumpstate::V1_1::DumpstateMode;
using ::testing::EndsWith;
using ::testing::Eq;
@@ -186,7 +188,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.limited_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeAdbBugreport) {
@@ -210,7 +211,6 @@
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.limited_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeAdbShellBugreport) {
@@ -234,13 +234,11 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.limited_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeFullBugReport) {
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_FULL, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::FULL);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -256,7 +254,6 @@
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, fd, fd, true);
EXPECT_TRUE(options_.do_progress_updates);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::INTERACTIVE);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -272,7 +269,6 @@
EXPECT_TRUE(options_.is_remote_mode);
EXPECT_FALSE(options_.do_vibrate);
EXPECT_FALSE(options_.do_screenshot);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::REMOTE);
// Other options retain default values
EXPECT_FALSE(options_.progress_updates_to_socket);
@@ -286,7 +282,7 @@
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WEAR, fd, fd, true);
EXPECT_TRUE(options_.do_screenshot);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WEAR);
+
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -302,7 +298,6 @@
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.telephony_only);
EXPECT_TRUE(options_.do_progress_updates);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::CONNECTIVITY);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -317,7 +312,6 @@
options_.Initialize(Dumpstate::BugreportMode::BUGREPORT_WIFI, fd, fd, false);
EXPECT_FALSE(options_.do_screenshot);
EXPECT_TRUE(options_.wifi_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::WIFI);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -354,7 +348,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.stream_to_socket);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeDefaultBugReport) {
@@ -371,7 +364,6 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_screenshot);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
@@ -408,7 +400,6 @@
EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.limited_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializePartial2) {
@@ -436,7 +427,6 @@
EXPECT_FALSE(options_.stream_to_socket);
EXPECT_FALSE(options_.progress_updates_to_socket);
EXPECT_FALSE(options_.limited_only);
- EXPECT_EQ(options_.dumpstate_hal_mode, DumpstateMode::DEFAULT);
}
TEST_F(DumpOptionsTest, InitializeHelp) {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 3f180d9..faa8485 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -45,6 +45,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "libziparchive",
"server_configurable_flags",
],
static_libs: [
@@ -267,6 +268,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "libziparchive",
"server_configurable_flags",
],
}
diff --git a/cmds/installd/CacheItem.cpp b/cmds/installd/CacheItem.cpp
index e29ff4c..27690a3 100644
--- a/cmds/installd/CacheItem.cpp
+++ b/cmds/installd/CacheItem.cpp
@@ -116,6 +116,7 @@
break;
}
}
+ fts_close(fts);
} else {
if (tombstone) {
if (truncate(path.c_str(), 0) != 0) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 8d23efc..2935c6a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -77,6 +77,8 @@
#define LOG_TAG "installd"
#endif
+#define GRANULAR_LOCKS
+
using android::base::ParseUint;
using android::base::StringPrintf;
using std::endl;
@@ -265,6 +267,104 @@
} \
}
+#ifdef GRANULAR_LOCKS
+
+/**
+ * This class obtains in constructor and keeps the local strong pointer to the RefLock.
+ * On destruction, it checks if there are any other strong pointers, and remove the map entry if
+ * this was the last one.
+ */
+template <class Key, class Mutex>
+struct LocalLockHolder {
+ using WeakPointer = std::weak_ptr<Mutex>;
+ using StrongPointer = std::shared_ptr<Mutex>;
+ using Map = std::unordered_map<Key, WeakPointer>;
+ using MapLock = std::recursive_mutex;
+
+ LocalLockHolder(Key key, Map& map, MapLock& mapLock)
+ : mKey(std::move(key)), mMap(map), mMapLock(mapLock) {
+ std::lock_guard lock(mMapLock);
+ auto& weakPtr = mMap[mKey];
+
+ // Check if the RefLock is still alive.
+ mRefLock = weakPtr.lock();
+ if (!mRefLock) {
+ // Create a new lock.
+ mRefLock = std::make_shared<Mutex>();
+ weakPtr = mRefLock;
+ }
+ }
+ LocalLockHolder(LocalLockHolder&& other) noexcept
+ : mKey(std::move(other.mKey)),
+ mMap(other.mMap),
+ mMapLock(other.mMapLock),
+ mRefLock(std::move(other.mRefLock)) {
+ other.mRefLock.reset();
+ }
+ ~LocalLockHolder() {
+ if (!mRefLock) {
+ return;
+ }
+
+ std::lock_guard lock(mMapLock);
+ // Clear the strong pointer.
+ mRefLock.reset();
+ auto found = mMap.find(mKey);
+ if (found == mMap.end()) {
+ return;
+ }
+ const auto& weakPtr = found->second;
+ // If this was the last pointer then it's ok to remove the map entry.
+ if (weakPtr.expired()) {
+ mMap.erase(found);
+ }
+ }
+
+ void lock() { mRefLock->lock(); }
+ void unlock() { mRefLock->unlock(); }
+ void lock_shared() { mRefLock->lock_shared(); }
+ void unlock_shared() { mRefLock->unlock_shared(); }
+
+private:
+ Key mKey;
+ Map& mMap;
+ MapLock& mMapLock;
+ StrongPointer mRefLock;
+};
+
+using UserLock = LocalLockHolder<userid_t, std::shared_mutex>;
+using UserWriteLockGuard = std::unique_lock<UserLock>;
+using UserReadLockGuard = std::shared_lock<UserLock>;
+
+using PackageLock = LocalLockHolder<std::string, std::recursive_mutex>;
+using PackageLockGuard = std::lock_guard<PackageLock>;
+
+#define LOCK_USER() \
+ UserLock localUserLock(userId, mUserIdLock, mLock); \
+ UserWriteLockGuard userLock(localUserLock)
+
+#define LOCK_USER_READ() \
+ UserLock localUserLock(userId, mUserIdLock, mLock); \
+ UserReadLockGuard userLock(localUserLock)
+
+#define LOCK_PACKAGE() \
+ PackageLock localPackageLock(packageName, mPackageNameLock, mLock); \
+ PackageLockGuard packageLock(localPackageLock)
+
+#define LOCK_PACKAGE_USER() \
+ LOCK_USER_READ(); \
+ LOCK_PACKAGE()
+
+#else
+
+#define LOCK_USER() std::lock_guard lock(mLock)
+#define LOCK_PACKAGE() std::lock_guard lock(mLock)
+#define LOCK_PACKAGE_USER() \
+ (void)userId; \
+ std::lock_guard lock(mLock)
+
+#endif // GRANULAR_LOCKS
+
} // namespace
status_t InstalldNativeService::start() {
@@ -288,8 +388,6 @@
return PERMISSION_DENIED;
}
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
{
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
dprintf(fd, "Storage mounts:\n");
@@ -543,14 +641,13 @@
return ok();
}
-binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
- const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
- int64_t* _aidl_return) {
+binder::Status InstalldNativeService::createAppDataLocked(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
+ int32_t targetSdkVersion, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -619,10 +716,22 @@
}
binder::Status InstalldNativeService::createAppData(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, int32_t previousAppId, const std::string& seInfo,
+ int32_t targetSdkVersion, int64_t* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ LOCK_PACKAGE_USER();
+ return createAppDataLocked(uuid, packageName, userId, flags, appId, previousAppId, seInfo,
+ targetSdkVersion, _aidl_return);
+}
+
+binder::Status InstalldNativeService::createAppData(
const android::os::CreateAppDataArgs& args,
android::os::CreateAppDataResult* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ // Locking is performed depeer in the callstack.
int64_t ceDataInode = -1;
auto status = createAppData(args.uuid, args.packageName, args.userId, args.flags, args.appId,
@@ -637,7 +746,7 @@
const std::vector<android::os::CreateAppDataArgs>& args,
std::vector<android::os::CreateAppDataResult>* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ // Locking is performed depeer in the callstack.
std::vector<android::os::CreateAppDataResult> results;
for (const auto &arg : args) {
@@ -654,7 +763,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -698,7 +807,7 @@
const std::string& profileName) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
binder::Status res = ok();
if (!clear_primary_reference_profile(packageName, profileName)) {
@@ -715,7 +824,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -812,7 +921,7 @@
binder::Status InstalldNativeService::destroyAppProfiles(const std::string& packageName) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
binder::Status res = ok();
std::vector<userid_t> users = get_known_users(/*volume_uuid*/ nullptr);
@@ -832,7 +941,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -903,15 +1012,15 @@
int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- std::lock_guard<std::recursive_mutex> lock(mLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
- for (auto user : get_known_users(uuid_)) {
+ for (auto userId : get_known_users(uuid_)) {
+ LOCK_USER();
ATRACE_BEGIN("fixup user");
FTS* fts;
FTSENT* p;
- auto ce_path = create_data_user_ce_path(uuid_, user);
- auto de_path = create_data_user_de_path(uuid_, user);
+ auto ce_path = create_data_user_ce_path(uuid_, userId);
+ auto de_path = create_data_user_de_path(uuid_, userId);
char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(), nullptr };
if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
return error("Failed to fts_open");
@@ -1018,14 +1127,14 @@
return logwrap_fork_execvp(ARRAY_SIZE(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
}
-binder::Status InstalldNativeService::snapshotAppData(
- const std::optional<std::string>& volumeUuid,
- const std::string& packageName, int32_t user, int32_t snapshotId,
- int32_t storageFlags, int64_t* _aidl_return) {
+binder::Status InstalldNativeService::snapshotAppData(const std::optional<std::string>& volumeUuid,
+ const std::string& packageName,
+ int32_t userId, int32_t snapshotId,
+ int32_t storageFlags, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
const char* package_name = packageName.c_str();
@@ -1038,19 +1147,19 @@
bool clear_ce_on_exit = false;
bool clear_de_on_exit = false;
- auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name,
- &snapshotId] {
+ auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &userId, &package_name,
+ &snapshotId] {
if (clear_de_on_exit) {
- auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId,
- package_name);
+ auto to = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to delete app data snapshot: " << to;
}
}
if (clear_ce_on_exit) {
- auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId,
- package_name);
+ auto to = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to delete app data snapshot: " << to;
}
@@ -1060,10 +1169,11 @@
auto scope_guard = android::base::make_scope_guard(deleter);
if (storageFlags & FLAG_STORAGE_DE) {
- auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
- auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
+ auto from = create_data_user_de_package_path(volume_uuid, userId, package_name);
+ auto to = create_data_misc_de_rollback_path(volume_uuid, userId, snapshotId);
+ auto rollback_package_path =
+ create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
if (rc != 0) {
@@ -1087,15 +1197,15 @@
}
// The app may not have any data at all, in which case it's OK to skip here.
- auto from_ce = create_data_user_ce_package_path(volume_uuid, user, package_name);
+ auto from_ce = create_data_user_ce_package_path(volume_uuid, userId, package_name);
if (access(from_ce.c_str(), F_OK) != 0) {
LOG(INFO) << "Missing source " << from_ce;
return ok();
}
// ce_data_inode is not needed when FLAG_CLEAR_CACHE_ONLY is set.
- binder::Status clear_cache_result = clearAppData(volumeUuid, packageName, user,
- storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
+ binder::Status clear_cache_result =
+ clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CACHE_ONLY, 0);
if (!clear_cache_result.isOk()) {
// It should be fine to continue snapshot if we for some reason failed
// to clear cache.
@@ -1103,8 +1213,9 @@
}
// ce_data_inode is not needed when FLAG_CLEAR_CODE_CACHE_ONLY is set.
- binder::Status clear_code_cache_result = clearAppData(volumeUuid, packageName, user,
- storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY, 0);
+ binder::Status clear_code_cache_result =
+ clearAppData(volumeUuid, packageName, userId, storageFlags | FLAG_CLEAR_CODE_CACHE_ONLY,
+ 0);
if (!clear_code_cache_result.isOk()) {
// It should be fine to continue snapshot if we for some reason failed
// to clear code_cache.
@@ -1112,10 +1223,11 @@
}
if (storageFlags & FLAG_STORAGE_CE) {
- auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
- auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
+ auto from = create_data_user_ce_package_path(volume_uuid, userId, package_name);
+ auto to = create_data_misc_ce_rollback_path(volume_uuid, userId, snapshotId);
+ auto rollback_package_path =
+ create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
if (rc != 0) {
@@ -1134,8 +1246,9 @@
return res;
}
if (_aidl_return != nullptr) {
- auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
- snapshotId, package_name);
+ auto ce_snapshot_path =
+ create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
if (rc != 0) {
res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
@@ -1150,20 +1263,20 @@
binder::Status InstalldNativeService::restoreAppDataSnapshot(
const std::optional<std::string>& volumeUuid, const std::string& packageName,
- const int32_t appId, const std::string& seInfo, const int32_t user,
+ const int32_t appId, const std::string& seInfo, const int32_t userId,
const int32_t snapshotId, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
const char* package_name = packageName.c_str();
- auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
- user, snapshotId, package_name);
- auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
- user, snapshotId, package_name);
+ auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
+ auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name);
const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) &&
(access(from_ce.c_str(), F_OK) == 0);
@@ -1183,14 +1296,14 @@
// It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot
// can only be called when user unlocks the phone, meaning that CE user data
// is decrypted.
- binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags,
- 0 /* ceDataInode */);
+ binder::Status res =
+ clearAppData(volumeUuid, packageName, userId, storageFlags, 0 /* ceDataInode */);
if (!res.isOk()) {
return res;
}
if (needs_ce_rollback) {
- auto to_ce = create_data_user_ce_path(volume_uuid, user);
+ auto to_ce = create_data_user_ce_path(volume_uuid, userId);
int rc = copy_directory_recursive(from_ce.c_str(), to_ce.c_str());
if (rc != 0) {
res = error(rc, "Failed copying " + from_ce + " to " + to_ce);
@@ -1200,11 +1313,11 @@
}
if (needs_de_rollback) {
- auto to_de = create_data_user_de_path(volume_uuid, user);
+ auto to_de = create_data_user_de_path(volume_uuid, userId);
int rc = copy_directory_recursive(from_de.c_str(), to_de.c_str());
if (rc != 0) {
if (needs_ce_rollback) {
- auto ce_data = create_data_user_ce_package_path(volume_uuid, user, package_name);
+ auto ce_data = create_data_user_ce_package_path(volume_uuid, userId, package_name);
LOG(WARNING) << "de_data rollback failed. Erasing rolled back ce_data " << ce_data;
if (delete_dir_contents(ce_data.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to delete rolled back ce_data " << ce_data;
@@ -1217,24 +1330,24 @@
}
// Finally, restore the SELinux label on the app data.
- return restoreconAppData(volumeUuid, packageName, user, storageFlags, appId, seInfo);
+ return restoreconAppData(volumeUuid, packageName, userId, storageFlags, appId, seInfo);
}
binder::Status InstalldNativeService::destroyAppDataSnapshot(
- const std::optional<std::string> &volumeUuid, const std::string& packageName,
- const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
+ const std::optional<std::string>& volumeUuid, const std::string& packageName,
+ const int32_t userId, const int64_t ceSnapshotInode, const int32_t snapshotId,
int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
const char* package_name = packageName.c_str();
if (storageFlags & FLAG_STORAGE_DE) {
- auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
- user, snapshotId, package_name);
+ auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, userId,
+ snapshotId, package_name);
int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
if (res != 0) {
@@ -1243,8 +1356,9 @@
}
if (storageFlags & FLAG_STORAGE_CE) {
- auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
- user, snapshotId, package_name, ceSnapshotInode);
+ auto ce_snapshot_path =
+ create_data_misc_ce_rollback_package_path(volume_uuid, userId, snapshotId,
+ package_name, ceSnapshotInode);
int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
if (res != 0) {
return error(res, "Failed clearing snapshot " + ce_snapshot_path);
@@ -1254,15 +1368,15 @@
}
binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified(
- const std::optional<std::string> &volumeUuid, const int32_t user,
+ const std::optional<std::string>& volumeUuid, const int32_t userId,
const std::vector<int32_t>& retainSnapshotIds) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_USER();
const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr;
- auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user);
+ auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId);
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir);
if (!dir) {
@@ -1280,8 +1394,8 @@
if (parse_ok &&
std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(),
snapshot_id) == retainSnapshotIds.end()) {
- auto rollback_path = create_data_misc_ce_rollback_path(
- volume_uuid, user, snapshot_id);
+ auto rollback_path =
+ create_data_misc_ce_rollback_path(volume_uuid, userId, snapshot_id);
int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */);
if (res != 0) {
return error(res, "Failed clearing snapshot " + rollback_path);
@@ -1299,7 +1413,7 @@
CHECK_ARGUMENT_UUID(fromUuid);
CHECK_ARGUMENT_UUID(toUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
const char* from_uuid = fromUuid ? fromUuid->c_str() : nullptr;
const char* to_uuid = toUuid ? toUuid->c_str() : nullptr;
@@ -1327,24 +1441,25 @@
}
// Copy private data for all known users
- for (auto user : users) {
+ for (auto userId : users) {
+ LOCK_USER();
// Data source may not exist for all users; that's okay
- auto from_ce = create_data_user_ce_package_path(from_uuid, user, package_name);
+ auto from_ce = create_data_user_ce_package_path(from_uuid, userId, package_name);
if (access(from_ce.c_str(), F_OK) != 0) {
LOG(INFO) << "Missing source " << from_ce;
continue;
}
- if (!createAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE, appId,
- /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr).isOk()) {
+ if (!createAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+ appId, /* previousAppId */ -1, seInfo, targetSdkVersion, nullptr)
+ .isOk()) {
res = error("Failed to create package target");
goto fail;
}
-
{
- auto from = create_data_user_de_package_path(from_uuid, user, package_name);
- auto to = create_data_user_de_path(to_uuid, user);
+ auto from = create_data_user_de_package_path(from_uuid, userId, package_name);
+ auto to = create_data_user_de_path(to_uuid, userId);
int rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
@@ -1353,8 +1468,8 @@
}
}
{
- auto from = create_data_user_ce_package_path(from_uuid, user, package_name);
- auto to = create_data_user_ce_path(to_uuid, user);
+ auto from = create_data_user_ce_package_path(from_uuid, userId, package_name);
+ auto to = create_data_user_ce_path(to_uuid, userId);
int rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
@@ -1363,8 +1478,9 @@
}
}
- if (!restoreconAppData(toUuid, packageName, user, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
- appId, seInfo).isOk()) {
+ if (!restoreconAppDataLocked(toUuid, packageName, userId, FLAG_STORAGE_CE | FLAG_STORAGE_DE,
+ appId, seInfo)
+ .isOk()) {
res = error("Failed to restorecon");
goto fail;
}
@@ -1382,15 +1498,16 @@
LOG(WARNING) << "Failed to rollback " << to_app_package_path;
}
}
- for (auto user : users) {
+ for (auto userId : users) {
+ LOCK_USER();
{
- auto to = create_data_user_de_package_path(to_uuid, user, package_name);
+ auto to = create_data_user_de_package_path(to_uuid, userId, package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to rollback " << to;
}
}
{
- auto to = create_data_user_ce_package_path(to_uuid, user, package_name);
+ auto to = create_data_user_ce_package_path(to_uuid, userId, package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to rollback " << to;
}
@@ -1403,7 +1520,7 @@
int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
if (flags & FLAG_STORAGE_DE) {
@@ -1421,7 +1538,7 @@
int32_t userId, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
binder::Status res = ok();
@@ -1458,7 +1575,9 @@
int64_t targetFreeBytes, int32_t flags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+#ifndef GRANULAR_LOCKS
+ std::lock_guard lock(mLock);
+#endif // !GRANULAR_LOCKS
auto uuidString = uuid.value_or("");
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -1485,13 +1604,24 @@
// 1. Create trackers for every known UID
ATRACE_BEGIN("create");
+ const auto users = get_known_users(uuid_);
+#ifdef GRANULAR_LOCKS
+ std::vector<UserLock> userLocks;
+ userLocks.reserve(users.size());
+ std::vector<UserWriteLockGuard> lockGuards;
+ lockGuards.reserve(users.size());
+#endif // GRANULAR_LOCKS
std::unordered_map<uid_t, std::shared_ptr<CacheTracker>> trackers;
- for (auto user : get_known_users(uuid_)) {
+ for (auto userId : users) {
+#ifdef GRANULAR_LOCKS
+ userLocks.emplace_back(userId, mUserIdLock, mLock);
+ lockGuards.emplace_back(userLocks.back());
+#endif // GRANULAR_LOCKS
FTS *fts;
FTSENT *p;
- auto ce_path = create_data_user_ce_path(uuid_, user);
- auto de_path = create_data_user_de_path(uuid_, user);
- auto media_path = findDataMediaPath(uuid, user) + "/Android/data/";
+ auto ce_path = create_data_user_ce_path(uuid_, userId);
+ auto de_path = create_data_user_de_path(uuid_, userId);
+ auto media_path = findDataMediaPath(uuid, userId) + "/Android/data/";
char *argv[] = { (char*) ce_path.c_str(), (char*) de_path.c_str(),
(char*) media_path.c_str(), nullptr };
if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
@@ -1621,7 +1751,6 @@
const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(codePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
char dex_path[PKG_PATH_MAX];
@@ -1851,7 +1980,18 @@
}
fts_close(fts);
}
-
+static bool ownsExternalStorage(int32_t appId) {
+ // Fetch external storage owner appid and check if it is the same as the
+ // current appId whose size is calculated
+ struct stat s;
+ auto _picDir = StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str());
+ // check if the stat are present
+ if (stat(_picDir.c_str(), &s) == 0) {
+ // fetch the appId from the uid of the media app
+ return ((int32_t)multiuser_get_app_id(s.st_uid) == appId);
+ }
+ return false;
+}
binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
int32_t appId, const std::vector<int64_t>& ceDataInodes,
@@ -1906,8 +2046,10 @@
calculate_tree_size(obbCodePath, &extStats.codeSize);
}
ATRACE_END();
-
- if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START) {
+ // Calculating the app size of the external storage owning app in a manual way, since
+ // calculating it through quota apis also includes external media storage in the app storage
+ // numbers
+ if (flags & FLAG_USE_QUOTA && appId >= AID_APP_START && !ownsExternalStorage(appId)) {
ATRACE_BEGIN("code");
for (const auto& codePath : codePaths) {
calculate_tree_size(codePath, &stats.codeSize, -1,
@@ -2337,7 +2479,7 @@
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
}
#ifdef ENABLE_STORAGE_CRATES
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
auto retVector = std::vector<std::optional<CrateMetadata>>();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
@@ -2383,7 +2525,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
#ifdef ENABLE_STORAGE_CRATES
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto retVector = std::vector<std::optional<CrateMetadata>>();
@@ -2440,7 +2582,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(codePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
*_aidl_return = dump_profiles(uid, packageName, profileName, codePath);
return ok();
@@ -2452,7 +2594,7 @@
bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
*_aidl_return = copy_system_profile(systemProfile, packageUid, packageName, profileName);
return ok();
}
@@ -2462,7 +2604,7 @@
const std::string& profileName, int* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
*_aidl_return = analyze_primary_profiles(uid, packageName, profileName);
return ok();
@@ -2473,7 +2615,7 @@
const std::string& classpath, bool* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
*_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath);
return ok();
@@ -2483,7 +2625,7 @@
const std::string& profileName) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
std::string snapshot = create_snapshot_profile_path(packageName, profileName);
if ((unlink(snapshot.c_str()) != 0) && (errno != ENOENT)) {
@@ -2496,35 +2638,34 @@
const char* default_value = nullptr) {
return data ? data->c_str() : default_value;
}
-binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid,
- const std::optional<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
+binder::Status InstalldNativeService::dexopt(
+ const std::string& apkPath, int32_t uid, const std::string& packageName,
+ const std::string& instructionSet, int32_t dexoptNeeded,
+ const std::optional<std::string>& outputPath, int32_t dexFlags,
const std::string& compilerFilter, const std::optional<std::string>& uuid,
const std::optional<std::string>& classLoaderContext,
const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion,
const std::optional<std::string>& profileName,
const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason,
- bool* aidl_return) {
+ const std::optional<std::string>& compilationReason, bool* aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PATH(apkPath);
- if (packageName && *packageName != "*") {
- CHECK_ARGUMENT_PACKAGE_NAME(*packageName);
- }
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(outputPath);
CHECK_ARGUMENT_PATH(dexMetadataPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ const auto userId = multiuser_get_user_id(uid);
+ LOCK_PACKAGE_USER();
const char* oat_dir = getCStr(outputPath);
const char* instruction_set = instructionSet.c_str();
- if (oat_dir != nullptr && !createOatDir(oat_dir, instruction_set).isOk()) {
+ if (oat_dir != nullptr && !createOatDir(packageName, oat_dir, instruction_set).isOk()) {
// Can't create oat dir - let dexopt use cache dir.
oat_dir = nullptr;
}
const char* apk_path = apkPath.c_str();
- const char* pkgname = getCStr(packageName, "*");
+ const char* pkgname = packageName.c_str();
const char* compiler_filter = compilerFilter.c_str();
const char* volume_uuid = getCStr(uuid);
const char* class_loader_context = getCStr(classLoaderContext);
@@ -2565,7 +2706,7 @@
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(nativeLibPath32);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
const char* pkgname = packageName.c_str();
@@ -2656,7 +2797,16 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID(uuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
+ return restoreconAppDataLocked(uuid, packageName, userId, flags, appId, seInfo);
+}
+
+binder::Status InstalldNativeService::restoreconAppDataLocked(
+ const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, const std::string& seInfo) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
binder::Status res = ok();
@@ -2682,11 +2832,13 @@
return res;
}
-binder::Status InstalldNativeService::createOatDir(const std::string& oatDir,
- const std::string& instructionSet) {
+binder::Status InstalldNativeService::createOatDir(const std::string& packageName,
+ const std::string& oatDir,
+ const std::string& instructionSet) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(oatDir);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
const char* oat_dir = oatDir.c_str();
const char* instruction_set = instructionSet.c_str();
@@ -2708,10 +2860,12 @@
return ok();
}
-binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) {
+binder::Status InstalldNativeService::rmPackageDir(const std::string& packageName,
+ const std::string& packageDir) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(packageDir);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
if (validate_apk_path(packageDir.c_str())) {
return error("Invalid path " + packageDir);
@@ -2722,12 +2876,15 @@
return ok();
}
-binder::Status InstalldNativeService::linkFile(const std::string& relativePath,
- const std::string& fromBase, const std::string& toBase) {
+binder::Status InstalldNativeService::linkFile(const std::string& packageName,
+ const std::string& relativePath,
+ const std::string& fromBase,
+ const std::string& toBase) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(fromBase);
CHECK_ARGUMENT_PATH(toBase);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
const char* relative_path = relativePath.c_str();
const char* from_base = fromBase.c_str();
@@ -2752,12 +2909,15 @@
return ok();
}
-binder::Status InstalldNativeService::moveAb(const std::string& apkPath,
- const std::string& instructionSet, const std::string& outputPath) {
+binder::Status InstalldNativeService::moveAb(const std::string& packageName,
+ const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::string& outputPath) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
const char* apk_path = apkPath.c_str();
const char* instruction_set = instructionSet.c_str();
@@ -2767,13 +2927,16 @@
return success ? ok() : error();
}
-binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath,
- const std::string& instructionSet, const std::optional<std::string>& outputPath,
- int64_t* _aidl_return) {
+binder::Status InstalldNativeService::deleteOdex(const std::string& packageName,
+ const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::optional<std::string>& outputPath,
+ int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(apkPath);
CHECK_ARGUMENT_PATH(outputPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
const char* apk_path = apkPath.c_str();
const char* instruction_set = instructionSet.c_str();
@@ -2802,11 +2965,14 @@
#endif
-binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
- android::base::unique_fd verityInputAshmem, int32_t contentSize) {
+binder::Status InstalldNativeService::installApkVerity(const std::string& packageName,
+ const std::string& filePath,
+ android::base::unique_fd verityInputAshmem,
+ int32_t contentSize) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(filePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
return ok();
@@ -2884,11 +3050,13 @@
return ok();
}
-binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath,
+binder::Status InstalldNativeService::assertFsverityRootHashMatches(
+ const std::string& packageName, const std::string& filePath,
const std::vector<uint8_t>& expectedHash) {
ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(filePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) {
return ok();
@@ -2927,7 +3095,8 @@
CHECK_ARGUMENT_UUID(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(dexPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ const auto userId = multiuser_get_user_id(uid);
+ LOCK_PACKAGE_USER();
bool result = android::installd::reconcile_secondary_dex_file(
dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return);
@@ -3007,8 +3176,9 @@
const char* uuid_ = uuid->c_str();
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
- std::lock_guard<std::recursive_mutex> lock(mLock);
if (fs_prepare_dir(mirrorVolCePath.c_str(), 0711, AID_SYSTEM, AID_SYSTEM) != 0) {
return error("Failed to create CE mirror");
}
@@ -3077,8 +3247,9 @@
std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ std::lock_guard<std::recursive_mutex> lock(mMountsLock);
+
// Unmount CE storage
- std::lock_guard<std::recursive_mutex> lock(mLock);
if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
if (errno != ENOENT) {
res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
@@ -3127,7 +3298,7 @@
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
CHECK_ARGUMENT_PATH(codePath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE_USER();
*_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath,
dexMetadata);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 3fdb01a..04662ea 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -21,8 +21,9 @@
#include <inttypes.h>
#include <unistd.h>
-#include <vector>
+#include <shared_mutex>
#include <unordered_map>
+#include <vector>
#include <android-base/macros.h>
#include <binder/BinderService.h>
@@ -49,6 +50,11 @@
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
int32_t previousAppId, const std::string& seInfo, int32_t targetSdkVersion,
int64_t* _aidl_return);
+ binder::Status createAppDataLocked(const std::optional<std::string>& uuid,
+ const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, int32_t previousAppId,
+ const std::string& seInfo, int32_t targetSdkVersion,
+ int64_t* _aidl_return);
binder::Status createAppData(
const android::os::CreateAppDataArgs& args,
@@ -60,6 +66,9 @@
binder::Status restoreconAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo);
+ binder::Status restoreconAppDataLocked(const std::optional<std::string>& uuid,
+ const std::string& packageName, int32_t userId,
+ int32_t flags, int32_t appId, const std::string& seInfo);
binder::Status migrateAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags);
binder::Status clearAppData(const std::optional<std::string>& uuid,
@@ -110,16 +119,15 @@
int32_t appId, const std::string& seInfo,
int32_t targetSdkVersion, const std::string& fromCodePath);
- binder::Status dexopt(const std::string& apkPath, int32_t uid,
- const std::optional<std::string>& packageName, const std::string& instructionSet,
- int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags,
- const std::string& compilerFilter, const std::optional<std::string>& uuid,
- const std::optional<std::string>& classLoaderContext,
- const std::optional<std::string>& seInfo, bool downgrade,
- int32_t targetSdkVersion, const std::optional<std::string>& profileName,
- const std::optional<std::string>& dexMetadataPath,
- const std::optional<std::string>& compilationReason,
- bool* aidl_return);
+ binder::Status dexopt(const std::string& apkPath, int32_t uid, const std::string& packageName,
+ const std::string& instructionSet, int32_t dexoptNeeded,
+ const std::optional<std::string>& outputPath, int32_t dexFlags,
+ const std::string& compilerFilter, const std::optional<std::string>& uuid,
+ const std::optional<std::string>& classLoaderContext,
+ const std::optional<std::string>& seInfo, bool downgrade,
+ int32_t targetSdkVersion, const std::optional<std::string>& profileName,
+ const std::optional<std::string>& dexMetadataPath,
+ const std::optional<std::string>& compilationReason, bool* aidl_return);
binder::Status controlDexOptBlocking(bool block);
@@ -143,22 +151,25 @@
binder::Status destroyProfileSnapshot(const std::string& packageName,
const std::string& profileName);
- binder::Status rmPackageDir(const std::string& packageDir);
+ binder::Status rmPackageDir(const std::string& packageName, const std::string& packageDir);
binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes,
int32_t flags);
binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid,
const std::string& packageName, const std::string& nativeLibPath32, int32_t userId);
- binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet);
- binder::Status linkFile(const std::string& relativePath, const std::string& fromBase,
- const std::string& toBase);
- binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet,
- const std::string& outputPath);
- binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
- const std::optional<std::string>& outputPath, int64_t* _aidl_return);
- binder::Status installApkVerity(const std::string& filePath,
- android::base::unique_fd verityInput, int32_t contentSize);
- binder::Status assertFsverityRootHashMatches(const std::string& filePath,
- const std::vector<uint8_t>& expectedHash);
+ binder::Status createOatDir(const std::string& packageName, const std::string& oatDir,
+ const std::string& instructionSet);
+ binder::Status linkFile(const std::string& packageName, const std::string& relativePath,
+ const std::string& fromBase, const std::string& toBase);
+ binder::Status moveAb(const std::string& packageName, const std::string& apkPath,
+ const std::string& instructionSet, const std::string& outputPath);
+ binder::Status deleteOdex(const std::string& packageName, const std::string& apkPath,
+ const std::string& instructionSet,
+ const std::optional<std::string>& outputPath, int64_t* _aidl_return);
+ binder::Status installApkVerity(const std::string& packageName, const std::string& filePath,
+ android::base::unique_fd verityInput, int32_t contentSize);
+ binder::Status assertFsverityRootHashMatches(const std::string& packageName,
+ const std::string& filePath,
+ const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
const std::string& packageName, int32_t uid, const std::vector<std::string>& isa,
const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return);
@@ -181,6 +192,8 @@
private:
std::recursive_mutex mLock;
+ std::unordered_map<userid_t, std::weak_ptr<std::shared_mutex>> mUserIdLock;
+ std::unordered_map<std::string, std::weak_ptr<std::recursive_mutex>> mPackageNameLock;
std::recursive_mutex mMountsLock;
std::recursive_mutex mQuotasLock;
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 9c51ff7..e024548 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -56,7 +56,7 @@
@utf8InCpp String seInfo, int targetSdkVersion, @utf8InCpp String fromCodePath);
// Returns false if it is cancelled. Returns true if it is completed or have other errors.
- boolean dexopt(@utf8InCpp String apkPath, int uid, @nullable @utf8InCpp String packageName,
+ boolean dexopt(@utf8InCpp String apkPath, int uid, @utf8InCpp String packageName,
@utf8InCpp String instructionSet, int dexoptNeeded,
@nullable @utf8InCpp String outputPath, int dexFlags,
@utf8InCpp String compilerFilter, @nullable @utf8InCpp String uuid,
@@ -85,20 +85,22 @@
@utf8InCpp String profileName, @utf8InCpp String classpath);
void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
- void rmPackageDir(@utf8InCpp String packageDir);
+ void rmPackageDir(@utf8InCpp String packageName, @utf8InCpp String packageDir);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes, int flags);
void linkNativeLibraryDirectory(@nullable @utf8InCpp String uuid,
@utf8InCpp String packageName, @utf8InCpp String nativeLibPath32, int userId);
- void createOatDir(@utf8InCpp String oatDir, @utf8InCpp String instructionSet);
- void linkFile(@utf8InCpp String relativePath, @utf8InCpp String fromBase,
- @utf8InCpp String toBase);
- void moveAb(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
- @utf8InCpp String outputPath);
- long deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet,
- @nullable @utf8InCpp String outputPath);
- void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput,
- int contentSize);
- void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash);
+ void createOatDir(@utf8InCpp String packageName, @utf8InCpp String oatDir,
+ @utf8InCpp String instructionSet);
+ void linkFile(@utf8InCpp String packageName, @utf8InCpp String relativePath,
+ @utf8InCpp String fromBase, @utf8InCpp String toBase);
+ void moveAb(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+ @utf8InCpp String instructionSet, @utf8InCpp String outputPath);
+ long deleteOdex(@utf8InCpp String packageName, @utf8InCpp String apkPath,
+ @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath);
+ void installApkVerity(@utf8InCpp String packageName, @utf8InCpp String filePath,
+ in FileDescriptor verityInput, int contentSize);
+ void assertFsverityRootHashMatches(@utf8InCpp String packageName, @utf8InCpp String filePath,
+ in byte[] expectedHash);
boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName,
int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid,
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f3ec63f..2bcf2d4 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -52,6 +52,7 @@
#include <server_configurable_flags/get_flags.h>
#include <system/thread_defs.h>
#include <utils/Mutex.h>
+#include <ziparchive/zip_archive.h>
#include "dexopt.h"
#include "dexopt_return_codes.h"
@@ -459,8 +460,8 @@
});
}
-static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name,
- const std::string& location) {
+static unique_fd open_snapshot_profile(uid_t uid, const std::string& package_name,
+ const std::string& location) {
std::string profile = create_snapshot_profile_path(package_name, location);
return open_profile(uid, profile, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
}
@@ -2562,7 +2563,7 @@
const std::string& classpath) {
int app_shared_gid = multiuser_get_shared_gid(/*user_id*/ 0, app_id);
- unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+ unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
if (snapshot_fd < 0) {
return false;
}
@@ -2636,7 +2637,7 @@
}
// Open and create the snapshot profile.
- unique_fd snapshot_fd = open_spnashot_profile(AID_SYSTEM, package_name, profile_name);
+ unique_fd snapshot_fd = open_snapshot_profile(AID_SYSTEM, package_name, profile_name);
// Collect all non empty profiles.
// The collection will traverse all applications profiles and find the non empty files.
@@ -2738,6 +2739,20 @@
}
}
+static bool check_profile_exists_in_dexmetadata(const std::string& dex_metadata) {
+ ZipArchiveHandle zip = nullptr;
+ if (OpenArchive(dex_metadata.c_str(), &zip) != 0) {
+ PLOG(ERROR) << "Failed to open dm '" << dex_metadata << "'";
+ return false;
+ }
+
+ ZipEntry64 entry;
+ int result = FindEntry(zip, "primary.prof", &entry);
+ CloseArchive(zip);
+
+ return result != 0 ? false : true;
+}
+
bool prepare_app_profile(const std::string& package_name,
userid_t user_id,
appid_t app_id,
@@ -2754,7 +2769,7 @@
}
// Check if we need to install the profile from the dex metadata.
- if (!dex_metadata) {
+ if (!dex_metadata || !check_profile_exists_in_dexmetadata(dex_metadata->c_str())) {
return true;
}
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 7082017..4cde7e3 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -13,7 +13,10 @@
test_suites: ["device-tests"],
clang: true,
srcs: ["installd_utils_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libutils",
@@ -33,7 +36,10 @@
test_suites: ["device-tests"],
clang: true,
srcs: ["installd_cache_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libbinder",
@@ -48,6 +54,7 @@
"libasync_safe",
"libdiskusage",
"libinstalld",
+ "libziparchive",
"liblog",
"liblogwrap",
],
@@ -74,7 +81,10 @@
test_suites: ["device-tests"],
clang: true,
srcs: ["installd_service_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libbinder",
@@ -83,12 +93,14 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "packagemanager_aidl-cpp",
"server_configurable_flags",
],
static_libs: [
"libasync_safe",
"libdiskusage",
"libinstalld",
+ "libziparchive",
"liblog",
"liblogwrap",
],
@@ -115,7 +127,10 @@
test_suites: ["device-tests"],
clang: true,
srcs: ["installd_dexopt_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libbinder",
@@ -158,7 +173,10 @@
test_suites: ["device-tests"],
clang: true,
srcs: ["installd_otapreopt_test.cpp"],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"libcutils",
@@ -167,6 +185,6 @@
],
static_libs: [
"liblog",
- "libotapreoptparameters"
+ "libotapreoptparameters",
],
}
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 9a1e17e..8a27a06 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -122,6 +122,7 @@
service = new InstalldNativeService();
testUuid = kTestUuid;
+ system("rm -rf /data/local/tmp/user");
system("mkdir -p /data/local/tmp/user/0");
}
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index a937436..bb36c39 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -635,6 +635,7 @@
int64_t bytes_freed;
binder::Status result = service_->deleteOdex(
+ package_name_,
apk_path_,
kRuntimeIsa,
in_dalvik_cache ? std::nullopt : std::make_optional<std::string>(app_oat_dir_.c_str()),
@@ -729,7 +730,7 @@
TEST_F(DexoptTest, DexoptPrimaryPublicCreateOatDir) {
LOG(INFO) << "DexoptPrimaryPublic";
- ASSERT_BINDER_SUCCESS(service_->createOatDir(app_oat_dir_, kRuntimeIsa));
+ ASSERT_BINDER_SUCCESS(service_->createOatDir(package_name_, app_oat_dir_, kRuntimeIsa));
CompilePrimaryDexOk("verify",
DEXOPT_BOOTCOMPLETE | DEXOPT_PUBLIC,
app_oat_dir_.c_str(),
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index 1e7559d..b831515 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -18,10 +18,11 @@
#include <string>
#include <fcntl.h>
+#include <pwd.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/statvfs.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/xattr.h>
#include <android-base/file.h>
@@ -32,8 +33,10 @@
#include <cutils/properties.h>
#include <gtest/gtest.h>
-#include "binder_test_utils.h"
+#include <android/content/pm/IPackageManagerNative.h>
+#include <binder/IServiceManager.h>
#include "InstalldNativeService.h"
+#include "binder_test_utils.h"
#include "dexopt.h"
#include "globals.h"
#include "utils.h"
@@ -41,6 +44,34 @@
using android::base::StringPrintf;
namespace android {
+std::string get_package_name(uid_t uid) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<content::pm::IPackageManagerNative> package_mgr;
+ if (sm.get() == nullptr) {
+ LOG(INFO) << "Cannot find service manager";
+ } else {
+ sp<IBinder> binder = sm->getService(String16("package_native"));
+ if (binder.get() == nullptr) {
+ LOG(INFO) << "Cannot find package_native";
+ } else {
+ package_mgr = interface_cast<content::pm::IPackageManagerNative>(binder);
+ }
+ }
+ // find package name
+ std::string pkg;
+ if (package_mgr != nullptr) {
+ std::vector<std::string> names;
+ binder::Status status = package_mgr->getNamesForUids({(int)uid}, &names);
+ if (!status.isOk()) {
+ LOG(INFO) << "getNamesForUids failed: %s", status.exceptionMessage().c_str();
+ } else {
+ if (!names[0].empty()) {
+ pkg = names[0].c_str();
+ }
+ }
+ }
+ return pkg;
+}
namespace installd {
constexpr const char* kTestUuid = "TEST";
@@ -107,6 +138,7 @@
service = new InstalldNativeService();
testUuid = kTestUuid;
+ system("rm -rf /data/local/tmp/user");
system("mkdir -p /data/local/tmp/user/0");
init_globals_from_data_and_root();
@@ -247,7 +279,50 @@
EXPECT_TRUE(create_cache_path(buf, "/path/to/file.apk", "isa"));
EXPECT_EQ("/data/dalvik-cache/isa/path@to@file.apk@classes.dex", std::string(buf));
}
+TEST_F(ServiceTest, GetAppSize) {
+ struct stat s;
+ std::string externalPicDir =
+ StringPrintf("%s/Pictures", create_data_media_path(nullptr, 0).c_str());
+ if (stat(externalPicDir.c_str(), &s) == 0) {
+ // fetch the appId from the uid of the external storage owning app
+ int32_t externalStorageAppId = multiuser_get_app_id(s.st_uid);
+ // Fetch Package Name for the external storage owning app uid
+ std::string pkg = get_package_name(s.st_uid);
+
+ std::vector<int64_t> externalStorageSize, externalStorageSizeAfterAddingExternalFile;
+ std::vector<int64_t> ceDataInodes;
+
+ std::vector<std::string> codePaths;
+ std::vector<std::string> packageNames;
+ // set up parameters
+ packageNames.push_back(pkg);
+ ceDataInodes.push_back(0);
+ // initialise the mounts
+ service->invalidateMounts();
+ // call the getAppSize to get the current size of the external storage owning app
+ service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA,
+ externalStorageAppId, ceDataInodes, codePaths, &externalStorageSize);
+ // add a file with 20MB size to the external storage
+ std::string externalFileLocation =
+ StringPrintf("%s/Pictures/%s", getenv("EXTERNAL_STORAGE"), "External.jpg");
+ std::string externalFileContentCommand =
+ StringPrintf("dd if=/dev/zero of=%s bs=1M count=20", externalFileLocation.c_str());
+ system(externalFileContentCommand.c_str());
+ // call the getAppSize again to get the new size of the external storage owning app
+ service->getAppSize(std::nullopt, packageNames, 0, InstalldNativeService::FLAG_USE_QUOTA,
+ externalStorageAppId, ceDataInodes, codePaths,
+ &externalStorageSizeAfterAddingExternalFile);
+ // check that the size before adding the file and after should be the same, as the app size
+ // is not changed.
+ for (size_t i = 0; i < externalStorageSize.size(); i++) {
+ ASSERT_TRUE(externalStorageSize[i] == externalStorageSizeAfterAddingExternalFile[i]);
+ }
+ // remove the external file
+ std::string removeCommand = StringPrintf("rm -f %s", externalFileLocation.c_str());
+ system(removeCommand.c_str());
+ }
+}
static bool mkdirs(const std::string& path, mode_t mode) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode)) {
diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp
index 3ebdeee..80c0548 100644
--- a/cmds/servicemanager/Android.bp
+++ b/cmds/servicemanager/Android.bp
@@ -47,6 +47,15 @@
}
cc_binary {
+ name: "servicemanager.recovery",
+ stem: "servicemanager",
+ recovery: true,
+ defaults: ["servicemanager_defaults"],
+ init_rc: ["servicemanager.recovery.rc"],
+ srcs: ["main.cpp"],
+}
+
+cc_binary {
name: "vndservicemanager",
defaults: ["servicemanager_defaults"],
init_rc: ["vndservicemanager.rc"],
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 4e44ac7..4374abe 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -28,6 +28,9 @@
#ifndef VENDORSERVICEMANAGER
#include <vintf/VintfObject.h>
+#ifdef __ANDROID_RECOVERY__
+#include <vintf/VintfObjectRecovery.h>
+#endif // __ANDROID_RECOVERY__
#include <vintf/constants.h>
#endif // !VENDORSERVICEMANAGER
@@ -37,16 +40,33 @@
namespace android {
#ifndef VENDORSERVICEMANAGER
+
struct ManifestWithDescription {
std::shared_ptr<const vintf::HalManifest> manifest;
const char* description;
};
+static std::vector<ManifestWithDescription> GetManifestsWithDescription() {
+#ifdef __ANDROID_RECOVERY__
+ auto vintfObject = vintf::VintfObjectRecovery::GetInstance();
+ if (vintfObject == nullptr) {
+ LOG(ERROR) << "NULL VintfObjectRecovery!";
+ return {};
+ }
+ return {ManifestWithDescription{vintfObject->getRecoveryHalManifest(), "recovery"}};
+#else
+ auto vintfObject = vintf::VintfObject::GetInstance();
+ if (vintfObject == nullptr) {
+ LOG(ERROR) << "NULL VintfObject!";
+ return {};
+ }
+ return {ManifestWithDescription{vintfObject->getDeviceHalManifest(), "device"},
+ ManifestWithDescription{vintfObject->getFrameworkHalManifest(), "framework"}};
+#endif
+}
+
// func true -> stop search and forEachManifest will return true
static bool forEachManifest(const std::function<bool(const ManifestWithDescription&)>& func) {
- for (const ManifestWithDescription& mwd : {
- ManifestWithDescription{ vintf::VintfObject::GetDeviceHalManifest(), "device" },
- ManifestWithDescription{ vintf::VintfObject::GetFrameworkHalManifest(), "framework" },
- }) {
+ for (const ManifestWithDescription& mwd : GetManifestsWithDescription()) {
if (mwd.manifest == nullptr) {
LOG(ERROR) << "NULL VINTF MANIFEST!: " << mwd.description;
// note, we explicitly do not retry here, so that we can detect VINTF
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 8c1beac..2fb9c2b 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -111,6 +111,10 @@
};
int main(int argc, char** argv) {
+#ifdef __ANDROID_RECOVERY__
+ android::base::InitLogging(argv, android::base::KernelLogger);
+#endif
+
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
diff --git a/cmds/servicemanager/servicemanager.recovery.rc b/cmds/servicemanager/servicemanager.recovery.rc
new file mode 100644
index 0000000..067faf9
--- /dev/null
+++ b/cmds/servicemanager/servicemanager.recovery.rc
@@ -0,0 +1,4 @@
+service servicemanager /system/bin/servicemanager
+ disabled
+ group system readproc
+ seclabel u:r:servicemanager:s0
diff --git a/include/input/Input.h b/include/input/Input.h
index ce9cefe..29503af 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -201,8 +201,17 @@
class Parcel;
#endif
+/*
+ * Apply the given transform to the point without applying any translation/offset.
+ */
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy);
+
const char* inputEventTypeToString(int32_t type);
+std::string inputEventSourceToString(int32_t source);
+
+bool isFromSource(uint32_t source, uint32_t test);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -565,6 +574,8 @@
inline ui::Transform getTransform() const { return mTransform; }
+ int getSurfaceRotation() const;
+
inline float getXPrecision() const { return mXPrecision; }
inline float getYPrecision() const { return mYPrecision; }
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 8270ae5..d8101fa 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -202,7 +202,7 @@
sanitize: {
misc_undefined: ["integer"],
},
- min_sdk_version: "29",
+ min_sdk_version: "30",
tidy: true,
tidy_flags: [
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 745d9e9..ba57a98 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -824,7 +824,7 @@
const size_t padded = pad_size(len);
- // sanity check for integer overflow
+ // check for integer overflow
if (mDataPos+padded < mDataPos) {
return nullptr;
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 7d14315..f5abb85 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -228,10 +228,8 @@
"android.gfx.tests.IIPCTest",
"android.gfx.tests.ISafeInterfaceTest",
"android.graphicsenv.IGpuService",
- "android.gui.DisplayEventConnection",
"android.gui.IConsumerListener",
"android.gui.IGraphicBufferConsumer",
- "android.gui.IRegionSamplingListener",
"android.gui.ITransactionComposerListener",
"android.gui.SensorEventConnection",
"android.gui.SensorServer",
diff --git a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
index 0ad400b..c903998 100644
--- a/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h
@@ -365,6 +365,8 @@
ScopedFileDescriptor(ScopedFileDescriptor&&) = default;
ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default;
+ ScopedFileDescriptor dup() const { return ScopedFileDescriptor(::dup(get())); }
+
bool operator!=(const ScopedFileDescriptor& rhs) const { return get() != rhs.get(); }
bool operator<(const ScopedFileDescriptor& rhs) const { return get() < rhs.get(); }
bool operator<=(const ScopedFileDescriptor& rhs) const { return get() <= rhs.get(); }
diff --git a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
index 5de64f8..09411e7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h
@@ -190,9 +190,9 @@
BnCInterface() {}
virtual ~BnCInterface() {}
- SpAIBinder asBinder() override;
+ SpAIBinder asBinder() override final;
- bool isRemote() override { return false; }
+ bool isRemote() override final { return false; }
protected:
/**
@@ -215,9 +215,9 @@
explicit BpCInterface(const SpAIBinder& binder) : mBinder(binder) {}
virtual ~BpCInterface() {}
- SpAIBinder asBinder() override;
+ SpAIBinder asBinder() override final;
- bool isRemote() override { return AIBinder_isRemote(mBinder.get()); }
+ bool isRemote() override final { return AIBinder_isRemote(mBinder.get()); }
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
return AIBinder_dump(asBinder().get(), fd, args, numArgs);
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
index 64833b6..91047be 100644
--- a/libs/binder/rust/binder_tokio/lib.rs
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -35,6 +35,11 @@
/// Retrieve an existing service for a particular interface, sleeping for a few
/// seconds if it doesn't yet exist.
pub async fn get_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::get_interface::<T>(name);
+ }
+
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
binder::public_api::get_interface::<T>(&name)
@@ -54,6 +59,11 @@
/// Retrieve an existing service for a particular interface, or start it if it
/// is configured as a dynamic service and isn't yet started.
pub async fn wait_for_interface<T: FromIBinder + ?Sized + 'static>(name: &str) -> Result<Strong<T>, StatusCode> {
+ if binder::is_handling_transaction() {
+ // See comment in the BinderAsyncPool impl.
+ return binder::public_api::wait_for_interface::<T>(name);
+ }
+
let name = name.to_string();
let res = tokio::task::spawn_blocking(move || {
binder::public_api::wait_for_interface::<T>(&name)
@@ -86,18 +96,27 @@
B: Send + 'a,
E: From<crate::StatusCode>,
{
- let handle = tokio::task::spawn_blocking(spawn_me);
- Box::pin(async move {
- // The `is_panic` branch is not actually reachable in Android as we compile
- // with `panic = abort`.
- match handle.await {
- Ok(res) => after_spawn(res).await,
- Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
- Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
- Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
- }
- })
+ if binder::is_handling_transaction() {
+ // We are currently on the thread pool for a binder server, so we should execute the
+ // transaction on the current thread so that the binder kernel driver is able to apply
+ // its deadlock prevention strategy to the sub-call.
+ //
+ // This shouldn't cause issues with blocking the thread as only one task will run in a
+ // call to `block_on`, so there aren't other tasks to block.
+ let result = spawn_me();
+ Box::pin(after_spawn(result))
+ } else {
+ let handle = tokio::task::spawn_blocking(spawn_me);
+ Box::pin(async move {
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match handle.await {
+ Ok(res) => after_spawn(res).await,
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION.into()),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR.into()),
+ }
+ })
+ }
}
}
-
-
diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs
index d09ac83..3d2eddf 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -1027,16 +1027,20 @@
#[macro_export]
macro_rules! declare_binder_enum {
{
+ $( #[$attr:meta] )*
$enum:ident : [$backing:ty; $size:expr] {
$( $name:ident = $value:expr, )*
}
} => {
+ $( #[$attr] )*
#[derive(Debug, Default, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
+ #[allow(missing_docs)]
pub struct $enum(pub $backing);
impl $enum {
- $( pub const $name: Self = Self($value); )*
+ $( #[allow(missing_docs)] pub const $name: Self = Self($value); )*
#[inline(always)]
+ #[allow(missing_docs)]
pub const fn enum_values() -> [Self; $size] {
[$(Self::$name),*]
}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index cce55c0..b94dfa1 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -114,7 +114,7 @@
};
pub use crate::binder_async::{BoxFuture, BinderAsyncPool};
pub use error::{status_t, ExceptionCode, Result, Status, StatusCode};
-pub use native::{add_service, force_lazy_services_persist, register_lazy_service, Binder};
+pub use native::{add_service, force_lazy_services_persist, is_handling_transaction, register_lazy_service, Binder};
pub use parcel::{BorrowedParcel, Parcel};
pub use proxy::{get_interface, get_service, wait_for_interface, wait_for_service};
pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder};
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index e183ea3..b7c7ae4 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -517,3 +517,12 @@
}
impl Interface for () {}
+
+/// Determine whether the current thread is currently executing an incoming
+/// transaction.
+pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: This method is always safe to call.
+ sys::AIBinder_isHandlingTransaction()
+ }
+}
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 1fd2ead..40359b4 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -16,7 +16,7 @@
//! Rust Binder crate integration tests
-use binder::declare_binder_interface;
+use binder::{declare_binder_enum, declare_binder_interface};
use binder::parcel::BorrowedParcel;
use binder::{
Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
@@ -294,6 +294,23 @@
impl ITestSameDescriptor for Binder<BnTestSameDescriptor> {}
+declare_binder_enum! {
+ TestEnum : [i32; 3] {
+ FOO = 1,
+ BAR = 2,
+ BAZ = 3,
+ }
+}
+
+declare_binder_enum! {
+ #[deprecated(since = "1.0.0")]
+ TestDeprecatedEnum : [i32; 3] {
+ FOO = 1,
+ BAR = 2,
+ BAZ = 3,
+ }
+}
+
#[cfg(test)]
mod tests {
use selinux_bindgen as selinux_sys;
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 19a29c1..c5dec19 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -180,11 +180,9 @@
"FrameTimelineInfo.cpp",
"GLConsumer.cpp",
"IConsumerListener.cpp",
- "IDisplayEventConnection.cpp",
"IGraphicBufferConsumer.cpp",
"IGraphicBufferProducer.cpp",
"IProducerListener.cpp",
- "IRegionSamplingListener.cpp",
"ISurfaceComposer.cpp",
"ISurfaceComposerClient.cpp",
"ITransactionCompletedListener.cpp",
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 5570321..dd50ca7 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -507,7 +507,6 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- incStrong((void*)transactionCommittedCallbackThunk);
const bool sizeHasChanged = mRequestedSize != mSize;
mSize = mRequestedSize;
@@ -527,7 +526,7 @@
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
- t->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this));
+
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
if (updateDestinationFrame) {
@@ -650,6 +649,9 @@
// add to shadow queue
mNumFrameAvailable++;
+ if (mWaitForTransactionCallback && mNumFrameAvailable == 2) {
+ acquireAndReleaseBuffer();
+ }
ATRACE_INT(mQueuedBufferTrace.c_str(),
mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
@@ -658,6 +660,13 @@
if (syncTransactionSet) {
acquireNextBufferLocked(mSyncTransaction);
+
+ // Only need a commit callback when syncing to ensure the buffer that's synced has been sent
+ // to SF
+ incStrong((void*)transactionCommittedCallbackThunk);
+ mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
+ static_cast<void*>(this));
+
if (mAcquireSingleBuffer) {
mSyncTransaction = nullptr;
}
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index 8379675..ee80082 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -182,7 +182,6 @@
outVsyncEventData->id = ev.vsync.vsyncId;
outVsyncEventData->deadlineTimestamp = ev.vsync.deadlineTimestamp;
outVsyncEventData->frameInterval = ev.vsync.frameInterval;
- outVsyncEventData->expectedPresentTime = ev.vsync.expectedVSyncTimestamp;
outVsyncEventData->preferredFrameTimelineIndex =
ev.vsync.preferredFrameTimelineIndex;
populateFrameTimelines(ev, outVsyncEventData);
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 03b33c7..b916e48 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -19,7 +19,6 @@
#include <utils/Errors.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.h>
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
deleted file mode 100644
index c0e246f..0000000
--- a/libs/gui/IDisplayEventConnection.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <gui/IDisplayEventConnection.h>
-
-#include <private/gui/BitTube.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- STEAL_RECEIVE_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
- SET_VSYNC_RATE,
- REQUEST_NEXT_VSYNC,
- LAST = REQUEST_NEXT_VSYNC,
-};
-
-} // Anonymous namespace
-
-class BpDisplayEventConnection : public SafeBpInterface<IDisplayEventConnection> {
-public:
- explicit BpDisplayEventConnection(const sp<IBinder>& impl)
- : SafeBpInterface<IDisplayEventConnection>(impl, "BpDisplayEventConnection") {}
-
- ~BpDisplayEventConnection() override;
-
- status_t stealReceiveChannel(gui::BitTube* outChannel) override {
- return callRemote<decltype(
- &IDisplayEventConnection::stealReceiveChannel)>(Tag::STEAL_RECEIVE_CHANNEL,
- outChannel);
- }
-
- status_t setVsyncRate(uint32_t count) override {
- return callRemote<decltype(&IDisplayEventConnection::setVsyncRate)>(Tag::SET_VSYNC_RATE,
- count);
- }
-
- void requestNextVsync() override {
- callRemoteAsync<decltype(&IDisplayEventConnection::requestNextVsync)>(
- Tag::REQUEST_NEXT_VSYNC);
- }
-};
-
-// Out-of-line virtual method definition to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpDisplayEventConnection::~BpDisplayEventConnection() = default;
-
-IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
-
-status_t BnDisplayEventConnection::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::STEAL_RECEIVE_CHANNEL:
- return callLocal(data, reply, &IDisplayEventConnection::stealReceiveChannel);
- case Tag::SET_VSYNC_RATE:
- return callLocal(data, reply, &IDisplayEventConnection::setVsyncRate);
- case Tag::REQUEST_NEXT_VSYNC:
- return callLocalAsync(data, reply, &IDisplayEventConnection::requestNextVsync);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/IRegionSamplingListener.cpp b/libs/gui/IRegionSamplingListener.cpp
deleted file mode 100644
index 40cbfce..0000000
--- a/libs/gui/IRegionSamplingListener.cpp
+++ /dev/null
@@ -1,64 +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 LOG_TAG "IRegionSamplingListener"
-//#define LOG_NDEBUG 0
-
-#include <gui/IRegionSamplingListener.h>
-
-namespace android {
-
-namespace { // Anonymous
-
-enum class Tag : uint32_t {
- ON_SAMPLE_COLLECTED = IBinder::FIRST_CALL_TRANSACTION,
- LAST = ON_SAMPLE_COLLECTED,
-};
-
-} // Anonymous namespace
-
-class BpRegionSamplingListener : public SafeBpInterface<IRegionSamplingListener> {
-public:
- explicit BpRegionSamplingListener(const sp<IBinder>& impl)
- : SafeBpInterface<IRegionSamplingListener>(impl, "BpRegionSamplingListener") {}
-
- ~BpRegionSamplingListener() override;
-
- void onSampleCollected(float medianLuma) override {
- callRemoteAsync<decltype(
- &IRegionSamplingListener::onSampleCollected)>(Tag::ON_SAMPLE_COLLECTED, medianLuma);
- }
-};
-
-// Out-of-line virtual method definitions to trigger vtable emission in this translation unit (see
-// clang warning -Wweak-vtables)
-BpRegionSamplingListener::~BpRegionSamplingListener() = default;
-
-IMPLEMENT_META_INTERFACE(RegionSamplingListener, "android.gui.IRegionSamplingListener");
-
-status_t BnRegionSamplingListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags) {
- if (code < IBinder::FIRST_CALL_TRANSACTION || code > static_cast<uint32_t>(Tag::LAST)) {
- return BBinder::onTransact(code, data, reply, flags);
- }
- auto tag = static_cast<Tag>(code);
- switch (tag) {
- case Tag::ON_SAMPLE_COLLECTED:
- return callLocalAsync(data, reply, &IRegionSamplingListener::onSampleCollected);
- }
-}
-
-} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 0295099..7f73013 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -17,13 +17,13 @@
// tag as surfaceflinger
#define LOG_TAG "SurfaceFlinger"
+#include <android/gui/IDisplayEventConnection.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/Parcel.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IGraphicBufferProducer.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerDebugInfo.h>
@@ -44,6 +44,8 @@
namespace android {
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IWindowInfosListener;
using ui::ColorMode;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f848e4f..cd1c810 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -502,6 +502,10 @@
what |= eDropInputModeChanged;
dropInputMode = other.dropInputMode;
}
+ if (other.what & eColorChanged) {
+ what |= eColorChanged;
+ color = other.color;
+ }
if ((other.what & what) != other.what) {
ALOGE("Unmerged SurfaceComposer Transaction properties. LayerState::merge needs updating? "
"other.what=0x%" PRIX64 " what=0x%" PRIX64 " unmerged flags=0x%" PRIX64,
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index b139cf1..e1fe26a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -53,6 +53,7 @@
namespace android {
using gui::FocusRequest;
+using gui::IRegionSamplingListener;
using gui::WindowInfo;
using gui::WindowInfoHandle;
using gui::WindowInfosListener;
@@ -352,6 +353,10 @@
// through all until the SC is found.
int32_t layerId = -1;
for (auto callbackId : transactionStats.callbackIds) {
+ if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
+ // We only want to run the stats callback for ON_COMPLETE
+ continue;
+ }
sp<SurfaceControl> sc =
callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl];
if (sc != nullptr) {
@@ -360,7 +365,7 @@
}
}
- {
+ if (layerId != -1) {
// Acquire surface stats listener lock such that we guarantee that after calling
// unregister, there won't be any further callback.
std::scoped_lock<std::recursive_mutex> lock(mSurfaceStatsListenerMutex);
diff --git a/libs/gui/aidl/android/gui/BitTube.aidl b/libs/gui/aidl/android/gui/BitTube.aidl
new file mode 100644
index 0000000..6b0595e
--- /dev/null
+++ b/libs/gui/aidl/android/gui/BitTube.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+parcelable BitTube cpp_header "private/gui/BitTube.h";
diff --git a/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
new file mode 100644
index 0000000..9f41593
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IDisplayEventConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+import android.gui.BitTube;
+
+/** @hide */
+interface IDisplayEventConnection {
+ /*
+ * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
+ * descriptor of outChannel will be initialized, and this effectively "steals" the receive
+ * channel from the remote end (such that the remote end can only use its send channel).
+ */
+ void stealReceiveChannel(out BitTube outChannel);
+
+ /*
+ * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
+ * A value of 2 returns every other event, etc. A value of 0 returns no event unless
+ * requestNextVsync() has been called.
+ */
+ void setVsyncRate(in int count);
+
+ /*
+ * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
+ */
+ oneway void requestNextVsync(); // Asynchronous
+}
diff --git a/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
new file mode 100644
index 0000000..00a3959
--- /dev/null
+++ b/libs/gui/aidl/android/gui/IRegionSamplingListener.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.gui;
+
+/** @hide */
+oneway interface IRegionSamplingListener {
+ void onSampleCollected(float medianLuma);
+}
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 8a3a476..40621dd 100644
--- a/libs/gui/include/gui/DisplayEventDispatcher.h
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -35,9 +35,6 @@
// The current frame interval in ns when this frame was scheduled.
int64_t frameInterval = 0;
- // The anticipated Vsync present time.
- int64_t expectedPresentTime = 0;
-
struct FrameTimeline {
// The Vsync Id corresponsing to this vsync event. This will be used to
// populate ISurfaceComposer::setFrameTimelineVsync and
diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h
index ca36843..456bbfb 100644
--- a/libs/gui/include/gui/DisplayEventReceiver.h
+++ b/libs/gui/include/gui/DisplayEventReceiver.h
@@ -33,7 +33,7 @@
// ----------------------------------------------------------------------------
-class IDisplayEventConnection;
+using gui::IDisplayEventConnection;
namespace gui {
class BitTube;
diff --git a/libs/gui/include/gui/IDisplayEventConnection.h b/libs/gui/include/gui/IDisplayEventConnection.h
deleted file mode 100644
index cff22a3..0000000
--- a/libs/gui/include/gui/IDisplayEventConnection.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-#include <gui/ISurfaceComposer.h>
-#include <utils/Errors.h>
-
-#include <cstdint>
-
-namespace android {
-
-namespace gui {
-class BitTube;
-} // namespace gui
-
-class IDisplayEventConnection : public IInterface {
-public:
- DECLARE_META_INTERFACE(DisplayEventConnection)
-
- /*
- * stealReceiveChannel() returns a BitTube to receive events from. Only the receive file
- * descriptor of outChannel will be initialized, and this effectively "steals" the receive
- * channel from the remote end (such that the remote end can only use its send channel).
- */
- virtual status_t stealReceiveChannel(gui::BitTube* outChannel) = 0;
-
- /*
- * setVsyncRate() sets the vsync event delivery rate. A value of 1 returns every vsync event.
- * A value of 2 returns every other event, etc. A value of 0 returns no event unless
- * requestNextVsync() has been called.
- */
- virtual status_t setVsyncRate(uint32_t count) = 0;
-
- /*
- * requestNextVsync() schedules the next vsync event. It has no effect if the vsync rate is > 0.
- */
- virtual void requestNextVsync() = 0; // Asynchronous
-};
-
-class BnDisplayEventConnection : public SafeBnInterface<IDisplayEventConnection> {
-public:
- BnDisplayEventConnection()
- : SafeBnInterface<IDisplayEventConnection>("BnDisplayEventConnection") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/IRegionSamplingListener.h b/libs/gui/include/gui/IRegionSamplingListener.h
deleted file mode 100644
index 1803d9a..0000000
--- a/libs/gui/include/gui/IRegionSamplingListener.h
+++ /dev/null
@@ -1,43 +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 <cstdint>
-#include <vector>
-
-#include <binder/IInterface.h>
-#include <binder/SafeInterface.h>
-
-namespace android {
-
-class IRegionSamplingListener : public IInterface {
-public:
- DECLARE_META_INTERFACE(RegionSamplingListener)
-
- virtual void onSampleCollected(float medianLuma) = 0;
-};
-
-class BnRegionSamplingListener : public SafeBnInterface<IRegionSamplingListener> {
-public:
- BnRegionSamplingListener()
- : SafeBnInterface<IRegionSamplingListener>("BnRegionSamplingListener") {}
-
- status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
- uint32_t flags = 0) override;
-};
-
-} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index e0183ad..2546e4c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -17,8 +17,10 @@
#pragma once
#include <android/gui/DisplayBrightness.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/gui/IFpsListener.h>
#include <android/gui/IHdrLayerInfoListener.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <android/gui/IScreenCaptureListener.h>
#include <android/gui/ITransactionTraceListener.h>
#include <android/gui/ITunnelModeEnabledListener.h>
@@ -60,13 +62,13 @@
struct LayerCaptureArgs;
class LayerDebugInfo;
class HdrCapabilities;
-class IDisplayEventConnection;
class IGraphicBufferProducer;
class ISurfaceComposerClient;
-class IRegionSamplingListener;
class Rect;
enum class FrameEvent;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using gui::IScreenCaptureListener;
namespace ui {
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index de14b3d..27f4d37 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -59,4 +59,14 @@
std::string itemToString(uint32_t key, const char* separator) const;
};
+// Keep in sync with the GameManager.java constants.
+enum class GameMode : int32_t {
+ Unsupported = 0,
+ Standard = 1,
+ Performance = 2,
+ Battery = 3,
+
+ ftl_last = Battery
+};
+
} // namespace android
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index e05c364..0fe1253 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -51,10 +51,11 @@
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
-class IRegionSamplingListener;
class ITunnelModeEnabledListener;
class Region;
+using gui::IRegionSamplingListener;
+
struct SurfaceControlStats {
SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, nsecs_t acquireTime,
const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence,
diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp
index d9beb23..f960e07 100644
--- a/libs/gui/tests/EndToEndNativeInputTest.cpp
+++ b/libs/gui/tests/EndToEndNativeInputTest.cpp
@@ -936,6 +936,20 @@
EXPECT_EQ(surface->consumeEvent(100), nullptr);
}
+TEST_F(InputSurfacesTest, ignore_touch_region_with_zero_sized_blast) {
+ std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+
+ std::unique_ptr<BlastInputSurface> bufferSurface =
+ BlastInputSurface::makeBlastInputSurface(mComposerClient, 0, 0);
+
+ surface->showAt(100, 100);
+ bufferSurface->mInputInfo.touchableRegion.orSelf(Rect(0, 0, 200, 200));
+ bufferSurface->showAt(100, 100, Rect::EMPTY_RECT);
+
+ injectTap(101, 101);
+ surface->expectTap(1, 1);
+}
+
TEST_F(InputSurfacesTest, drop_input_policy) {
std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
surface->doTransaction(
diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp
index 6746b0a..c9106be 100644
--- a/libs/gui/tests/RegionSampling_test.cpp
+++ b/libs/gui/tests/RegionSampling_test.cpp
@@ -17,9 +17,9 @@
#include <gtest/gtest.h>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/ProcessState.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
#include <gui/SurfaceComposerClient.h>
@@ -135,12 +135,13 @@
std::atomic<bool> poll_{true};
};
-struct Listener : BnRegionSamplingListener {
- void onSampleCollected(float medianLuma) override {
+struct Listener : android::gui::BnRegionSamplingListener {
+ binder::Status onSampleCollected(float medianLuma) override {
std::unique_lock<decltype(mutex)> lk(mutex);
received = true;
mLuma = medianLuma;
cv.notify_all();
+ return binder::Status::ok();
};
bool wait_event(std::chrono::milliseconds timeout) {
std::unique_lock<decltype(mutex)> lk(mutex);
diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp
index 0cd150d..a083a22 100644
--- a/libs/gui/tests/SamplingDemo.cpp
+++ b/libs/gui/tests/SamplingDemo.cpp
@@ -20,9 +20,9 @@
#include <chrono>
#include <thread>
+#include <android/gui/BnRegionSamplingListener.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/SurfaceControl.h>
@@ -33,7 +33,7 @@
namespace android {
-class Button : public BnRegionSamplingListener {
+class Button : public gui::BnRegionSamplingListener {
public:
Button(const char* name, const Rect& samplingArea) {
sp<SurfaceComposerClient> client = new SurfaceComposerClient;
@@ -99,9 +99,10 @@
.apply();
}
- void onSampleCollected(float medianLuma) override {
+ binder::Status onSampleCollected(float medianLuma) override {
ATRACE_CALL();
setColor(medianLuma);
+ return binder::Status::ok();
}
sp<SurfaceComposerClient> mClient;
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index b2baea6..d6ac3f9 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -19,11 +19,11 @@
#include <gtest/gtest.h>
#include <SurfaceFlingerProperties.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <binder/ProcessState.h>
#include <configstore/Utils.h>
#include <gui/BufferItemConsumer.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -45,6 +45,8 @@
// retrieve wide-color and hdr settings from configstore
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
+using gui::IDisplayEventConnection;
+using gui::IRegionSamplingListener;
using ui::ColorMode;
using Transaction = SurfaceComposerClient::Transaction;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index c7f77d4..44487c3 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -24,6 +24,7 @@
#include <android-base/stringprintf.h>
#include <gui/constants.h>
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
@@ -60,16 +61,6 @@
return atan2f(transformedPoint.x, -transformedPoint.y);
}
-vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
- const vec2 transformedXy = transform.transform(xy);
- const vec2 transformedOrigin = transform.transform(0, 0);
- return transformedXy - transformedOrigin;
-}
-
-bool isFromSource(uint32_t source, uint32_t test) {
- return (source & test) == test;
-}
-
bool shouldDisregardTransformation(uint32_t source) {
// Do not apply any transformations to axes from joysticks or touchpads.
return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
@@ -124,6 +115,12 @@
// --- InputEvent ---
+vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) {
+ const vec2 transformedXy = transform.transform(xy);
+ const vec2 transformedOrigin = transform.transform(0, 0);
+ return transformedXy - transformedOrigin;
+}
+
const char* inputEventTypeToString(int32_t type) {
switch (type) {
case AINPUT_EVENT_TYPE_KEY: {
@@ -148,6 +145,49 @@
return "UNKNOWN";
}
+std::string inputEventSourceToString(int32_t source) {
+ if (source == AINPUT_SOURCE_UNKNOWN) {
+ return "UNKNOWN";
+ }
+ if (source == static_cast<int32_t>(AINPUT_SOURCE_ANY)) {
+ return "ANY";
+ }
+ static const std::map<int32_t, const char*> SOURCES{
+ {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"},
+ {AINPUT_SOURCE_DPAD, "DPAD"},
+ {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"},
+ {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"},
+ {AINPUT_SOURCE_MOUSE, "MOUSE"},
+ {AINPUT_SOURCE_STYLUS, "STYLUS"},
+ {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"},
+ {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"},
+ {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"},
+ {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"},
+ {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"},
+ {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"},
+ {AINPUT_SOURCE_HDMI, "HDMI"},
+ {AINPUT_SOURCE_SENSOR, "SENSOR"},
+ {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"},
+ };
+ std::string result;
+ for (const auto& [source_entry, str] : SOURCES) {
+ if ((source & source_entry) == source_entry) {
+ if (!result.empty()) {
+ result += " | ";
+ }
+ result += str;
+ }
+ }
+ if (result.empty()) {
+ result = StringPrintf("0x%08x", source);
+ }
+ return result;
+}
+
+bool isFromSource(uint32_t source, uint32_t test) {
+ return (source & test) == test;
+}
+
VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) {
return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(),
event.getSource(), event.getDisplayId()},
@@ -467,6 +507,24 @@
mSamplePointerCoords.appendArray(pointerCoords, getPointerCount());
}
+int MotionEvent::getSurfaceRotation() const {
+ // The surface rotation is the rotation from the window's coordinate space to that of the
+ // display. Since the event's transform takes display space coordinates to window space, the
+ // returned surface rotation is the inverse of the rotation for the surface.
+ switch (mTransform.getOrientation()) {
+ case ui::Transform::ROT_0:
+ return DISPLAY_ORIENTATION_0;
+ case ui::Transform::ROT_90:
+ return DISPLAY_ORIENTATION_270;
+ case ui::Transform::ROT_180:
+ return DISPLAY_ORIENTATION_180;
+ case ui::Transform::ROT_270:
+ return DISPLAY_ORIENTATION_90;
+ default:
+ return -1;
+ }
+}
+
float MotionEvent::getXCursorPosition() const {
vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition());
return vals.x;
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 015bd81..ac84627 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -208,10 +208,8 @@
const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange(
int32_t axis, uint32_t source) const {
- size_t numRanges = mMotionRanges.size();
- for (size_t i = 0; i < numRanges; i++) {
- const MotionRange& range = mMotionRanges[i];
- if (range.axis == axis && range.source == source) {
+ for (const MotionRange& range : mMotionRanges) {
+ if (range.axis == axis && isFromSource(range.source, source)) {
return ⦥
}
}
diff --git a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
index 35ae3d2..6fd4b8f 100644
--- a/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
+++ b/libs/nativedisplay/include/surfacetexture/ImageConsumer.h
@@ -42,7 +42,8 @@
typedef status_t (*SurfaceTexture_fenceWait)(int fence, void* fencePassThroughHandle);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& cb,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& cb,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle);
diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
index bac44c9..0f119f3 100644
--- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
+++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h
@@ -272,8 +272,8 @@
status_t attachToContext(uint32_t tex);
sp<GraphicBuffer> dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- float* outTransformMatrix, uint32_t* outTransform,
- bool* outQueueEmpty,
+ HdrMetadata* outHdrMetadata, float* outTransformMatrix,
+ uint32_t* outTransform, bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle, ARect* currentCrop);
diff --git a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
index e85009c..2987f3a 100644
--- a/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
+++ b/libs/nativedisplay/include/surfacetexture/surface_texture_platform.h
@@ -19,6 +19,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
+#include <android/hdr_metadata.h>
#include <jni.h>
#include <system/graphics.h>
@@ -82,13 +83,12 @@
* The caller gets ownership of the buffer and need to release it with
* AHardwareBuffer_release.
*/
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, uint32_t* outTransform,
- bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait,
- void* fencePassThroughHandle, ARect* currentCrop);
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* fencePassThroughHandle, ARect* currentCrop);
} // namespace android
diff --git a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
index 365e788..cf16739 100644
--- a/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/ImageConsumer.cpp
@@ -28,7 +28,8 @@
}
sp<GraphicBuffer> ImageConsumer::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
- bool* outQueueEmpty, SurfaceTexture& st,
+ HdrMetadata* outHdrMetadata, bool* outQueueEmpty,
+ SurfaceTexture& st,
SurfaceTexture_createReleaseFence createFence,
SurfaceTexture_fenceWait fenceWait,
void* fencePassThroughHandle) {
@@ -121,6 +122,7 @@
st.computeCurrentTransformMatrixLocked();
*outDataspace = item.mDataSpace;
+ *outHdrMetadata = item.mHdrMetadata;
*outSlotid = slot;
return st.mSlots[slot].mGraphicBuffer;
}
diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
index 3535e67..d3d4cba 100644
--- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
+++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp
@@ -464,6 +464,7 @@
}
sp<GraphicBuffer> SurfaceTexture::dequeueBuffer(int* outSlotid, android_dataspace* outDataspace,
+ HdrMetadata* outHdrMetadata,
float* outTransformMatrix, uint32_t* outTransform,
bool* outQueueEmpty,
SurfaceTexture_createReleaseFence createFence,
@@ -482,8 +483,8 @@
return buffer;
}
- buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outQueueEmpty, *this,
- createFence, fenceWait, fencePassThroughHandle);
+ buffer = mImageConsumer.dequeueBuffer(outSlotid, outDataspace, outHdrMetadata, outQueueEmpty,
+ *this, createFence, fenceWait, fencePassThroughHandle);
memcpy(outTransformMatrix, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
*outTransform = mCurrentTransform;
*currentCrop = mCurrentCrop;
diff --git a/libs/nativedisplay/surfacetexture/surface_texture.cpp b/libs/nativedisplay/surfacetexture/surface_texture.cpp
index cc0a12d..39a925f 100644
--- a/libs/nativedisplay/surfacetexture/surface_texture.cpp
+++ b/libs/nativedisplay/surfacetexture/surface_texture.cpp
@@ -192,20 +192,23 @@
texture->consumer->releaseConsumerOwnership();
}
-AHardwareBuffer* ASurfaceTexture_dequeueBuffer(ASurfaceTexture* st, int* outSlotid,
- android_dataspace* outDataspace,
- float* outTransformMatrix, uint32_t* outTransform,
- bool* outNewContent,
- ASurfaceTexture_createReleaseFence createFence,
- ASurfaceTexture_fenceWait fenceWait, void* handle,
- ARect* currentCrop) {
+AHardwareBuffer* ASurfaceTexture_dequeueBuffer(
+ ASurfaceTexture* st, int* outSlotid, android_dataspace* outDataspace,
+ AHdrMetadataType* outHdrType, android_cta861_3_metadata* outCta861_3,
+ android_smpte2086_metadata* outSmpte2086, float* outTransformMatrix, uint32_t* outTransform,
+ bool* outNewContent, ASurfaceTexture_createReleaseFence createFence,
+ ASurfaceTexture_fenceWait fenceWait, void* handle, ARect* currentCrop) {
sp<GraphicBuffer> buffer;
*outNewContent = false;
bool queueEmpty;
do {
- buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, outTransformMatrix,
+ HdrMetadata metadata;
+ buffer = st->consumer->dequeueBuffer(outSlotid, outDataspace, &metadata, outTransformMatrix,
outTransform, &queueEmpty, createFence, fenceWait,
handle, currentCrop);
+ *outHdrType = static_cast<AHdrMetadataType>(metadata.validTypes);
+ *outCta861_3 = metadata.cta8613;
+ *outSmpte2086 = metadata.smpte2086;
if (!queueEmpty) {
*outNewContent = true;
}
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index e2f32e3..d429551 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -370,7 +370,7 @@
if (!AHardwareBuffer_isValidDescription(desc, /*log=*/false)) return 0;
bool supported = false;
- GraphicBuffer* gBuffer = new GraphicBuffer();
+ sp<GraphicBuffer> gBuffer(new GraphicBuffer());
status_t err = gBuffer->isSupported(desc->width, desc->height, desc->format, desc->layers,
desc->usage, &supported);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index ecfaef8..07c5dd8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -42,6 +42,7 @@
],
static_libs: [
+ "libshaders",
"libtonemap",
],
local_include_dirs: ["include"],
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index 65d4895..c7ad058 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -26,23 +26,7 @@
namespace android {
namespace renderengine {
-std::unique_ptr<RenderEngine> RenderEngine::create(RenderEngineCreationArgs args) {
- // Keep the ability to override by PROPERTIES:
- char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
- if (strcmp(prop, "gles") == 0) {
- args.renderEngineType = RenderEngineType::GLES;
- }
- if (strcmp(prop, "threaded") == 0) {
- args.renderEngineType = RenderEngineType::THREADED;
- }
- if (strcmp(prop, "skiagl") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL;
- }
- if (strcmp(prop, "skiaglthreaded") == 0) {
- args.renderEngineType = RenderEngineType::SKIA_GL_THREADED;
- }
-
+std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
switch (args.renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
index baa5054..471159f 100644
--- a/libs/renderengine/benchmark/Android.bp
+++ b/libs/renderengine/benchmark/Android.bp
@@ -35,6 +35,7 @@
],
static_libs: [
"librenderengine",
+ "libshaders",
"libtonemap",
],
cflags: [
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
index 6b85c57..b9cc648 100644
--- a/libs/renderengine/include/renderengine/RenderEngine.h
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -99,7 +99,7 @@
SKIA_GL_THREADED = 4,
};
- static std::unique_ptr<RenderEngine> create(RenderEngineCreationArgs args);
+ static std::unique_ptr<RenderEngine> create(const RenderEngineCreationArgs& args);
virtual ~RenderEngine() = 0;
diff --git a/libs/renderengine/skia/ColorSpaces.cpp b/libs/renderengine/skia/ColorSpaces.cpp
index ff4d348..f367a84 100644
--- a/libs/renderengine/skia/ColorSpaces.cpp
+++ b/libs/renderengine/skia/ColorSpaces.cpp
@@ -20,6 +20,7 @@
namespace renderengine {
namespace skia {
+// please keep in sync with hwui/utils/Color.cpp
sk_sp<SkColorSpace> toSkColorSpace(ui::Dataspace dataspace) {
skcms_Matrix3x3 gamut;
switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
@@ -32,6 +33,17 @@
case HAL_DATASPACE_STANDARD_DCI_P3:
gamut = SkNamedGamut::kDisplayP3;
break;
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ gamut = SkNamedGamut::kAdobeRGB;
+ break;
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ case HAL_DATASPACE_STANDARD_BT470M:
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
default:
gamut = SkNamedGamut::kSRGB;
break;
@@ -42,10 +54,19 @@
return SkColorSpace::MakeRGB(SkNamedTransferFn::kLinear, gamut);
case HAL_DATASPACE_TRANSFER_SRGB:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ return SkColorSpace::MakeRGB({2.2f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ return SkColorSpace::MakeRGB({2.6f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ return SkColorSpace::MakeRGB({2.8f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, gamut);
case HAL_DATASPACE_TRANSFER_ST2084:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kPQ, gamut);
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ return SkColorSpace::MakeRGB(SkNamedTransferFn::kRec2020, gamut);
case HAL_DATASPACE_TRANSFER_HLG:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kHLG, gamut);
+ case HAL_DATASPACE_TRANSFER_UNSPECIFIED:
default:
return SkColorSpace::MakeRGB(SkNamedTransferFn::kSRGB, gamut);
}
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.cpp b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
index 21d5603..376e279 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -636,9 +636,9 @@
const ui::Dataspace outputDataspace =
mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- LinearEffect effect = LinearEffect{.inputDataspace = inputDataspace,
- .outputDataspace = outputDataspace,
- .undoPremultipliedAlpha = undoPremultipliedAlpha};
+ auto effect = shaders::LinearEffect{.inputDataspace = inputDataspace,
+ .outputDataspace = outputDataspace,
+ .undoPremultipliedAlpha = undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 74ce651..53792f9 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -133,7 +133,8 @@
// Cache of GL textures that we'll store per GraphicBuffer ID, shared between GPU contexts.
std::unordered_map<GraphicBufferId, std::shared_ptr<AutoBackendTexture::LocalRef>> mTextureCache
GUARDED_BY(mRenderingMutex);
- std::unordered_map<LinearEffect, sk_sp<SkRuntimeEffect>, LinearEffectHasher> mRuntimeEffects;
+ std::unordered_map<shaders::LinearEffect, sk_sp<SkRuntimeEffect>, shaders::LinearEffectHasher>
+ mRuntimeEffects;
AutoBackendTexture::CleanupManager mTextureCleanupMgr GUARDED_BY(mRenderingMutex);
StretchShaderFactory mStretchShaderFactory;
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index c3a5a60..36305ae 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,303 +19,19 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <SkString.h>
-#include <tonemap/tonemap.h>
+#include <log/log.h>
+#include <shaders/shaders.h>
#include <utils/Trace.h>
-#include <optional>
-
-#include "log/log.h"
-#include "math/mat4.h"
-#include "system/graphics-base-v1.0.h"
-#include "ui/ColorSpace.h"
+#include <math/mat4.h>
namespace android {
namespace renderengine {
namespace skia {
-static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
- ui::Dataspace dataspace) {
- return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
-}
-
-static void generateEOTF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 EOTF(float3 color) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
- tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
- return pow(tmp, 1.0 / float3(m1));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float EOTF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 0.5 ? channel * channel / 3.0 :
- (exp((channel - c) / a) + b) / 12.0;
- }
-
- float3 EOTF(float3 color) {
- return float3(EOTF_channel(color.r), EOTF_channel(color.g),
- EOTF_channel(color.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 EOTF(float3 color) {
- return color;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
-
- float EOTF_sRGB(float srgb) {
- return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
- }
-
- float3 EOTF_sRGB(float3 srgb) {
- return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
- }
-
- float3 EOTF(float3 srgb) {
- return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateXYZTransforms(SkString& shader) {
- shader.append(R"(
- uniform float4x4 in_rgbToXyz;
- uniform float4x4 in_xyzToRgb;
- float3 ToXYZ(float3 rgb) {
- return clamp((in_rgbToXyz * float4(rgb, 1.0)).rgb, 0.0, 1.0);
- }
-
- float3 ToRGB(float3 xyz) {
- return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
- }
- )");
-}
-
-// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
-static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
- ui::Dataspace outputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * 1000.0 * pow(xyz.y, 0.2);
- }
- )");
- break;
- default:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // SDR -> HDR tonemap
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_inputMaxLuminance;
- }
- )");
- break;
- default:
- // Input and output are both SDR, so no tone-mapping is expected so
- // no-op the luminance normalization.
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_libtonemap_displayMaxLuminance;
- }
- )");
- break;
- }
- }
-}
-
-// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
-static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace, SkString& shader) {
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 10000.0;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
- }
- )");
- break;
- default:
- shader.append(R"(
- float3 NormalizeLuminance(float3 xyz) {
- return xyz / in_libtonemap_displayMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- SkString& shader) {
- shader.append(tonemap::getToneMapper()
- ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
- toAidlDataspace(outputDataspace))
- .c_str());
-
- generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
-
- shader.append(R"(
- float3 OOTF(float3 linearRGB, float3 xyz) {
- float3 scaledLinearRGB = ScaleLuminance(linearRGB);
- float3 scaledXYZ = ScaleLuminance(xyz);
-
- float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
-
- return NormalizeLuminance(scaledXYZ * gain);
- }
- )");
-}
-
-static void generateOETF(ui::Dataspace dataspace, SkString& shader) {
- switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
-
- float3 OETF(float3 xyz) {
- float m1 = (2610.0 / 4096.0) / 4.0;
- float m2 = (2523.0 / 4096.0) * 128.0;
- float c1 = (3424.0 / 4096.0);
- float c2 = (2413.0 / 4096.0) * 32.0;
- float c3 = (2392.0 / 4096.0) * 32.0;
-
- float3 tmp = pow(xyz, float3(m1));
- tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
- return pow(tmp, float3(m2));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- shader.append(R"(
- float OETF_channel(float channel) {
- const float a = 0.17883277;
- const float b = 0.28466892;
- const float c = 0.55991073;
- return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
- a * log(12.0 * channel - b) + c;
- }
-
- float3 OETF(float3 linear) {
- return float3(OETF_channel(linear.r), OETF_channel(linear.g),
- OETF_channel(linear.b));
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_LINEAR:
- shader.append(R"(
- float3 OETF(float3 linear) {
- return linear;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_SRGB:
- default:
- shader.append(R"(
- float OETF_sRGB(float linear) {
- return linear <= 0.0031308 ?
- linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
- }
-
- float3 OETF_sRGB(float3 linear) {
- return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
- }
-
- float3 OETF(float3 linear) {
- return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
- }
- )");
- break;
- }
-}
-
-static void generateEffectiveOOTF(bool undoPremultipliedAlpha, SkString& shader) {
- shader.append(R"(
- uniform shader child;
- half4 main(float2 xy) {
- float4 c = float4(child.eval(xy));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb / (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- float3 linearRGB = EOTF(c.rgb);
- float3 xyz = ToXYZ(linearRGB);
- c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
- )");
- if (undoPremultipliedAlpha) {
- shader.append(R"(
- c.rgb = c.rgb * (c.a + 0.0019);
- )");
- }
- shader.append(R"(
- return c;
- }
- )");
-}
-static ColorSpace toColorSpace(ui::Dataspace dataspace) {
- switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
- case HAL_DATASPACE_STANDARD_BT709:
- return ColorSpace::sRGB();
- break;
- case HAL_DATASPACE_STANDARD_DCI_P3:
- return ColorSpace::DisplayP3();
- break;
- case HAL_DATASPACE_STANDARD_BT2020:
- return ColorSpace::BT2020();
- break;
- default:
- return ColorSpace::sRGB();
- break;
- }
-}
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect) {
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect) {
ATRACE_CALL();
- SkString shaderString;
- generateEOTF(linearEffect.inputDataspace, shaderString);
- generateXYZTransforms(shaderString);
- generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
- generateOETF(linearEffect.outputDataspace, shaderString);
- generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ SkString shaderString = SkString(shaders::buildLinearEffectSkSL(linearEffect));
auto [shader, error] = SkRuntimeEffect::MakeForShader(shaderString);
if (!shader) {
@@ -324,7 +40,8 @@
return shader;
}
-sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader, const LinearEffect& linearEffect,
+sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> shader,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance) {
@@ -333,27 +50,8 @@
effectBuilder.child("child") = shader;
- if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
- effectBuilder.uniform("in_rgbToXyz") = mat4();
- effectBuilder.uniform("in_xyzToRgb") = colorTransform;
- } else {
- ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
- ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
-
- effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
- effectBuilder.uniform("in_xyzToRgb") =
- colorTransform * mat4(outputColorSpace.getXYZtoRGB());
- }
-
- tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
- // If the input luminance is unknown, use display luminance (aka,
- // no-op any luminance changes)
- // This will be the case for eg screenshots in addition to
- // uncalibrated displays
- .contentMaxLuminance =
- maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};
-
- const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);
+ const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
+ maxDisplayLuminance, maxLuminance);
for (const auto& uniform : uniforms) {
effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
diff --git a/libs/renderengine/skia/filters/LinearEffect.h b/libs/renderengine/skia/filters/LinearEffect.h
index 14a3b61..8eb6670 100644
--- a/libs/renderengine/skia/filters/LinearEffect.h
+++ b/libs/renderengine/skia/filters/LinearEffect.h
@@ -20,6 +20,7 @@
#include <optional>
+#include <shaders/shaders.h>
#include "SkRuntimeEffect.h"
#include "SkShader.h"
#include "ui/GraphicTypes.h"
@@ -28,61 +29,7 @@
namespace renderengine {
namespace skia {
-/**
- * Arguments for creating an effect that applies color transformations in linear XYZ space.
- * A linear effect is decomposed into the following steps when operating on an image:
- * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
- * relative display brightness of the scene in nits for each RGB channel
- * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
- * luminance.
- * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
- * mapping to display SDR content alongside HDR content, or any number of subjective transformations
- * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
- * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
- * output RGB colors.
- *
- * For further reading, consult the recommendation in ITU-R BT.2390-4:
- * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
- *
- * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
- * intended to be the output surface. However, Skia does not support complex tone mapping such as
- * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
- * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
- * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
- * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
- * and intermediate values are interpolated.
- */
-struct LinearEffect {
- // Input dataspace of the source colors.
- const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
-
- // Working dataspace for the output surface, for conversion from linear space.
- const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
-
- // Sets whether alpha premultiplication must be undone.
- // This is required if the source colors use premultiplied alpha and is not opaque.
- const bool undoPremultipliedAlpha = false;
-};
-
-static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
- return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
- lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha;
-}
-
-struct LinearEffectHasher {
- // Inspired by art/runtime/class_linker.cc
- // Also this is what boost:hash_combine does
- static size_t HashCombine(size_t seed, size_t val) {
- return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
- }
- size_t operator()(const LinearEffect& le) const {
- size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
- result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
- return HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
- }
-};
-
-sk_sp<SkRuntimeEffect> buildRuntimeEffect(const LinearEffect& linearEffect);
+sk_sp<SkRuntimeEffect> buildRuntimeEffect(const shaders::LinearEffect& linearEffect);
// Generates a shader resulting from applying the a linear effect created from
// LinearEffectArgs::buildEffect to an inputShader.
@@ -93,7 +40,7 @@
// * The max luminance is provided as the max luminance for the buffer, either from the SMPTE 2086
// or as the max light level from the CTA 861.3 standard.
sk_sp<SkShader> createLinearEffectShader(sk_sp<SkShader> inputShader,
- const LinearEffect& linearEffect,
+ const shaders::LinearEffect& linearEffect,
sk_sp<SkRuntimeEffect> runtimeEffect,
const mat4& colorTransform, float maxDisplayLuminance,
float maxLuminance);
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
index 52b6c8f..a426850 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -39,6 +39,7 @@
"libgmock",
"librenderengine",
"librenderengine_mocks",
+ "libshaders",
"libtonemap",
],
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index c2c05f4..8259063 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -27,6 +27,9 @@
#include <renderengine/ExternalTexture.h>
#include <renderengine/RenderEngine.h>
#include <sync/sync.h>
+#include <system/graphics-base-v1.0.h>
+#include <tonemap/tonemap.h>
+#include <ui/ColorSpace.h>
#include <ui/PixelFormat.h>
#include <chrono>
@@ -282,6 +285,13 @@
void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
uint8_t tolerance = 0) {
+ auto generator = [=](Point) { return ubyte4(r, g, b, a); };
+ expectBufferColor(rect, generator, tolerance);
+ }
+
+ using ColorGenerator = std::function<ubyte4(Point location)>;
+
+ void expectBufferColor(const Rect& rect, ColorGenerator generator, uint8_t tolerance = 0) {
auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
uint8_t tmp = a >= b ? a - b : b - a;
@@ -290,10 +300,10 @@
return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
- expectBufferColor(rect, r, g, b, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ void expectBufferColor(const Rect& region, ColorGenerator generator,
std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
uint8_t* pixels;
mBuffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
@@ -304,19 +314,22 @@
const uint8_t* src = pixels +
(mBuffer->getBuffer()->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
- const uint8_t expected[4] = {r, g, b, a};
- bool equal = colorCompare(src, expected);
- EXPECT_TRUE(equal)
+ const auto location = Point(region.left + i, region.top + j);
+ const ubyte4 colors = generator(location);
+ const uint8_t expected[4] = {colors.r, colors.g, colors.b, colors.a};
+ bool colorMatches = colorCompare(src, expected);
+ EXPECT_TRUE(colorMatches)
<< GetParam()->name().c_str() << ": "
- << "pixel @ (" << region.left + i << ", " << region.top + j << "): "
- << "expected (" << static_cast<uint32_t>(r) << ", "
- << static_cast<uint32_t>(g) << ", " << static_cast<uint32_t>(b) << ", "
- << static_cast<uint32_t>(a) << "), "
+ << "pixel @ (" << location.x << ", " << location.y << "): "
+ << "expected (" << static_cast<uint32_t>(colors.r) << ", "
+ << static_cast<uint32_t>(colors.g) << ", "
+ << static_cast<uint32_t>(colors.b) << ", "
+ << static_cast<uint32_t>(colors.a) << "), "
<< "got (" << static_cast<uint32_t>(src[0]) << ", "
<< static_cast<uint32_t>(src[1]) << ", " << static_cast<uint32_t>(src[2])
<< ", " << static_cast<uint32_t>(src[3]) << ")";
src += 4;
- if (!equal && ++fails >= maxFails) {
+ if (!colorMatches && ++fails >= maxFails) {
break;
}
}
@@ -328,10 +341,11 @@
}
void expectAlpha(const Rect& rect, uint8_t a) {
+ auto generator = [=](Point) { return ubyte4(0, 0, 0, a); };
auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
return colorA[3] == colorB[3];
};
- expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ expectBufferColor(rect, generator, colorCompare);
}
void expectShadowColor(const renderengine::LayerSettings& castingLayer,
@@ -490,6 +504,18 @@
void fillBufferColorTransform();
template <typename SourceVariant>
+ void fillBufferWithColorTransformAndSourceDataspace(const ui::Dataspace sourceDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndSourceDataspace();
+
+ template <typename SourceVariant>
+ void fillBufferWithColorTransformAndOutputDataspace(const ui::Dataspace outputDataspace);
+
+ template <typename SourceVariant>
+ void fillBufferColorTransformAndOutputDataspace();
+
+ template <typename SourceVariant>
void fillBufferWithColorTransformZeroLayerAlpha();
template <typename SourceVariant>
@@ -867,12 +893,104 @@
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndSourceDataspace(
+ const ui::Dataspace sourceDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = ui::Dataspace::V0_SRGB_LINEAR;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = sourceDataspace;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
void RenderEngineTest::fillBufferColorTransform() {
fillBufferWithColorTransform<SourceVariant>();
expectBufferColor(fullscreenRect(), 172, 0, 0, 255, 1);
}
template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndSourceDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {172, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {172, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_2 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {172, 0, 0, 255};
+ for (const auto& [sourceDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndSourceDataspace<SourceVariant>(sourceDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferWithColorTransformAndOutputDataspace(
+ const ui::Dataspace outputDataspace) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = Rect(1, 1);
+ settings.outputDataspace = outputDataspace;
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ renderengine::LayerSettings layer;
+ layer.sourceDataspace = ui::Dataspace::V0_SCRGB_LINEAR;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+ SourceVariant::fillColor(layer, 0.5f, 0.25f, 0.125f, this);
+ layer.alpha = 1.0f;
+
+ // construct a fake color matrix
+ // annihilate green and blue channels
+ settings.colorTransform = mat4::scale(vec4(0.9f, 0, 0, 1));
+ // set red channel to red + green
+ layer.colorTransform = mat4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
+
+ layer.alpha = 1.0f;
+ layer.geometry.boundaries = Rect(1, 1).toFloatRect();
+
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers);
+}
+
+template <typename SourceVariant>
+void RenderEngineTest::fillBufferColorTransformAndOutputDataspace() {
+ unordered_map<ui::Dataspace, ubyte4> dataspaceToColorMap;
+ dataspaceToColorMap[ui::Dataspace::V0_BT709] = {202, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::BT2020] = {192, 0, 0, 255};
+ dataspaceToColorMap[ui::Dataspace::ADOBE_RGB] = {202, 0, 0, 255};
+ ui::Dataspace customizedDataspace = static_cast<ui::Dataspace>(
+ ui::Dataspace::STANDARD_BT709 | ui::Dataspace::TRANSFER_GAMMA2_6 |
+ ui::Dataspace::RANGE_FULL);
+ dataspaceToColorMap[customizedDataspace] = {202, 0, 0, 255};
+ for (const auto& [outputDataspace, color] : dataspaceToColorMap) {
+ fillBufferWithColorTransformAndOutputDataspace<SourceVariant>(outputDataspace);
+ expectBufferColor(fullscreenRect(), color.r, color.g, color.b, color.a, 1);
+ }
+}
+
+template <typename SourceVariant>
void RenderEngineTest::fillBufferWithColorTransformZeroLayerAlpha() {
renderengine::DisplaySettings settings;
settings.physicalDisplay = fullscreenRect();
@@ -1099,7 +1217,7 @@
layer.source.buffer.buffer = buf;
layer.source.buffer.textureName = texName;
// Transform coordinates to only be inside the red quadrant.
- layer.source.buffer.textureTransform = mat4::scale(vec4(0.2, 0.2, 1, 1));
+ layer.source.buffer.textureTransform = mat4::scale(vec4(0.2f, 0.2f, 1.f, 1.f));
layer.alpha = 1.0f;
layer.geometry.boundaries = Rect(1, 1).toFloatRect();
@@ -1281,7 +1399,8 @@
settings.clip = fullscreenRect();
// 255, 255, 255, 255 is full opaque white.
- const ubyte4 backgroundColor(255.f, 255.f, 255.f, 255.f);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
// Create layer with given color.
renderengine::LayerSettings bgLayer;
bgLayer.sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR;
@@ -1412,6 +1531,36 @@
fillBufferColorTransform<ColorSourceVariant>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_sourceDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<ColorSourceVariant>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransform_outputDataspace) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<ColorSourceVariant>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_colorSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<ColorSourceVariant>();
@@ -1492,6 +1641,36 @@
fillBufferColorTransform<BufferSourceVariant<ForceOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_opaqueBufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<ForceOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_opaqueBufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<ForceOpaqueBufferVariant>>();
@@ -1572,6 +1751,36 @@
fillBufferColorTransform<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
}
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndSourceDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndSourceDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
+TEST_P(RenderEngineTest, drawLayers_fillBufferColorTransformAndOutputDataspace_bufferSource) {
+ const auto& renderEngineFactory = GetParam();
+ // skip for non color management
+ if (!renderEngineFactory->useColorManagement()) {
+ return;
+ }
+ // skip for GLESRenderEngine
+ if (renderEngineFactory->type() != renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+ fillBufferColorTransformAndOutputDataspace<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
+}
+
TEST_P(RenderEngineTest, drawLayers_fillBufferRoundedCorners_bufferSource) {
initializeRenderEngine();
fillBufferWithRoundedCorners<BufferSourceVariant<RelaxOpaqueBufferVariant>>();
@@ -1615,7 +1824,8 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_castsWithoutCasterLayer) {
initializeRenderEngine();
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1630,8 +1840,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(1, 1);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1649,8 +1861,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1669,8 +1883,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -1690,8 +1906,10 @@
TEST_P(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
initializeRenderEngine();
- const ubyte4 casterColor(255, 0, 0, 255);
- const ubyte4 backgroundColor(255, 255, 255, 255);
+ const ubyte4 casterColor(static_cast<uint8_t>(255), static_cast<uint8_t>(0),
+ static_cast<uint8_t>(0), static_cast<uint8_t>(255));
+ const ubyte4 backgroundColor(static_cast<uint8_t>(255), static_cast<uint8_t>(255),
+ static_cast<uint8_t>(255), static_cast<uint8_t>(255));
const float shadowLength = 5.0f;
Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
@@ -2027,6 +2245,155 @@
expectBufferColor(rect, 0, 255, 0, 255);
}
}
+
+double EOTF_PQ(double channel) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float tmp = std::pow(std::clamp(channel, 0.0, 1.0), 1.0 / m2);
+ tmp = std::fmax(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return std::pow(tmp, 1.0 / m1);
+}
+
+vec3 EOTF_PQ(vec3 color) {
+ return vec3(EOTF_PQ(color.r), EOTF_PQ(color.g), EOTF_PQ(color.b));
+}
+
+double OETF_sRGB(double channel) {
+ return channel <= 0.0031308 ? channel * 12.92 : (pow(channel, 1.0 / 2.4) * 1.055) - 0.055;
+}
+
+int sign(float in) {
+ return in >= 0.0 ? 1 : -1;
+}
+
+vec3 OETF_sRGB(vec3 linear) {
+ return vec3(sign(linear.r) * OETF_sRGB(linear.r), sign(linear.g) * OETF_sRGB(linear.g),
+ sign(linear.b) * OETF_sRGB(linear.b));
+}
+
+TEST_P(RenderEngineTest, test_tonemapPQMatches) {
+ if (!GetParam()->useColorManagement()) {
+ return;
+ }
+
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ initializeRenderEngine();
+
+ constexpr int32_t kGreyLevels = 256;
+
+ const auto rect = Rect(0, 0, kGreyLevels, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = rect,
+ .clip = rect,
+ .maxLuminance = 750.0f,
+ .outputDataspace = ui::Dataspace::DISPLAY_P3,
+ };
+
+ auto buf = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "input"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, buf->getBuffer()->initCheck());
+
+ {
+ uint8_t* pixels;
+ buf->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+
+ uint8_t color = 0;
+ for (int32_t j = 0; j < buf->getBuffer()->getHeight(); j++) {
+ uint8_t* dest = pixels + (buf->getBuffer()->getStride() * j * 4);
+ for (int32_t i = 0; i < buf->getBuffer()->getWidth(); i++) {
+ dest[0] = color;
+ dest[1] = color;
+ dest[2] = color;
+ dest[3] = 255;
+ color++;
+ dest += 4;
+ }
+ }
+ buf->getBuffer()->unlock();
+ }
+
+ mBuffer = std::make_shared<
+ renderengine::ExternalTexture>(new GraphicBuffer(kGreyLevels, 1,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_READ_OFTEN |
+ GRALLOC_USAGE_SW_WRITE_OFTEN |
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE,
+ "output"),
+ *mRE,
+ renderengine::ExternalTexture::Usage::READABLE |
+ renderengine::ExternalTexture::Usage::WRITEABLE);
+ ASSERT_EQ(0, mBuffer->getBuffer()->initCheck());
+
+ const renderengine::LayerSettings layer{
+ .geometry.boundaries = rect.toFloatRect(),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = std::move(buf),
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = static_cast<ui::Dataspace>(HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ };
+
+ std::vector<renderengine::LayerSettings> layers{layer};
+ invokeDraw(display, layers);
+
+ ColorSpace displayP3 = ColorSpace::DisplayP3();
+ ColorSpace bt2020 = ColorSpace::BT2020();
+
+ tonemap::Metadata metadata{.displayMaxLuminance = 750.0f};
+
+ auto generator = [=](Point location) {
+ const double normColor = static_cast<double>(location.x) / (kGreyLevels - 1);
+ const vec3 rgb = vec3(normColor, normColor, normColor);
+
+ const vec3 linearRGB = EOTF_PQ(rgb);
+
+ static constexpr float kMaxPQLuminance = 10000.f;
+ const vec3 xyz = bt2020.getRGBtoXYZ() * linearRGB * kMaxPQLuminance;
+ const double gain =
+ tonemap::getToneMapper()
+ ->lookupTonemapGain(static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ HAL_DATASPACE_STANDARD_BT2020 |
+ HAL_DATASPACE_TRANSFER_ST2084 |
+ HAL_DATASPACE_RANGE_FULL),
+ static_cast<aidl::android::hardware::graphics::common::
+ Dataspace>(
+ ui::Dataspace::DISPLAY_P3),
+ linearRGB * 10000.0, xyz, metadata);
+ const vec3 scaledXYZ = xyz * gain / metadata.displayMaxLuminance;
+
+ const vec3 targetRGB = OETF_sRGB(displayP3.getXYZtoRGB() * scaledXYZ) * 255;
+ return ubyte4(static_cast<uint8_t>(targetRGB.r), static_cast<uint8_t>(targetRGB.g),
+ static_cast<uint8_t>(targetRGB.b), 255);
+ };
+
+ expectBufferColor(Rect(kGreyLevels, 1), generator, 2);
+}
} // namespace renderengine
} // namespace android
diff --git a/libs/shaders/Android.bp b/libs/shaders/Android.bp
new file mode 100644
index 0000000..390b228
--- /dev/null
+++ b/libs/shaders/Android.bp
@@ -0,0 +1,44 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_library_static {
+ name: "libshaders",
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "android.hardware.graphics.common@1.2",
+ ],
+
+ static_libs: [
+ "libmath",
+ "libtonemap",
+ "libui-types",
+ ],
+
+ srcs: [
+ "shaders.cpp",
+ ],
+}
diff --git a/libs/shaders/OWNERS b/libs/shaders/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/shaders/OWNERS
@@ -0,0 +1,4 @@
+alecmouri@google.com
+jreck@google.com
+sallyqi@google.com
+scroggo@google.com
\ No newline at end of file
diff --git a/libs/shaders/include/shaders/shaders.h b/libs/shaders/include/shaders/shaders.h
new file mode 100644
index 0000000..712a27a
--- /dev/null
+++ b/libs/shaders/include/shaders/shaders.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <math/mat4.h>
+#include <tonemap/tonemap.h>
+#include <ui/GraphicTypes.h>
+#include <cstddef>
+
+namespace android::shaders {
+
+/**
+ * Arguments for creating an effect that applies color transformations in linear XYZ space.
+ * A linear effect is decomposed into the following steps when operating on an image:
+ * 1. Electrical-Optical Transfer Function (EOTF) maps the input RGB signal into the intended
+ * relative display brightness of the scene in nits for each RGB channel
+ * 2. Transformation matrix from linear RGB brightness to linear XYZ, to operate on display
+ * luminance.
+ * 3. Opto-Optical Transfer Function (OOTF) applies a "rendering intent". This can include tone
+ * mapping to display SDR content alongside HDR content, or any number of subjective transformations
+ * 4. Transformation matrix from linear XYZ back to linear RGB brightness.
+ * 5. Opto-Electronic Transfer Function (OETF) maps the display brightness of the scene back to
+ * output RGB colors.
+ *
+ * For further reading, consult the recommendation in ITU-R BT.2390-4:
+ * https://www.itu.int/dms_pub/itu-r/opb/rep/R-REP-BT.2390-4-2018-PDF-E.pdf
+ *
+ * Skia normally attempts to do its own simple tone mapping, i.e., the working color space is
+ * intended to be the output surface. However, Skia does not support complex tone mapping such as
+ * polynomial interpolation. As such, this filter assumes that tone mapping has not yet been applied
+ * to the source colors. so that the tone mapping process is only applied once by this effect. Tone
+ * mapping is applied when presenting HDR content (content with HLG or PQ transfer functions)
+ * alongside other content, whereby maximum input luminance is mapped to maximum output luminance
+ * and intermediate values are interpolated.
+ */
+struct LinearEffect {
+ // Input dataspace of the source colors.
+ const ui::Dataspace inputDataspace = ui::Dataspace::SRGB;
+
+ // Working dataspace for the output surface, for conversion from linear space.
+ const ui::Dataspace outputDataspace = ui::Dataspace::SRGB;
+
+ // Sets whether alpha premultiplication must be undone.
+ // This is required if the source colors use premultiplied alpha and is not opaque.
+ const bool undoPremultipliedAlpha = false;
+
+ // "Fake" dataspace of the source colors. This is used for applying an EOTF to compute linear
+ // RGB. This is used when Skia is expected to color manage the input image based on the
+ // dataspace of the provided source image and destination surface. SkRuntimeEffects use the
+ // destination color space as the working color space. RenderEngine deliberately sets the color
+ // space for input images and destination surfaces to be the same whenever LinearEffects are
+ // expected to be used so that color-management is controlled by RenderEngine, but other users
+ // of a LinearEffect may not be able to control the color space of the images and surfaces. So
+ // fakeInputDataspace is used to essentially masquerade the input dataspace to be the output
+ // dataspace for correct conversion to linear colors.
+ ui::Dataspace fakeInputDataspace = ui::Dataspace::UNKNOWN;
+};
+
+static inline bool operator==(const LinearEffect& lhs, const LinearEffect& rhs) {
+ return lhs.inputDataspace == rhs.inputDataspace && lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.undoPremultipliedAlpha == rhs.undoPremultipliedAlpha &&
+ lhs.fakeInputDataspace == rhs.fakeInputDataspace;
+}
+
+struct LinearEffectHasher {
+ // Inspired by art/runtime/class_linker.cc
+ // Also this is what boost:hash_combine does
+ static size_t HashCombine(size_t seed, size_t val) {
+ return seed ^ (val + 0x9e3779b9 + (seed << 6) + (seed >> 2));
+ }
+ size_t operator()(const LinearEffect& le) const {
+ size_t result = std::hash<ui::Dataspace>{}(le.inputDataspace);
+ result = HashCombine(result, std::hash<ui::Dataspace>{}(le.outputDataspace));
+ result = HashCombine(result, std::hash<bool>{}(le.undoPremultipliedAlpha));
+ return HashCombine(result, std::hash<ui::Dataspace>{}(le.fakeInputDataspace));
+ }
+};
+
+// Generates a shader string that applies color transforms in linear space.
+// Typical use-cases supported:
+// 1. Apply tone-mapping
+// 2. Apply color transform matrices in linear space
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect);
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance);
+
+} // namespace android::shaders
diff --git a/libs/shaders/shaders.cpp b/libs/shaders/shaders.cpp
new file mode 100644
index 0000000..6019c4a
--- /dev/null
+++ b/libs/shaders/shaders.cpp
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <shaders/shaders.h>
+
+#include <tonemap/tonemap.h>
+
+#include <optional>
+
+#include <math/mat4.h>
+#include <system/graphics-base-v1.0.h>
+#include <ui/ColorSpace.h>
+
+namespace android::shaders {
+
+static aidl::android::hardware::graphics::common::Dataspace toAidlDataspace(
+ ui::Dataspace dataspace) {
+ return static_cast<aidl::android::hardware::graphics::common::Dataspace>(dataspace);
+}
+
+static void generateEOTF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 EOTF(float3 color) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(clamp(color, 0.0, 1.0), 1.0 / float3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / float3(m1));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float EOTF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ float3 EOTF(float3 color) {
+ return float3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 EOTF(float3 color) {
+ return color;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.08125 ? srgb / 4.50 : pow((srgb + 0.099) / 1.099, 0.45);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.2);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.6);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return pow(srgb, 2.8);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ float3 EOTF_sRGB(float3 srgb) {
+ return float3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ float3 EOTF(float3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateXYZTransforms(std::string& shader) {
+ shader.append(R"(
+ uniform float4x4 in_rgbToXyz;
+ uniform float4x4 in_xyzToRgb;
+ float3 ToXYZ(float3 rgb) {
+ return (in_rgbToXyz * float4(rgb, 1.0)).rgb;
+ }
+
+ float3 ToRGB(float3 xyz) {
+ return clamp((in_xyzToRgb * float4(xyz, 1.0)).rgb, 0.0, 1.0);
+ }
+ )");
+}
+
+// Conversion from relative light to absolute light (maps from [0, 1] to [0, maxNits])
+static void generateLuminanceScalesForOOTF(ui::Dataspace inputDataspace,
+ ui::Dataspace outputDataspace, std::string& shader) {
+ switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * 1000.0 * pow(xyz.y, 0.2);
+ }
+ )");
+ break;
+ default:
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ case HAL_DATASPACE_TRANSFER_HLG:
+ // SDR -> HDR tonemap
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_inputMaxLuminance;
+ }
+ )");
+ break;
+ default:
+ // Input and output are both SDR, so no tone-mapping is expected so
+ // no-op the luminance normalization.
+ shader.append(R"(
+ float3 ScaleLuminance(float3 xyz) {
+ return xyz * in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+ }
+}
+
+// Normalizes from absolute light back to relative light (maps from [0, maxNits] back to [0, 1])
+static void generateLuminanceNormalizationForOOTF(ui::Dataspace outputDataspace,
+ std::string& shader) {
+ switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 10000.0;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / 1000.0 * pow(xyz.y / 1000.0, -0.2 / 1.2);
+ }
+ )");
+ break;
+ default:
+ shader.append(R"(
+ float3 NormalizeLuminance(float3 xyz) {
+ return xyz / in_libtonemap_displayMaxLuminance;
+ }
+ )");
+ break;
+ }
+}
+
+static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
+ std::string& shader) {
+ shader.append(tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(toAidlDataspace(inputDataspace),
+ toAidlDataspace(outputDataspace))
+ .c_str());
+
+ generateLuminanceScalesForOOTF(inputDataspace, outputDataspace, shader);
+ generateLuminanceNormalizationForOOTF(outputDataspace, shader);
+
+ shader.append(R"(
+ float3 OOTF(float3 linearRGB, float3 xyz) {
+ float3 scaledLinearRGB = ScaleLuminance(linearRGB);
+ float3 scaledXYZ = ScaleLuminance(xyz);
+
+ float gain = libtonemap_LookupTonemapGain(scaledLinearRGB, scaledXYZ);
+
+ return NormalizeLuminance(scaledXYZ * gain);
+ }
+ )");
+}
+
+static void generateOETF(ui::Dataspace dataspace, std::string& shader) {
+ switch (dataspace & HAL_DATASPACE_TRANSFER_MASK) {
+ case HAL_DATASPACE_TRANSFER_ST2084:
+ shader.append(R"(
+
+ float3 OETF(float3 xyz) {
+ float m1 = (2610.0 / 4096.0) / 4.0;
+ float m2 = (2523.0 / 4096.0) * 128.0;
+ float c1 = (3424.0 / 4096.0);
+ float c2 = (2413.0 / 4096.0) * 32.0;
+ float c3 = (2392.0 / 4096.0) * 32.0;
+
+ float3 tmp = pow(xyz, float3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float3(m2));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_HLG:
+ shader.append(R"(
+ float OETF_channel(float channel) {
+ const float a = 0.17883277;
+ const float b = 0.28466892;
+ const float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ float3 OETF(float3 linear) {
+ return float3(OETF_channel(linear.r), OETF_channel(linear.g),
+ OETF_channel(linear.b));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_LINEAR:
+ shader.append(R"(
+ float3 OETF(float3 linear) {
+ return linear;
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SMPTE_170M:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.018 ?
+ linear * 4.50 : (pow(linear, 0.45) * 1.099) - 0.099;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_2:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.2));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_6:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.6));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_GAMMA2_8:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return pow(linear, (1.0 / 2.8));
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ case HAL_DATASPACE_TRANSFER_SRGB:
+ default:
+ shader.append(R"(
+ float OETF_sRGB(float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ float3 OETF_sRGB(float3 linear) {
+ return float3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ float3 OETF(float3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )");
+ break;
+ }
+}
+
+static void generateEffectiveOOTF(bool undoPremultipliedAlpha, std::string& shader) {
+ shader.append(R"(
+ uniform shader child;
+ half4 main(float2 xy) {
+ float4 c = float4(child.eval(xy));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb / (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ float3 linearRGB = EOTF(c.rgb);
+ float3 xyz = ToXYZ(linearRGB);
+ c.rgb = OETF(ToRGB(OOTF(linearRGB, xyz)));
+ )");
+ if (undoPremultipliedAlpha) {
+ shader.append(R"(
+ c.rgb = c.rgb * (c.a + 0.0019);
+ )");
+ }
+ shader.append(R"(
+ return c;
+ }
+ )");
+}
+
+// please keep in sync with toSkColorSpace function in renderengine/skia/ColorSpaces.cpp
+static ColorSpace toColorSpace(ui::Dataspace dataspace) {
+ switch (dataspace & HAL_DATASPACE_STANDARD_MASK) {
+ case HAL_DATASPACE_STANDARD_BT709:
+ return ColorSpace::sRGB();
+ case HAL_DATASPACE_STANDARD_DCI_P3:
+ return ColorSpace::DisplayP3();
+ case HAL_DATASPACE_STANDARD_BT2020:
+ case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE:
+ return ColorSpace::BT2020();
+ case HAL_DATASPACE_STANDARD_ADOBE_RGB:
+ return ColorSpace::AdobeRGB();
+ // TODO(b/208290320): BT601 format and variants return different primaries
+ case HAL_DATASPACE_STANDARD_BT601_625:
+ case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED:
+ case HAL_DATASPACE_STANDARD_BT601_525:
+ case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED:
+ // TODO(b/208290329): BT407M format returns different primaries
+ case HAL_DATASPACE_STANDARD_BT470M:
+ // TODO(b/208290904): FILM format returns different primaries
+ case HAL_DATASPACE_STANDARD_FILM:
+ case HAL_DATASPACE_STANDARD_UNSPECIFIED:
+ default:
+ return ColorSpace::sRGB();
+ }
+}
+
+std::string buildLinearEffectSkSL(const LinearEffect& linearEffect) {
+ std::string shaderString;
+ generateEOTF(linearEffect.fakeInputDataspace == ui::Dataspace::UNKNOWN
+ ? linearEffect.inputDataspace
+ : linearEffect.fakeInputDataspace,
+ shaderString);
+ generateXYZTransforms(shaderString);
+ generateOOTF(linearEffect.inputDataspace, linearEffect.outputDataspace, shaderString);
+ generateOETF(linearEffect.outputDataspace, shaderString);
+ generateEffectiveOOTF(linearEffect.undoPremultipliedAlpha, shaderString);
+ return shaderString;
+}
+
+template <typename T, std::enable_if_t<std::is_trivially_copyable<T>::value, bool> = true>
+std::vector<uint8_t> buildUniformValue(T value) {
+ std::vector<uint8_t> result;
+ result.resize(sizeof(value));
+ std::memcpy(result.data(), &value, sizeof(value));
+ return result;
+}
+
+// Generates a list of uniforms to set on the LinearEffect shader above.
+std::vector<tonemap::ShaderUniform> buildLinearEffectUniforms(const LinearEffect& linearEffect,
+ const mat4& colorTransform,
+ float maxDisplayLuminance,
+ float maxLuminance) {
+ std::vector<tonemap::ShaderUniform> uniforms;
+ if (linearEffect.inputDataspace == linearEffect.outputDataspace) {
+ uniforms.push_back({.name = "in_rgbToXyz", .value = buildUniformValue<mat4>(mat4())});
+ uniforms.push_back(
+ {.name = "in_xyzToRgb", .value = buildUniformValue<mat4>(colorTransform)});
+ } else {
+ ColorSpace inputColorSpace = toColorSpace(linearEffect.inputDataspace);
+ ColorSpace outputColorSpace = toColorSpace(linearEffect.outputDataspace);
+ uniforms.push_back({.name = "in_rgbToXyz",
+ .value = buildUniformValue<mat4>(mat4(inputColorSpace.getRGBtoXYZ()))});
+ uniforms.push_back({.name = "in_xyzToRgb",
+ .value = buildUniformValue<mat4>(
+ colorTransform * mat4(outputColorSpace.getXYZtoRGB()))});
+ }
+
+ tonemap::Metadata metadata{.displayMaxLuminance = maxDisplayLuminance,
+ // If the input luminance is unknown, use display luminance (aka,
+ // no-op any luminance changes)
+ // This will be the case for eg screenshots in addition to
+ // uncalibrated displays
+ .contentMaxLuminance =
+ maxLuminance > 0 ? maxLuminance : maxDisplayLuminance};
+
+ for (const auto uniform : tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata)) {
+ uniforms.push_back(uniform);
+ }
+
+ return uniforms;
+}
+
+} // namespace android::shaders
\ No newline at end of file
diff --git a/libs/tonemap/Android.bp b/libs/tonemap/Android.bp
index 231a342..5360fe2 100644
--- a/libs/tonemap/Android.bp
+++ b/libs/tonemap/Android.bp
@@ -30,7 +30,13 @@
shared_libs: [
"android.hardware.graphics.common-V3-ndk",
+ "liblog",
],
+
+ static_libs: [
+ "libmath",
+ ],
+
srcs: [
"tonemap.cpp",
],
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
index d350e16..bd7b72d 100644
--- a/libs/tonemap/include/tonemap/tonemap.h
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -17,6 +17,7 @@
#pragma once
#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <math/vec3.h>
#include <string>
#include <vector>
@@ -48,7 +49,9 @@
class ToneMapper {
public:
virtual ~ToneMapper() {}
- // Constructs a tonemap shader whose shader language is SkSL
+ // Constructs a tonemap shader whose shader language is SkSL, which tonemaps from an
+ // input whose dataspace is described by sourceDataspace, to an output whose dataspace
+ // is described by destinationDataspace
//
// The returned shader string *must* contain a function with the following signature:
// float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz);
@@ -94,6 +97,19 @@
// assume that there are predefined floats in_libtonemap_displayMaxLuminance and
// in_libtonemap_inputMaxLuminance inside of the body of the tone-mapping shader.
virtual std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) = 0;
+
+ // CPU implementation of the tonemapping gain. This must match the GPU implementation returned
+ // by generateTonemapGainShaderSKSL() above, with some epsilon difference to account for
+ // differences in hardware precision.
+ //
+ // The gain is computed assuming an input described by sourceDataspace, tonemapped to an output
+ // described by destinationDataspace. To compute the gain, the input colors are provided by
+ // linearRGB, which is the RGB colors in linear space. The colors in XYZ space are also
+ // provided. Metadata is also provided for helping to compute the tonemapping curve.
+ virtual double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 xyz, const Metadata& metadata) = 0;
};
// Retrieves a tonemapper instance.
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
index e58d519..f46f3fa 100644
--- a/libs/tonemap/tests/Android.bp
+++ b/libs/tonemap/tests/Android.bp
@@ -31,6 +31,7 @@
"android.hardware.graphics.common-V3-ndk",
],
static_libs: [
+ "libmath",
"libgmock",
"libgtest",
"libtonemap",
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
index 2cec773..c2372fe 100644
--- a/libs/tonemap/tonemap.cpp
+++ b/libs/tonemap/tonemap.cpp
@@ -16,6 +16,7 @@
#include <tonemap/tonemap.h>
+#include <algorithm>
#include <cstdint>
#include <mutex>
#include <type_traits>
@@ -234,9 +235,163 @@
.value = buildUniformValue<float>(metadata.contentMaxLuminance)});
return uniforms;
}
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 /* linearRGB */, vec3 xyz, const Metadata& metadata) override {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = xyz.y;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(xyz.y, 0.0f, 1000.0f);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ targetNits = xyz.y;
+ // if the max input luminance is less than what we can output then
+ // no tone mapping is needed as all color values will be in range.
+ if (metadata.contentMaxLuminance > metadata.displayMaxLuminance) {
+ // three control points
+ const double x0 = 10.0;
+ const double y0 = 17.0;
+ double x1 = metadata.displayMaxLuminance * 0.75;
+ double y1 = x1;
+ double x2 = x1 + (metadata.contentMaxLuminance - x1) / 2.0;
+ double y2 = y1 + (metadata.displayMaxLuminance - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ double h12 = x2 - x1;
+ double h23 = metadata.contentMaxLuminance - x2;
+ // tangents at the last three control points
+ double m1 = (y2 - y1) / h12;
+ double m3 = (metadata.displayMaxLuminance - y2) / h23;
+ double m2 = (m1 + m3) / 2.0;
+
+ if (targetNits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ double slope = (y1 - y0) / (x1 - x0);
+ targetNits = y0 + (targetNits - x0) * slope;
+ } else if (targetNits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ double t = (targetNits - x1) / h12;
+ targetNits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) *
+ (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ double t = (targetNits - x2) / h23;
+ targetNits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) *
+ (1.0 - t) +
+ (metadata.displayMaxLuminance * (3.0 - 2.0 * t) +
+ h23 * m3 * (t - 1.0)) *
+ t * t;
+ }
+ }
+ break;
+ }
+ break;
+ default:
+ // source is SDR
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG: {
+ // Map from SDR onto an HDR output buffer
+ // Here we use a polynomial curve to map from [0, displayMaxLuminance] onto
+ // [0, maxOutLumi] which is hard-coded to be 3000 nits.
+ const double maxOutLumi = 3000.0;
+
+ double x0 = 5.0;
+ double y0 = 2.5;
+ double x1 = metadata.displayMaxLuminance * 0.7;
+ double y1 = maxOutLumi * 0.15;
+ double x2 = metadata.displayMaxLuminance * 0.9;
+ double y2 = maxOutLumi * 0.45;
+ double x3 = metadata.displayMaxLuminance;
+ double y3 = maxOutLumi;
+
+ double c1 = y1 / 3.0;
+ double c2 = y2 / 2.0;
+ double c3 = y3 / 1.5;
+
+ targetNits = xyz.y;
+
+ if (targetNits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ double slope = y0 / x0;
+ targetNits *= slope;
+ } else if (targetNits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ double t = (targetNits - x0) / (x1 - x0);
+ targetNits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 +
+ t * t * y1;
+ } else if (targetNits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ double t = (targetNits - x1) / (x2 - x1);
+ targetNits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 +
+ t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ double t = (targetNits - x2) / (x3 - x2);
+ targetNits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 +
+ t * t * y3;
+ }
+ } break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ targetNits = xyz.y;
+ break;
+ }
+ }
+
+ return targetNits / xyz.y;
+ }
};
class ToneMapper13 : public ToneMapper {
+private:
+ double OETF_ST2084(double nits) {
+ nits = nits / 10000.0;
+ double m1 = (2610.0 / 4096.0) / 4.0;
+ double m2 = (2523.0 / 4096.0) * 128.0;
+ double c1 = (3424.0 / 4096.0);
+ double c2 = (2413.0 / 4096.0) * 32.0;
+ double c3 = (2392.0 / 4096.0) * 32.0;
+
+ double tmp = std::pow(nits, m1);
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return std::pow(tmp, m2);
+ }
+
+ double OETF_HLG(double nits) {
+ nits = nits / 1000.0;
+ const double a = 0.17883277;
+ const double b = 0.28466892;
+ const double c = 0.55991073;
+ return nits <= 1.0 / 12.0 ? std::sqrt(3.0 * nits) : a * std::log(12.0 * nits - b) + c;
+ }
+
public:
std::string generateTonemapGainShaderSkSL(
aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
@@ -386,6 +541,108 @@
.value = buildUniformValue<float>(kContentMaxLuminance)});
return uniforms;
}
+
+ double lookupTonemapGain(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace,
+ vec3 linearRGB, vec3 /* xyz */, const Metadata& metadata) override {
+ double maxRGB = std::max({linearRGB.r, linearRGB.g, linearRGB.b});
+
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ double targetNits = 0.0;
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ targetNits = maxRGB;
+ break;
+ case kTransferHLG:
+ // PQ has a wider luminance range (10,000 nits vs. 1,000 nits) than HLG, so
+ // we'll clamp the luminance range in case we're mapping from PQ input to
+ // HLG output.
+ targetNits = std::clamp(maxRGB, 0.0, 1000.0);
+ break;
+ default:
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+
+ double maxInLumi = 4000;
+ double maxOutLumi = metadata.displayMaxLuminance;
+
+ targetNits = maxRGB;
+
+ double x1 = maxOutLumi * 0.65;
+ double y1 = x1;
+
+ double x3 = maxInLumi;
+ double y3 = maxOutLumi;
+
+ double x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ double y2 = maxOutLumi * 0.9;
+
+ double greyNorm1 = 0.0;
+ double greyNorm2 = 0.0;
+ double greyNorm3 = 0.0;
+
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNorm1 = OETF_ST2084(x1);
+ greyNorm2 = OETF_ST2084(x2);
+ greyNorm3 = OETF_ST2084(x3);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNorm1 = OETF_HLG(x1);
+ greyNorm2 = OETF_HLG(x2);
+ greyNorm3 = OETF_HLG(x3);
+ }
+
+ double slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ double slope3 = (y3 - y2) / (greyNorm3 - greyNorm2);
+
+ if (targetNits < x1) {
+ break;
+ }
+
+ if (targetNits > maxInLumi) {
+ targetNits = maxOutLumi;
+ break;
+ }
+
+ double greyNits = 0.0;
+ if ((sourceDataspaceInt & kTransferMask) == kTransferST2084) {
+ greyNits = OETF_ST2084(targetNits);
+ } else if ((sourceDataspaceInt & kTransferMask) == kTransferHLG) {
+ greyNits = OETF_HLG(targetNits);
+ }
+
+ if (greyNits <= greyNorm2) {
+ targetNits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ targetNits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ targetNits = maxOutLumi;
+ }
+ break;
+ }
+ break;
+ default:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ default:
+ targetNits = maxRGB;
+ break;
+ }
+ break;
+ }
+
+ return targetNits / maxRGB;
+ }
};
} // namespace
@@ -406,5 +663,4 @@
return sToneMapper.get();
}
-
} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index e88fdd5..d1925cb 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -39,25 +39,6 @@
return 0;
}
-uint32_t bitsPerPixel(PixelFormat format) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_FP16:
- return 64;
- case PIXEL_FORMAT_RGBA_8888:
- case PIXEL_FORMAT_RGBX_8888:
- case PIXEL_FORMAT_BGRA_8888:
- case PIXEL_FORMAT_RGBA_1010102:
- return 32;
- case PIXEL_FORMAT_RGB_888:
- return 24;
- case PIXEL_FORMAT_RGB_565:
- case PIXEL_FORMAT_RGBA_5551:
- case PIXEL_FORMAT_RGBA_4444:
- return 16;
- }
- return 0;
-}
-
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index 02773d9..91ff39e 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -67,7 +67,6 @@
typedef int32_t PixelFormat;
uint32_t bytesPerPixel(PixelFormat format);
-uint32_t bitsPerPixel(PixelFormat format);
}; // namespace android
diff --git a/libs/ui/include_types/ui/GraphicTypes.h b/libs/ui/include_types/ui/GraphicTypes.h
new file mode 120000
index 0000000..b1859e0
--- /dev/null
+++ b/libs/ui/include_types/ui/GraphicTypes.h
@@ -0,0 +1 @@
+../../include/ui/GraphicTypes.h
\ No newline at end of file
diff --git a/services/gpuservice/gpumem/GpuMem.cpp b/services/gpuservice/gpumem/GpuMem.cpp
index 3aa862f..dd3cc3b 100644
--- a/services/gpuservice/gpumem/GpuMem.cpp
+++ b/services/gpuservice/gpumem/GpuMem.cpp
@@ -22,7 +22,7 @@
#include <android-base/stringprintf.h>
#include <libbpf.h>
-#include <libbpf_android.h>
+#include <bpf/WaitForProgsLoaded.h>
#include <log/log.h>
#include <unistd.h>
#include <utils/Timers.h>
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 29d8a0f..19cad7b 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -68,7 +68,8 @@
}
static bool isTouchEvent(const NotifyMotionArgs& args) {
- return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN;
+ return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) ||
+ isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN);
}
// --- ClassifierEvent ---
diff --git a/services/inputflinger/dispatcher/Connection.cpp b/services/inputflinger/dispatcher/Connection.cpp
index cee9c39..b4497fd 100644
--- a/services/inputflinger/dispatcher/Connection.cpp
+++ b/services/inputflinger/dispatcher/Connection.cpp
@@ -22,7 +22,7 @@
Connection::Connection(const std::shared_ptr<InputChannel>& inputChannel, bool monitor,
const IdGenerator& idGenerator)
- : status(STATUS_NORMAL),
+ : status(Status::NORMAL),
inputChannel(inputChannel),
monitor(monitor),
inputPublisher(inputChannel),
@@ -40,19 +40,6 @@
return "?";
}
-const char* Connection::getStatusLabel() const {
- switch (status) {
- case STATUS_NORMAL:
- return "NORMAL";
- case STATUS_BROKEN:
- return "BROKEN";
- case STATUS_ZOMBIE:
- return "ZOMBIE";
- default:
- return "UNKNOWN";
- }
-}
-
std::deque<DispatchEntry*>::iterator Connection::findWaitQueueEntry(uint32_t seq) {
for (std::deque<DispatchEntry*>::iterator it = waitQueue.begin(); it != waitQueue.end(); it++) {
if ((*it)->seq == seq) {
diff --git a/services/inputflinger/dispatcher/Connection.h b/services/inputflinger/dispatcher/Connection.h
index ba60283..dc6a081 100644
--- a/services/inputflinger/dispatcher/Connection.h
+++ b/services/inputflinger/dispatcher/Connection.h
@@ -33,13 +33,16 @@
virtual ~Connection();
public:
- enum Status {
+ enum class Status {
// Everything is peachy.
- STATUS_NORMAL,
+ NORMAL,
// An unrecoverable communication error has occurred.
- STATUS_BROKEN,
+ BROKEN,
// The input channel has been unregistered.
- STATUS_ZOMBIE
+ ZOMBIE,
+
+ ftl_first = NORMAL,
+ ftl_last = ZOMBIE,
};
Status status;
@@ -66,7 +69,6 @@
inline const std::string getInputChannelName() const { return inputChannel->getName(); }
const std::string getWindowName() const;
- const char* getStatusLabel() const;
std::deque<DispatchEntry*>::iterator findWaitQueueEntry(uint32_t seq);
};
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index c03581d..f6bb6a6 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -175,13 +175,13 @@
if (!GetBoolProperty("ro.debuggable", false)) {
return "KeyEvent";
}
- return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64
- ", source=0x%08x, displayId=%" PRId32 ", action=%s, "
+ return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32
+ ", action=%s, "
"flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, "
"repeatCount=%d), policyFlags=0x%08x",
- deviceId, eventTime, source, displayId, KeyEvent::actionToString(action),
- flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState,
- repeatCount, policyFlags);
+ deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
+ KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode),
+ keyCode, scanCode, metaState, repeatCount, policyFlags);
}
void KeyEntry::recycle() {
@@ -249,12 +249,12 @@
}
std::string msg;
msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64
- ", source=0x%08x, displayId=%" PRId32
+ ", source=%s, displayId=%" PRId32
", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, "
"buttonState=0x%08x, "
"classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, "
"xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[",
- deviceId, eventTime, source, displayId,
+ deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId,
MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState,
buttonState, motionClassificationToString(classification), edgeFlags,
xPrecision, yPrecision, xCursorPosition, yCursorPosition);
@@ -289,9 +289,10 @@
std::string SensorEntry::getDescription() const {
std::string msg;
- msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, "
+ msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, "
"accuracy=0x%08x, hwTimestamp=%" PRId64,
- deviceId, source, sensorType, accuracy, hwTimestamp);
+ deviceId, inputEventSourceToString(source).c_str(),
+ ftl::enum_string(sensorType).c_str(), accuracy, hwTimestamp);
if (!GetBoolProperty("ro.debuggable", false)) {
for (size_t i = 0; i < values.size(); i++) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 5c56e84..c9397c3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -506,10 +506,6 @@
return true;
}
-bool isFromSource(uint32_t source, uint32_t test) {
- return (source & test) == test;
-}
-
vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) {
const vec2 transformedXy = transform.transform(x, y);
const vec2 transformedOrigin = transform.transform(0, 0);
@@ -1753,7 +1749,7 @@
// pile up.
ALOGW("Canceling events for %s because it is unresponsive",
connection->inputChannel->getName().c_str());
- if (connection->status == Connection::STATUS_NORMAL) {
+ if (connection->status == Connection::Status::NORMAL) {
CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS,
"application not responding");
synthesizeCancelationEventsForConnectionLocked(connection, options);
@@ -2107,7 +2103,7 @@
const std::vector<Monitor> newGestureMonitors = isDown
? selectResponsiveMonitorsLocked(
getValueByKey(mGestureMonitorsByDisplay, displayId))
- : std::vector<Monitor>{};
+ : tempTouchState.gestureMonitors;
if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
ALOGI("Dropping event because there is no touchable window or gesture monitor at "
@@ -2143,9 +2139,14 @@
pointerIds.markBit(pointerId);
}
tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+ } else if (tempTouchState.windows.empty()) {
+ // If no window is touched, set split to true. This will allow the next pointer down to
+ // be delivered to a new window which supports split touch.
+ tempTouchState.split = true;
}
-
- tempTouchState.addGestureMonitors(newGestureMonitors);
+ if (isDown) {
+ tempTouchState.addGestureMonitors(newGestureMonitors);
+ }
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -2829,10 +2830,11 @@
// Skip this event if the connection status is not normal.
// We don't want to enqueue additional outbound events if the connection is broken.
- if (connection->status != Connection::STATUS_NORMAL) {
+ if (connection->status != Connection::Status::NORMAL) {
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("channel '%s' ~ Dropping event because the channel status is %s",
- connection->getInputChannelName().c_str(), connection->getStatusLabel());
+ connection->getInputChannelName().c_str(),
+ ftl::enum_string(connection->status).c_str());
}
return;
}
@@ -3146,7 +3148,7 @@
ALOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName().c_str());
}
- while (connection->status == Connection::STATUS_NORMAL && !connection->outboundQueue.empty()) {
+ while (connection->status == Connection::Status::NORMAL && !connection->outboundQueue.empty()) {
DispatchEntry* dispatchEntry = connection->outboundQueue.front();
dispatchEntry->deliveryTime = currentTime;
const std::chrono::nanoseconds timeout =
@@ -3367,8 +3369,8 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
}
- if (connection->status == Connection::STATUS_BROKEN ||
- connection->status == Connection::STATUS_ZOMBIE) {
+ if (connection->status == Connection::Status::BROKEN ||
+ connection->status == Connection::Status::ZOMBIE) {
return;
}
@@ -3395,8 +3397,8 @@
// The connection appears to be unrecoverably broken.
// Ignore already broken or zombie connections.
- if (connection->status == Connection::STATUS_NORMAL) {
- connection->status = Connection::STATUS_BROKEN;
+ if (connection->status == Connection::Status::NORMAL) {
+ connection->status = Connection::Status::BROKEN;
if (notify) {
// Notify other system components.
@@ -3404,7 +3406,6 @@
connection->getInputChannelName().c_str());
auto command = [this, connection]() REQUIRES(mLock) {
- if (connection->status == Connection::STATUS_ZOMBIE) return;
scoped_unlock unlock(mLock);
mPolicy->notifyInputChannelBroken(connection->inputChannel->getConnectionToken());
};
@@ -3540,7 +3541,7 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const sp<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3613,7 +3614,7 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const sp<Connection>& connection) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -4693,8 +4694,10 @@
if (wallpaper != nullptr) {
sp<Connection> wallpaperConnection =
getConnectionLocked(wallpaper->getToken());
- synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
- options);
+ if (wallpaperConnection != nullptr) {
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+ options);
+ }
}
}
}
@@ -5290,7 +5293,8 @@
"status=%s, monitor=%s, responsive=%s\n",
connection->inputChannel->getFd().get(),
connection->getInputChannelName().c_str(),
- connection->getWindowName().c_str(), connection->getStatusLabel(),
+ connection->getWindowName().c_str(),
+ ftl::enum_string(connection->status).c_str(),
toString(connection->monitor), toString(connection->responsive));
if (!connection->outboundQueue.empty()) {
@@ -5463,7 +5467,7 @@
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
- connection->status = Connection::STATUS_ZOMBIE;
+ connection->status = Connection::Status::ZOMBIE;
return OK;
}
@@ -5547,6 +5551,7 @@
canceledWindows.c_str());
// Then clear the current touch state so we stop dispatching to them as well.
+ state.split = false;
state.filterNonMonitors();
}
return OK;
@@ -5682,7 +5687,7 @@
}
}
traceWaitQueueLength(*connection);
- if (restartEvent && connection->status == Connection::STATUS_NORMAL) {
+ if (restartEvent && connection->status == Connection::Status::NORMAL) {
connection->outboundQueue.push_front(dispatchEntry);
traceOutboundQueueLength(*connection);
} else {
@@ -5980,7 +5985,7 @@
mLock.lock();
- if (connection->status != Connection::STATUS_NORMAL) {
+ if (connection->status != Connection::Status::NORMAL) {
connection->inputState.removeFallbackKey(originalKeyCode);
return false;
}
diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h
index b106949..db4228d 100644
--- a/services/inputflinger/include/PointerControllerInterface.h
+++ b/services/inputflinger/include/PointerControllerInterface.h
@@ -30,7 +30,8 @@
* fingers
*
* The pointer controller is responsible for providing synchronization and for tracking
- * display orientation changes if needed.
+ * display orientation changes if needed. It works in the display panel's coordinate space, which
+ * is the same coordinate space used by InputReader.
*/
class PointerControllerInterface {
protected:
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index 15ba459..fcb56ef 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -188,8 +188,6 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mOrientation = DISPLAY_ORIENTATION_0;
- mDisplayWidth = 0;
- mDisplayHeight = 0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
@@ -203,8 +201,6 @@
config->getDisplayViewportByType(ViewportType::INTERNAL);
if (internalViewport) {
mOrientation = getInverseRotation(internalViewport->orientation);
- mDisplayWidth = internalViewport->deviceWidth;
- mDisplayHeight = internalViewport->deviceHeight;
}
}
@@ -335,14 +331,7 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (moved) {
- float dx = deltaX;
- float dy = deltaY;
- // Rotate the delta from InputReader's un-rotated coordinate space to
- // PointerController's rotated coordinate space that is oriented with the
- // viewport.
- rotateDelta(getInverseRotation(mOrientation), &dx, &dy);
-
- mPointerController->move(dx, dy);
+ mPointerController->move(deltaX, deltaY);
}
if (buttonsChanged) {
@@ -353,10 +342,6 @@
}
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
- // Rotate the cursor position that is in PointerController's rotated coordinate space
- // to InputReader's un-rotated coordinate space.
- rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/,
- mDisplayWidth, mDisplayHeight);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h
index 88e947f..9a8ca01 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.h
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.h
@@ -105,8 +105,6 @@
VelocityControl mWheelYVelocityControl;
int32_t mOrientation;
- int32_t mDisplayWidth;
- int32_t mDisplayHeight;
std::shared_ptr<PointerControllerInterface> mPointerController;
diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
index 8c30e38..31a3d2e 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -64,26 +64,6 @@
}
}
-// Rotates the given point (x, y) by the supplied orientation. The width and height are the
-// dimensions of the surface prior to this rotation being applied.
-static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) {
- rotateDelta(orientation, &x, &y);
- switch (orientation) {
- case DISPLAY_ORIENTATION_90:
- y += width;
- break;
- case DISPLAY_ORIENTATION_180:
- x += width;
- y += height;
- break;
- case DISPLAY_ORIENTATION_270:
- x += height;
- break;
- default:
- break;
- }
-}
-
// Returns true if the pointer should be reported as being down given the specified
// button states. This determines whether the event is reported as a touch event.
static bool isPointerDown(int32_t buttonState) {
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 3fe6fd1..913c666 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -1668,9 +1668,10 @@
mPointerController->fade(PointerControllerInterface::Transition::GRADUAL);
mPointerController->setButtonState(mCurrentRawState.buttonState);
- setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
- mCurrentCookedState.cookedPointerData.idToIndex,
- mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId);
+ mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords,
+ mCurrentCookedState.cookedPointerData.idToIndex,
+ mCurrentCookedState.cookedPointerData.touchingIdBits,
+ mViewport.displayId);
}
bool TouchInputMapper::isTouchScreen() {
@@ -2410,9 +2411,10 @@
}
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
- setTouchSpots(mPointerGesture.currentGestureCoords,
- mPointerGesture.currentGestureIdToIndex,
- mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId());
+ mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+ mPointerGesture.currentGestureIdToIndex,
+ mPointerGesture.currentGestureIdBits,
+ mPointerController->getDisplayId());
}
} else {
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
@@ -2562,7 +2564,8 @@
// the pointer is hovering again even if the user is not currently touching
// the touch pad. This ensures that a view will receive a fresh hover enter
// event after a tap.
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
PointerProperties pointerProperties;
pointerProperties.clear();
@@ -2819,12 +2822,13 @@
// Move the pointer using a relative motion.
// When using spots, the click will occur at the position of the anchor
// spot and all other spots will move there.
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG;
mPointerGesture.currentGestureIdBits.clear();
@@ -2850,7 +2854,8 @@
mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) &&
lastFingerCount == 1) {
if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) {
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
#if DEBUG_GESTURES
@@ -2918,7 +2923,8 @@
mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER;
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop &&
fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2952,7 +2958,7 @@
// Move the pointer using a relative motion.
// When using spots, the hover or drag will occur at the position of the anchor spot.
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -2974,7 +2980,8 @@
down = false;
}
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3047,9 +3054,8 @@
mCurrentRawState.rawPointerData
.getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX,
&mPointerGesture.referenceTouchY);
- auto [x, y] = getMouseCursorPosition();
- mPointerGesture.referenceGestureX = x;
- mPointerGesture.referenceGestureY = y;
+ mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+ &mPointerGesture.referenceGestureY);
}
// Clear the reference deltas for fingers not yet included in the reference calculation.
@@ -3387,13 +3393,15 @@
if (!mCurrentCookedState.stylusIdBits.isEmpty()) {
uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit();
uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id];
- setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
- mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
+ mPointerController
+ ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(),
+ mCurrentCookedState.cookedPointerData.pointerCoords[index].getY());
hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id);
down = !hovering;
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[index]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3434,7 +3442,7 @@
rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -3442,7 +3450,8 @@
down = isPointerDown(mCurrentRawState.buttonState);
hovering = !down;
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerSimple.currentCoords.copyFrom(
mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]);
mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
@@ -3482,7 +3491,8 @@
}
int32_t displayId = mPointerController->getDisplayId();
- auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3648,9 +3658,7 @@
float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
if (mDeviceMode == DeviceMode::POINTER) {
- auto [x, y] = getMouseCursorPosition();
- xCursorPosition = x;
- yCursorPosition = y;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
}
const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE);
const int32_t deviceId = getDeviceId();
@@ -3999,56 +4007,4 @@
return std::nullopt;
}
-void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
- // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate
- // space that is oriented with the viewport.
- rotateDelta(mViewport.orientation, &dx, &dy);
-
- mPointerController->move(dx, dy);
-}
-
-std::pair<float, float> TouchInputMapper::getMouseCursorPosition() const {
- float x = 0;
- float y = 0;
- mPointerController->getPosition(&x, &y);
-
- if (!mViewport.isValid()) return {x, y};
-
- // Convert from PointerController's rotated coordinate space that is oriented with the viewport
- // to InputReader's un-rotated coordinate space.
- const int32_t orientation = getInverseRotation(mViewport.orientation);
- rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight);
- return {x, y};
-}
-
-void TouchInputMapper::setMouseCursorPosition(float x, float y) const {
- // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
- // coordinate space that is oriented with the viewport.
- rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight);
-
- mPointerController->setPosition(x, y);
-}
-
-void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId) {
- std::array<PointerCoords, MAX_POINTERS> outSpotCoords{};
-
- for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) {
- const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()];
- float x = spotCoords[index].getX();
- float y = spotCoords[index].getY();
- float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE);
-
- // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
- // coordinate space.
- rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight);
-
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x);
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
- outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure);
- }
-
- mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId);
-}
-
} // namespace android
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h
index 496491b..9b020a6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -803,14 +803,6 @@
const char* modeToString(DeviceMode deviceMode);
void rotateAndScale(float& x, float& y) const;
-
- // Wrapper methods for interfacing with PointerController. These are used to convert points
- // between the coordinate spaces used by InputReader and PointerController, if they differ.
- void moveMouseCursor(float dx, float dy) const;
- std::pair<float, float> getMouseCursorPosition() const;
- void setMouseCursorPosition(float x, float y) const;
- void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
- BitSet32 spotIdBits, int32_t displayId);
};
} // namespace android
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 515a01e..39c5262 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -219,21 +219,21 @@
template <class T>
T getAnrTokenLockedInterruptible(std::chrono::nanoseconds timeout, std::queue<T>& storage,
std::unique_lock<std::mutex>& lock) REQUIRES(mLock) {
- const std::chrono::time_point start = std::chrono::steady_clock::now();
- std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
-
// If there is an ANR, Dispatcher won't be idle because there are still events
// in the waitQueue that we need to check on. So we can't wait for dispatcher to be idle
// before checking if ANR was called.
// Since dispatcher is not guaranteed to call notifyNoFocusedWindowAnr right away, we need
// to provide it some time to act. 100ms seems reasonable.
- mNotifyAnr.wait_for(lock, timeToWait,
- [&storage]() REQUIRES(mLock) { return !storage.empty(); });
- const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
- if (storage.empty()) {
+ std::chrono::duration timeToWait = timeout + 100ms; // provide some slack
+ const std::chrono::time_point start = std::chrono::steady_clock::now();
+ std::optional<T> token =
+ getItemFromStorageLockedInterruptible(timeToWait, storage, lock, mNotifyAnr);
+ if (!token.has_value()) {
ADD_FAILURE() << "Did not receive the ANR callback";
return {};
}
+
+ const std::chrono::duration waited = std::chrono::steady_clock::now() - start;
// Ensure that the ANR didn't get raised too early. We can't be too strict here because
// the dispatcher started counting before this function was called
if (std::chrono::abs(timeout - waited) > 100ms) {
@@ -243,9 +243,24 @@
<< std::chrono::duration_cast<std::chrono::milliseconds>(waited).count()
<< "ms instead";
}
- T token = storage.front();
+ return *token;
+ }
+
+ template <class T>
+ std::optional<T> getItemFromStorageLockedInterruptible(std::chrono::nanoseconds timeout,
+ std::queue<T>& storage,
+ std::unique_lock<std::mutex>& lock,
+ std::condition_variable& condition)
+ REQUIRES(mLock) {
+ condition.wait_for(lock, timeout,
+ [&storage]() REQUIRES(mLock) { return !storage.empty(); });
+ if (storage.empty()) {
+ ADD_FAILURE() << "Did not receive the expected callback";
+ return std::nullopt;
+ }
+ T item = storage.front();
storage.pop();
- return token;
+ return std::make_optional(item);
}
void assertNotifyAnrWasNotCalled() {
@@ -303,6 +318,16 @@
mNotifyDropWindowWasCalled = false;
}
+ void assertNotifyInputChannelBrokenWasCalled(const sp<IBinder>& token) {
+ std::unique_lock lock(mLock);
+ base::ScopedLockAssertion assumeLocked(mLock);
+ std::optional<sp<IBinder>> receivedToken =
+ getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock,
+ mNotifyInputChannelBroken);
+ ASSERT_TRUE(receivedToken.has_value());
+ ASSERT_EQ(token, *receivedToken);
+ }
+
private:
std::mutex mLock;
std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock);
@@ -321,6 +346,8 @@
std::queue<int32_t> mAnrMonitorPids GUARDED_BY(mLock);
std::queue<int32_t> mResponsiveMonitorPids GUARDED_BY(mLock);
std::condition_variable mNotifyAnr;
+ std::queue<sp<IBinder>> mBrokenInputChannels GUARDED_BY(mLock);
+ std::condition_variable mNotifyInputChannelBroken;
sp<IBinder> mDropTargetWindowToken GUARDED_BY(mLock);
bool mNotifyDropWindowWasCalled GUARDED_BY(mLock) = false;
@@ -361,7 +388,11 @@
mNotifyAnr.notify_all();
}
- void notifyInputChannelBroken(const sp<IBinder>&) override {}
+ void notifyInputChannelBroken(const sp<IBinder>& connectionToken) override {
+ std::scoped_lock lock(mLock);
+ mBrokenInputChannels.push(connectionToken);
+ mNotifyInputChannelBroken.notify_all();
+ }
void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
@@ -1175,6 +1206,8 @@
mInfo.ownerUid = ownerUid;
}
+ void destroyReceiver() { mInputReceiver = nullptr; }
+
private:
const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1438,6 +1471,23 @@
return NotifyPointerCaptureChangedArgs(/* id */ 0, systemTime(SYSTEM_TIME_MONOTONIC), request);
}
+/**
+ * When a window unexpectedly disposes of its input channel, policy should be notified about the
+ * broken channel.
+ */
+TEST_F(InputDispatcherTest, WhenInputChannelBreaks_PolicyIsNotified) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Window that breaks its input channel",
+ ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // Window closes its channel, but the window remains.
+ window->destroyReceiver();
+ mFakePolicy->assertNotifyInputChannelBrokenWasCalled(window->getInfo()->token);
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window =
@@ -1578,6 +1628,53 @@
}
/**
+ * Same test as WhenForegroundWindowDisappears_WallpaperTouchIsCanceled above,
+ * with the following differences:
+ * After ACTION_DOWN, Wallpaper window hangs up its channel, which forces the dispatcher to
+ * clean up the connection.
+ * This later may crash dispatcher during ACTION_CANCEL synthesis, if the dispatcher is not careful.
+ * Ensure that there's no crash in the dispatcher.
+ */
+TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> foregroundWindow =
+ new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT);
+ foregroundWindow->setHasWallpaper(true);
+ sp<FakeWindowHandle> wallpaperWindow =
+ new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT);
+ wallpaperWindow->setType(WindowInfo::Type::WALLPAPER);
+ constexpr int expectedWallpaperFlags =
+ AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}});
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Both foreground window and its wallpaper should receive the touch down
+ foregroundWindow->consumeMotionDown();
+ wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ foregroundWindow->consumeMotionMove();
+ wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags);
+
+ // Wallpaper closes its channel, but the window remains.
+ wallpaperWindow->destroyReceiver();
+ mFakePolicy->assertNotifyInputChannelBrokenWasCalled(wallpaperWindow->getInfo()->token);
+
+ // Now the foreground window goes away, but the wallpaper stays, even though its channel
+ // is no longer valid.
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}});
+ foregroundWindow->consumeMotionCancel();
+}
+
+/**
* A single window that receives touch (on top), and a wallpaper window underneath it.
* The top window gets a multitouch gesture.
* Ensure that wallpaper gets the same gesture.
@@ -2669,6 +2766,13 @@
expectedDisplayId, expectedFlags);
}
+ void consumeMotionPointerDown(int32_t pointerIdx) {
+ int32_t action = AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, action, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+ }
+
MotionEvent* consumeMotion() {
InputEvent* event = mInputReceiver->consume();
if (!event) {
@@ -2877,6 +2981,91 @@
0 /*expectedFlags*/);
}
+TEST_F(InputDispatcherTest, GestureMonitor_SplitIfNoWindowTouched) {
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // First finger down, no window touched.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+
+ // Second finger down on window, the window should receive touch down.
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(200))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionPointerDown(1 /* pointerIndex */);
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_NoSplitAfterPilfer) {
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ // Create a non touch modal window that supports split touch
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFrame(Rect(0, 0, 100, 100));
+ window->setFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
+
+ // First finger down, no window touched.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {100, 200}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ window->assertNoEvents();
+
+ // Gesture monitor pilfer the pointers.
+ mDispatcher->pilferPointers(monitor.getToken());
+
+ // Second finger down on window, the window should not receive touch down.
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER)
+ .x(100)
+ .y(200))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ window->assertNoEvents();
+ monitor.consumeMotionPointerDown(1 /* pointerIndex */);
+}
+
/**
* Dispatcher has touch mode enabled by default. Typically, the policy overrides that value to
* the device default right away. In the test scenario, we check both the default value,
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1be5a96..b596708 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -78,6 +78,11 @@
"libsensorprivacy",
"libpermission",
],
+
+ pgo: {
+ sampling: true,
+ profile_file: "sensorservice/libsensorservice.profdata",
+ },
}
cc_binary {
diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h
new file mode 100644
index 0000000..c9e089e
--- /dev/null
+++ b/services/sensorservice/ISensorHalWrapper.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_ISENSOR_HAL_WRAPPER_H
+#define ANDROID_ISENSOR_HAL_WRAPPER_H
+
+#include <hardware/sensors.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "SensorService.h"
+
+namespace android {
+
+/**
+ * A wrapper for various types of HAL implementation, e.g. to distinguish HIDL and AIDL versions.
+ */
+class ISensorHalWrapper {
+public:
+ class ICallback : public ISensorsCallback {
+
+ void onDynamicSensorsConnected(
+ const std::vector<sensor_t> &dynamicSensorsAdded) = 0;
+
+ void onDynamicSensorsDisconnected(
+ const std::vector<int32_t> &dynamicSensorHandlesRemoved) = 0;
+ };
+
+ /**
+ * Connects to the underlying sensors HAL. This should also be used for any reconnections
+ * due to HAL resets.
+ */
+ virtual bool connect(ICallback *callback) = 0;
+
+ /**
+ * Polls for available sensor events. This could be using the traditional sensors
+ * polling or from a FMQ.
+ */
+ virtual ssize_t poll(sensors_event_t* buffer, size_t count) = 0;
+
+ /**
+ * The below functions directly mirrors the sensors HAL definitions.
+ */
+ virtual std::vector<sensor_t> getSensorsList() = 0;
+
+ virtual status_t setOperationMode(SensorService::Mode mode) = 0;
+
+ virtual status_t activate(int32_t sensorHandle, bool enabled) = 0;
+
+ virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) = 0;
+
+ virtual status_t flush(int32_t sensorHandle) = 0;
+
+ virtual status_t injectSensorData(const sensors_event_t *event) = 0;
+
+ virtual status_t registerDirectChannel(const sensors_direct_mem_t *memory,
+ int32_t *channelHandle) = 0;
+
+ virtual void unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t *config) = 0;
+}
+
+} // namespace android
+
+#endif // ANDROID_ISENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index b059e61..9b6d01a 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -89,6 +89,52 @@
UID_STATE_IDLE,
};
+ enum Mode {
+ // The regular operating mode where any application can register/unregister/call flush on
+ // sensors.
+ NORMAL = 0,
+ // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
+ // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
+ // injected from the SensorService as if it were the real sensor data. This mode is primarily
+ // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
+ // Step Detector etc. Typically in this mode, there will be a client (a
+ // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
+ // unregister and register for any sensor that supports injection. Registering to sensors
+ // that do not support injection will give an error. TODO: Allow exactly one
+ // client to inject sensor data at a time.
+ DATA_INJECTION = 1,
+ // This mode is used only for testing sensors. Each sensor can be tested in isolation with
+ // the required sampling_rate and maxReportLatency parameters without having to think about
+ // the data rates requested by other applications. End user devices are always expected to be
+ // in NORMAL mode. When this mode is first activated, all active sensors from all connections
+ // are disabled. Calling flush() will return an error. In this mode, only the requests from
+ // selected apps whose package names are allowlisted are allowed (typically CTS apps). Only
+ // these apps can register/unregister/call flush() on sensors. If SensorService switches to
+ // NORMAL mode again, all sensors that were previously registered to are activated with the
+ // corresponding parameters if the application hasn't unregistered for sensors in the mean
+ // time. NOTE: Non allowlisted app whose sensors were previously deactivated may still
+ // receive events if a allowlisted app requests data from the same sensor.
+ RESTRICTED = 2
+
+ // State Transitions supported.
+ // RESTRICTED <--- NORMAL ---> DATA_INJECTION
+ // ---> <---
+
+ // Shell commands to switch modes in SensorService.
+ // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
+ // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
+ //
+ // $ adb shell dumpsys sensorservice restrict .cts.
+ //
+ // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
+ // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
+ //
+ // $ adb shell dumpsys sensorservice data_injection .xts.
+ //
+ // 3) Reset sensorservice back to NORMAL mode.
+ // $ adb shell dumpsys sensorservice enable
+ };
+
class ProximityActiveListener : public virtual RefBase {
public:
// Note that the callback is invoked from an async thread and can interact with the
@@ -276,52 +322,6 @@
const int64_t mToken;
};
- enum Mode {
- // The regular operating mode where any application can register/unregister/call flush on
- // sensors.
- NORMAL = 0,
- // This mode is only used for testing purposes. Not all HALs support this mode. In this mode,
- // the HAL ignores the sensor data provided by physical sensors and accepts the data that is
- // injected from the SensorService as if it were the real sensor data. This mode is primarily
- // used for testing various algorithms like vendor provided SensorFusion, Step Counter and
- // Step Detector etc. Typically in this mode, there will be a client (a
- // SensorEventConnection) which will be injecting sensor data into the HAL. Normal apps can
- // unregister and register for any sensor that supports injection. Registering to sensors
- // that do not support injection will give an error. TODO(aakella) : Allow exactly one
- // client to inject sensor data at a time.
- DATA_INJECTION = 1,
- // This mode is used only for testing sensors. Each sensor can be tested in isolation with
- // the required sampling_rate and maxReportLatency parameters without having to think about
- // the data rates requested by other applications. End user devices are always expected to be
- // in NORMAL mode. When this mode is first activated, all active sensors from all connections
- // are disabled. Calling flush() will return an error. In this mode, only the requests from
- // selected apps whose package names are whitelisted are allowed (typically CTS apps). Only
- // these apps can register/unregister/call flush() on sensors. If SensorService switches to
- // NORMAL mode again, all sensors that were previously registered to are activated with the
- // corresponding paramaters if the application hasn't unregistered for sensors in the mean
- // time. NOTE: Non whitelisted app whose sensors were previously deactivated may still
- // receive events if a whitelisted app requests data from the same sensor.
- RESTRICTED = 2
-
- // State Transitions supported.
- // RESTRICTED <--- NORMAL ---> DATA_INJECTION
- // ---> <---
-
- // Shell commands to switch modes in SensorService.
- // 1) Put SensorService in RESTRICTED mode with packageName .cts. If it is already in
- // restricted mode it is treated as a NO_OP (and packageName is NOT changed).
- //
- // $ adb shell dumpsys sensorservice restrict .cts.
- //
- // 2) Put SensorService in DATA_INJECTION mode with packageName .xts. If it is already in
- // data_injection mode it is treated as a NO_OP (and packageName is NOT changed).
- //
- // $ adb shell dumpsys sensorservice data_injection .xts.
- //
- // 3) Reset sensorservice back to NORMAL mode.
- // $ adb shell dumpsys sensorservice enable
- };
-
static const char* WAKE_LOCK_NAME;
virtual ~SensorService();
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index fb42cc0..5560ed7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -78,10 +78,11 @@
"libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
+ "libscheduler",
"libserviceutils",
+ "libshaders",
"libtonemap",
"libtrace_proto",
- "libaidlcommonsupport",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -144,6 +145,7 @@
filegroup {
name: "libsurfaceflinger_sources",
srcs: [
+ "BackgroundExecutor.cpp",
"BufferLayer.cpp",
"BufferLayerConsumer.cpp",
"BufferQueueLayer.cpp",
@@ -194,14 +196,16 @@
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
- "Scheduler/VsyncModulator.cpp",
"Scheduler/VSyncReactor.cpp",
"Scheduler/VsyncConfiguration.cpp",
+ "Scheduler/VsyncModulator.cpp",
+ "Scheduler/VsyncSchedule.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
"SurfaceInterceptor.cpp",
"Tracing/LayerTracing.cpp",
+ "Tracing/TransactionTracing.cpp",
"Tracing/TransactionProtoParser.cpp",
"TransactionCallbackInvoker.cpp",
"TunnelModeEnabledReporter.cpp",
diff --git a/services/surfaceflinger/BackgroundExecutor.cpp b/services/surfaceflinger/BackgroundExecutor.cpp
new file mode 100644
index 0000000..3663cdb
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "BackgroundExecutor"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "BackgroundExecutor.h"
+
+namespace android {
+
+ANDROID_SINGLETON_STATIC_INSTANCE(BackgroundExecutor);
+
+BackgroundExecutor::BackgroundExecutor() : Singleton<BackgroundExecutor>() {
+ mThread = std::thread([&]() {
+ bool done = false;
+ while (!done) {
+ std::vector<std::function<void()>> tasks;
+ {
+ std::unique_lock lock(mMutex);
+ mWorkAvailableCv.wait(lock, [&]() { return mDone || !mTasks.empty(); });
+ tasks = std::move(mTasks);
+ mTasks.clear();
+ done = mDone;
+ } // unlock mMutex
+
+ for (auto& task : tasks) {
+ task();
+ }
+ }
+ });
+}
+
+BackgroundExecutor::~BackgroundExecutor() {
+ {
+ std::unique_lock lock(mMutex);
+ mDone = true;
+ mWorkAvailableCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void BackgroundExecutor::execute(std::function<void()> task) {
+ std::unique_lock lock(mMutex);
+ mTasks.emplace_back(std::move(task));
+ mWorkAvailableCv.notify_all();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/BackgroundExecutor.h b/services/surfaceflinger/BackgroundExecutor.h
new file mode 100644
index 0000000..6f6d305
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/Singleton.h>
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace android {
+
+// Executes tasks off the main thread.
+class BackgroundExecutor : public Singleton<BackgroundExecutor> {
+public:
+ BackgroundExecutor();
+ ~BackgroundExecutor();
+ void execute(std::function<void()>);
+
+private:
+ std::mutex mMutex;
+ std::condition_variable mWorkAvailableCv;
+ std::thread mThread;
+ bool mDone = false;
+ std::vector<std::function<void()>> mTasks;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 861d496..494b61c 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -401,12 +401,13 @@
const Fps refreshRate = display->refreshRateConfigs().getCurrentRefreshRate().getFps();
const std::optional<Fps> renderRate =
mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
+
+ const auto vote = frameRateToSetFrameRateVotePayload(mDrawingState.frameRate);
+ const auto gameMode = getGameMode();
+
if (presentFence->isValid()) {
mFlinger->mTimeStats->setPresentFence(layerId, mCurrentFrameNumber, presentFence,
- refreshRate, renderRate,
- frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate),
- getGameMode());
+ refreshRate, renderRate, vote, gameMode);
mFlinger->mFrameTracer->traceFence(layerId, getCurrentBufferId(), mCurrentFrameNumber,
presentFence,
FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -417,10 +418,7 @@
// timestamp instead.
const nsecs_t actualPresentTime = display->getRefreshTimestamp();
mFlinger->mTimeStats->setPresentTime(layerId, mCurrentFrameNumber, actualPresentTime,
- refreshRate, renderRate,
- frameRateToSetFrameRateVotePayload(
- mDrawingState.frameRate),
- getGameMode());
+ refreshRate, renderRate, vote, gameMode);
mFlinger->mFrameTracer->traceTimestamp(layerId, getCurrentBufferId(),
mCurrentFrameNumber, actualPresentTime,
FrameTracer::FrameEvent::PRESENT_FENCE);
@@ -798,7 +796,7 @@
wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
WindowInfo tmpInputInfo = mDrawingState.inputInfo;
- mDrawingState = clonedFrom->mDrawingState;
+ cloneDrawingState(clonedFrom.get());
mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index dec7cc0..926aa1d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -372,8 +372,9 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- LayerHistory::LayerUpdateType::Buffer);
+
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b4ccb80..2fac880 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -61,7 +61,7 @@
// one of the layers, in this case the original layer, needs to handle the deletion. The
// original layer and the clone should be removed at the same time so there shouldn't be any
// issue with the clone layer trying to use the texture.
- if (mBufferInfo.mBuffer != nullptr && !isClone()) {
+ if (mBufferInfo.mBuffer != nullptr) {
callReleaseBufferCallback(mDrawingState.releaseBufferListener,
mBufferInfo.mBuffer->getBuffer(), mBufferInfo.mFrameNumber,
mBufferInfo.mFence,
@@ -476,8 +476,9 @@
return static_cast<nsecs_t>(0);
}();
- mFlinger->mScheduler->recordLayerHistory(this, presentTime,
- LayerHistory::LayerUpdateType::Buffer);
+
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime, LayerUpdateType::Buffer);
addFrameEvent(mDrawingState.acquireFence, postTime, isAutoTimestamp ? 0 : desiredPresentTime);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 4502eee..c553fce 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -16,6 +16,8 @@
#pragma once
+#include <cinttypes>
+
#include <ui/Size.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
@@ -47,13 +49,8 @@
// before composition takes place. The DisplaySurface can use the
// composition type to decide how to manage the flow of buffers between
// GPU and HWC for this frame.
- enum CompositionType {
- COMPOSITION_UNKNOWN = 0,
- COMPOSITION_GPU = 1,
- COMPOSITION_HWC = 2,
- COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
- };
- virtual status_t prepareFrame(CompositionType compositionType) = 0;
+ enum class CompositionType : uint8_t { Unknown = 0, Gpu = 0b1, Hwc = 0b10, Mixed = Gpu | Hwc };
+ virtual status_t prepareFrame(CompositionType) = 0;
// Inform the surface that GPU composition is complete for this frame, and
// the surface should make sure that HWComposer has the correct buffer for
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index ef50870..a19d23f 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -127,19 +127,18 @@
}
void RenderSurface::prepareFrame(bool usesClientComposition, bool usesDeviceComposition) {
- DisplaySurface::CompositionType compositionType;
- if (usesClientComposition && usesDeviceComposition) {
- compositionType = DisplaySurface::COMPOSITION_MIXED;
- } else if (usesClientComposition) {
- compositionType = DisplaySurface::COMPOSITION_GPU;
- } else if (usesDeviceComposition) {
- compositionType = DisplaySurface::COMPOSITION_HWC;
- } else {
+ const auto compositionType = [=] {
+ using CompositionType = DisplaySurface::CompositionType;
+
+ if (usesClientComposition && usesDeviceComposition) return CompositionType::Mixed;
+ if (usesClientComposition) return CompositionType::Gpu;
+ if (usesDeviceComposition) return CompositionType::Hwc;
+
// Nothing to do -- when turning the screen off we get a frame like
// this. Call it a HWC frame since we won't be doing any GPU work but
// will do a prepare/set cycle.
- compositionType = DisplaySurface::COMPOSITION_HWC;
- }
+ return CompositionType::Hwc;
+ }();
if (status_t result = mDisplaySurface->prepareFrame(compositionType); result != NO_ERROR) {
ALOGE("updateCompositionType failed for %s: %d (%s)", mDisplay.getName().c_str(), result,
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index 431cc93..7c8e41b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -201,28 +201,28 @@
*/
TEST_F(RenderSurfaceTest, prepareFrameHandlesMixedComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_MIXED))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Mixed))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, true);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Gpu))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, false);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyHwcComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(false, true);
}
TEST_F(RenderSurfaceTest, prepareFrameHandlesNoComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_HWC))
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::CompositionType::Hwc))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(false, false);
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index fd09ae4..76bbe2c 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -483,7 +483,7 @@
std::scoped_lock lock(mActiveModeLock);
if (mDesiredActiveModeChanged) {
// If a mode change is pending, just cache the latest request in mDesiredActiveMode
- const Scheduler::ModeEvent prevConfig = mDesiredActiveMode.event;
+ const auto prevConfig = mDesiredActiveMode.event;
mDesiredActiveMode = info;
mDesiredActiveMode.event = mDesiredActiveMode.event | prevConfig;
return false;
@@ -508,7 +508,7 @@
void DisplayDevice::clearDesiredActiveModeState() {
std::scoped_lock lock(mActiveModeLock);
- mDesiredActiveMode.event = Scheduler::ModeEvent::None;
+ mDesiredActiveMode.event = scheduler::DisplayModeEvent::None;
mDesiredActiveModeChanged = false;
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 4b9718f..324145e 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -190,7 +190,7 @@
struct ActiveModeInfo {
DisplayModePtr mode;
- scheduler::RefreshRateConfigEvent event = scheduler::RefreshRateConfigEvent::None;
+ scheduler::DisplayModeEvent event = scheduler::DisplayModeEvent::None;
bool operator!=(const ActiveModeInfo& other) const {
return mode != other.mode || event != other.event;
diff --git a/services/surfaceflinger/DisplayHardware/DisplayMode.h b/services/surfaceflinger/DisplayHardware/DisplayMode.h
index 5de622b..0ab9605 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayMode.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayMode.h
@@ -16,9 +16,9 @@
#pragma once
-#include "DisplayHardware/Hal.h"
-#include "Fps.h"
-#include "Scheduler/StrongTyping.h"
+#include <cstddef>
+#include <memory>
+#include <vector>
#include <android-base/stringprintf.h>
#include <android/configuration.h>
@@ -27,9 +27,10 @@
#include <ui/Size.h>
#include <utils/Timers.h>
-#include <cstddef>
-#include <memory>
-#include <vector>
+#include <scheduler/Fps.h>
+
+#include "DisplayHardware/Hal.h"
+#include "Scheduler/StrongTyping.h"
namespace android {
@@ -161,4 +162,4 @@
mode.getDpiY(), mode.getGroup());
}
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 82a9ae2..b4fb51f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -21,20 +21,18 @@
// #define LOG_NDEBUG 0
#include "VirtualDisplaySurface.h"
-#include <inttypes.h>
+#include <cinttypes>
#include "HWComposer.h"
#include "SurfaceFlinger.h"
+#include <ftl/Flags.h>
+#include <ftl/enum.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueue.h>
#include <gui/IProducerListener.h>
#include <system/window.h>
-// ---------------------------------------------------------------------------
-namespace android {
-// ---------------------------------------------------------------------------
-
#define VDS_LOGE(msg, ...) ALOGE("[%s] " msg, \
mDisplayName.c_str(), ##__VA_ARGS__)
#define VDS_LOGW_IF(cond, msg, ...) ALOGW_IF(cond, "[%s] " msg, \
@@ -42,20 +40,11 @@
#define VDS_LOGV(msg, ...) ALOGV("[%s] " msg, \
mDisplayName.c_str(), ##__VA_ARGS__)
-static const char* dbgCompositionTypeStr(compositionengine::DisplaySurface::CompositionType type) {
- switch (type) {
- case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
- return "UNKNOWN";
- case compositionengine::DisplaySurface::COMPOSITION_GPU:
- return "GPU";
- case compositionengine::DisplaySurface::COMPOSITION_HWC:
- return "HWC";
- case compositionengine::DisplaySurface::COMPOSITION_MIXED:
- return "MIXED";
- default:
- return "<INVALID>";
- }
-}
+#define UNSUPPORTED() \
+ VDS_LOGE("%s: Invalid operation on virtual display", __func__); \
+ return INVALID_OPERATION
+
+namespace android {
VirtualDisplaySurface::VirtualDisplaySurface(HWComposer& hwc, VirtualDisplayId displayId,
const sp<IGraphicBufferProducer>& sink,
@@ -76,14 +65,10 @@
mQueueBufferOutput(),
mSinkBufferWidth(0),
mSinkBufferHeight(0),
- mCompositionType(COMPOSITION_UNKNOWN),
mFbFence(Fence::NO_FENCE),
mOutputFence(Fence::NO_FENCE),
mFbProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
mOutputProducerSlot(BufferQueue::INVALID_BUFFER_SLOT),
- mDbgState(DBG_STATE_IDLE),
- mDbgLastCompositionType(COMPOSITION_UNKNOWN),
- mMustRecompose(false),
mForceHwcCopy(SurfaceFlinger::useHwcForRgbToYuv) {
mSource[SOURCE_SINK] = sink;
mSource[SOURCE_SCRATCH] = bqProducer;
@@ -131,9 +116,9 @@
mMustRecompose = mustRecompose;
- VDS_LOGW_IF(mDbgState != DBG_STATE_IDLE,
- "Unexpected beginFrame() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_BEGUN;
+ VDS_LOGW_IF(mDebugState != DebugState::Idle, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Begun;
return refreshOutputBuffer();
}
@@ -143,12 +128,12 @@
return NO_ERROR;
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_BEGUN,
- "Unexpected prepareFrame() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_PREPARED;
+ VDS_LOGW_IF(mDebugState != DebugState::Begun, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Prepared;
mCompositionType = compositionType;
- if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
+ if (mForceHwcCopy && mCompositionType == CompositionType::Gpu) {
// Some hardware can do RGB->YUV conversion more efficiently in hardware
// controlled by HWC than in hardware controlled by the video encoder.
// Forcing GPU-composed frames to go through an extra copy by the HWC
@@ -157,16 +142,16 @@
//
// On the other hand, when the consumer prefers RGB or can consume RGB
// inexpensively, this forces an unnecessary copy.
- mCompositionType = COMPOSITION_MIXED;
+ mCompositionType = CompositionType::Mixed;
}
- if (mCompositionType != mDbgLastCompositionType) {
- VDS_LOGV("prepareFrame: composition type changed to %s",
- dbgCompositionTypeStr(mCompositionType));
- mDbgLastCompositionType = mCompositionType;
+ if (mCompositionType != mDebugLastCompositionType) {
+ VDS_LOGV("%s: composition type changed to %s", __func__,
+ toString(mCompositionType).c_str());
+ mDebugLastCompositionType = mCompositionType;
}
- if (mCompositionType != COMPOSITION_GPU &&
+ if (mCompositionType != CompositionType::Gpu &&
(mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
// We must have just switched from GPU-only to MIXED or HWC
// composition. Stop using the format and usage requested by the GPU
@@ -191,33 +176,32 @@
return NO_ERROR;
}
- if (mCompositionType == COMPOSITION_HWC) {
- VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
- "Unexpected advanceFrame() in %s state on HWC frame",
- dbgStateStr());
+ if (mCompositionType == CompositionType::Hwc) {
+ VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state on HWC frame",
+ __func__, ftl::enum_string(mDebugState).c_str());
} else {
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
- "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
+ VDS_LOGW_IF(mDebugState != DebugState::GpuDone,
+ "Unexpected %s in %s state on GPU/MIXED frame", __func__,
+ ftl::enum_string(mDebugState).c_str());
}
- mDbgState = DBG_STATE_HWC;
+ mDebugState = DebugState::Hwc;
if (mOutputProducerSlot < 0 ||
- (mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
+ (mCompositionType != CompositionType::Hwc && mFbProducerSlot < 0)) {
// Last chance bailout if something bad happened earlier. For example,
// in a graphics API configuration, if the sink disappears then dequeueBuffer
// will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
- VDS_LOGE("advanceFrame: no buffer, bailing out");
+ VDS_LOGE("%s: no buffer, bailing out", __func__);
return NO_MEMORY;
}
sp<GraphicBuffer> fbBuffer = mFbProducerSlot >= 0 ?
mProducerBuffers[mFbProducerSlot] : sp<GraphicBuffer>(nullptr);
sp<GraphicBuffer> outBuffer = mProducerBuffers[mOutputProducerSlot];
- VDS_LOGV("advanceFrame: fb=%d(%p) out=%d(%p)",
- mFbProducerSlot, fbBuffer.get(),
- mOutputProducerSlot, outBuffer.get());
+ VDS_LOGV("%s: fb=%d(%p) out=%d(%p)", __func__, mFbProducerSlot, fbBuffer.get(),
+ mOutputProducerSlot, outBuffer.get());
const auto halDisplayId = HalVirtualDisplayId::tryCast(mDisplayId);
LOG_FATAL_IF(!halDisplayId);
@@ -245,16 +229,16 @@
return;
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_HWC,
- "Unexpected onFrameCommitted() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_IDLE;
+ VDS_LOGW_IF(mDebugState != DebugState::Hwc, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Idle;
sp<Fence> retireFence = mHwc.getPresentFence(*halDisplayId);
- if (mCompositionType == COMPOSITION_MIXED && mFbProducerSlot >= 0) {
+ if (mCompositionType == CompositionType::Mixed && mFbProducerSlot >= 0) {
// release the scratch buffer back to the pool
Mutex::Autolock lock(mMutex);
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, mFbProducerSlot);
- VDS_LOGV("onFrameCommitted: release scratch sslot=%d", sslot);
+ VDS_LOGV("%s: release scratch sslot=%d", __func__, sslot);
addReleaseFenceLocked(sslot, mProducerBuffers[mFbProducerSlot],
retireFence);
releaseBufferLocked(sslot, mProducerBuffers[mFbProducerSlot]);
@@ -263,7 +247,7 @@
if (mOutputProducerSlot >= 0) {
int sslot = mapProducer2SourceSlot(SOURCE_SINK, mOutputProducerSlot);
QueueBufferOutput qbo;
- VDS_LOGV("onFrameCommitted: queue sink sslot=%d", sslot);
+ VDS_LOGV("%s: queue sink sslot=%d", __func__, sslot);
if (mMustRecompose) {
status_t result = mSource[SOURCE_SINK]->queueBuffer(sslot,
QueueBufferInput(
@@ -308,8 +292,8 @@
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
- dbgStateStr());
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s pslot=%d in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
*outBuf = mProducerBuffers[pslot];
return NO_ERROR;
@@ -334,8 +318,8 @@
if (result < 0)
return result;
int pslot = mapSource2ProducerSlot(source, *sslot);
- VDS_LOGV("dequeueBuffer(%s): sslot=%d pslot=%d result=%d",
- dbgSourceStr(source), *sslot, pslot, result);
+ VDS_LOGV("%s(%s): sslot=%d pslot=%d result=%d", __func__, ftl::enum_string(source).c_str(),
+ *sslot, pslot, result);
uint64_t sourceBit = static_cast<uint64_t>(source) << pslot;
// reset producer slot reallocation flag
@@ -363,10 +347,9 @@
mSource[source]->cancelBuffer(*sslot, *fence);
return result;
}
- VDS_LOGV("dequeueBuffer(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64,
- dbgSourceStr(source), pslot, mProducerBuffers[pslot].get(),
- mProducerBuffers[pslot]->getPixelFormat(),
- mProducerBuffers[pslot]->getUsage());
+ VDS_LOGV("%s(%s): buffers[%d]=%p fmt=%d usage=%#" PRIx64, __func__,
+ ftl::enum_string(source).c_str(), pslot, mProducerBuffers[pslot].get(),
+ mProducerBuffers[pslot]->getPixelFormat(), mProducerBuffers[pslot]->getUsage());
// propagate reallocation to VDS consumer
mProducerSlotNeedReallocation |= 1ULL << pslot;
@@ -384,11 +367,11 @@
outTimestamps);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
- "Unexpected dequeueBuffer() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_GPU;
+ VDS_LOGW_IF(mDebugState != DebugState::Prepared, "Unexpected %s in %s state", __func__,
+ ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::Gpu;
- VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
+ VDS_LOGV("%s %dx%d fmt=%d usage=%#" PRIx64, __func__, w, h, format, usage);
status_t result = NO_ERROR;
Source source = fbSourceForCompositionType(mCompositionType);
@@ -401,7 +384,7 @@
// will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
- VDS_LOGE("dequeueBuffer: no buffer, bailing out");
+ VDS_LOGE("%s: no buffer, bailing out", __func__);
return NO_MEMORY;
}
@@ -417,12 +400,11 @@
(format != 0 && format != buf->getPixelFormat()) ||
(w != 0 && w != mSinkBufferWidth) ||
(h != 0 && h != mSinkBufferHeight)) {
- VDS_LOGV("dequeueBuffer: dequeueing new output buffer: "
- "want %dx%d fmt=%d use=%#" PRIx64 ", "
- "have %dx%d fmt=%d use=%#" PRIx64,
- w, h, format, usage,
- mSinkBufferWidth, mSinkBufferHeight,
- buf->getPixelFormat(), buf->getUsage());
+ VDS_LOGV("%s: dequeueing new output buffer: "
+ "want %dx%d fmt=%d use=%#" PRIx64 ", "
+ "have %dx%d fmt=%d use=%#" PRIx64,
+ __func__, w, h, format, usage, mSinkBufferWidth, mSinkBufferHeight,
+ buf->getPixelFormat(), buf->getUsage());
mOutputFormat = format;
mOutputUsage = usage;
result = refreshOutputBuffer();
@@ -452,21 +434,16 @@
return result;
}
-status_t VirtualDisplaySurface::detachBuffer(int /* slot */) {
- VDS_LOGE("detachBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachBuffer(int) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::detachNextBuffer(
- sp<GraphicBuffer>* /* outBuffer */, sp<Fence>* /* outFence */) {
- VDS_LOGE("detachNextBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::detachNextBuffer(sp<GraphicBuffer>*, sp<Fence>*) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::attachBuffer(int* /* outSlot */,
- const sp<GraphicBuffer>& /* buffer */) {
- VDS_LOGE("attachBuffer is not available for VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::attachBuffer(int*, const sp<GraphicBuffer>&) {
+ UNSUPPORTED();
}
status_t VirtualDisplaySurface::queueBuffer(int pslot,
@@ -475,14 +452,14 @@
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- mDbgState = DBG_STATE_GPU_DONE;
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
+ mDebugState = DebugState::GpuDone;
- VDS_LOGV("queueBuffer pslot=%d", pslot);
+ VDS_LOGV("%s pslot=%d", __func__, pslot);
status_t result;
- if (mCompositionType == COMPOSITION_MIXED) {
+ if (mCompositionType == CompositionType::Mixed) {
// Queue the buffer back into the scratch pool
QueueBufferOutput scratchQBO;
int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot);
@@ -498,15 +475,15 @@
if (result != NO_ERROR)
return result;
VDS_LOGW_IF(item.mSlot != sslot,
- "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d",
- item.mSlot, sslot);
+ "%s: acquired sslot %d from SCRATCH after queueing sslot %d", __func__,
+ item.mSlot, sslot);
mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mSlot);
mFbFence = mSlots[item.mSlot].mFence;
} else {
- LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
- "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
- dbgCompositionTypeStr(mCompositionType));
+ LOG_FATAL_IF(mCompositionType != CompositionType::Gpu,
+ "Unexpected %s in state %s for composition type %s", __func__,
+ ftl::enum_string(mDebugState).c_str(), toString(mCompositionType).c_str());
// Extract the GPU release fence for HWC to acquire
int64_t timestamp;
@@ -533,9 +510,9 @@
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- VDS_LOGV("cancelBuffer pslot=%d", pslot);
+ VDS_LOGW_IF(mDebugState != DebugState::Gpu, "Unexpected %s(pslot=%d) in %s state", __func__,
+ pslot, ftl::enum_string(mDebugState).c_str());
+ VDS_LOGV("%s pslot=%d", __func__, pslot);
Source source = fbSourceForCompositionType(mCompositionType);
return mSource[source]->cancelBuffer(
mapProducer2SourceSlot(source, pslot), fence);
@@ -573,8 +550,8 @@
return mSource[SOURCE_SINK]->disconnect(api, mode);
}
-status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>& /*stream*/) {
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSidebandStream(const sp<NativeHandle>&) {
+ UNSUPPORTED();
}
void VirtualDisplaySurface::allocateBuffers(uint32_t /* width */,
@@ -586,40 +563,32 @@
return INVALID_OPERATION;
}
-status_t VirtualDisplaySurface::setGenerationNumber(uint32_t /* generation */) {
- ALOGE("setGenerationNumber not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setGenerationNumber(uint32_t) {
+ UNSUPPORTED();
}
String8 VirtualDisplaySurface::getConsumerName() const {
return String8("VirtualDisplaySurface");
}
-status_t VirtualDisplaySurface::setSharedBufferMode(bool /*sharedBufferMode*/) {
- ALOGE("setSharedBufferMode not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setSharedBufferMode(bool) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::setAutoRefresh(bool /*autoRefresh*/) {
- ALOGE("setAutoRefresh not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setAutoRefresh(bool) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t /* timeout */) {
- ALOGE("setDequeueTimeout not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::setDequeueTimeout(nsecs_t) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::getLastQueuedBuffer(
- sp<GraphicBuffer>* /*outBuffer*/, sp<Fence>* /*outFence*/,
- float[16] /* outTransformMatrix*/) {
- ALOGE("getLastQueuedBuffer not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getLastQueuedBuffer(sp<GraphicBuffer>*, sp<Fence>*, float[16]) {
+ UNSUPPORTED();
}
-status_t VirtualDisplaySurface::getUniqueId(uint64_t* /*outId*/) const {
- ALOGE("getUniqueId not supported on VirtualDisplaySurface");
- return INVALID_OPERATION;
+status_t VirtualDisplaySurface::getUniqueId(uint64_t*) const {
+ UNSUPPORTED();
}
status_t VirtualDisplaySurface::getConsumerUsage(uint64_t* outUsage) const {
@@ -633,7 +602,7 @@
}
void VirtualDisplaySurface::resetPerFrameState() {
- mCompositionType = COMPOSITION_UNKNOWN;
+ mCompositionType = CompositionType::Unknown;
mFbFence = Fence::NO_FENCE;
mOutputFence = Fence::NO_FENCE;
mOutputProducerSlot = -1;
@@ -682,39 +651,16 @@
return mapSource2ProducerSlot(source, pslot);
}
-VirtualDisplaySurface::Source
-VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) {
- return type == COMPOSITION_MIXED ? SOURCE_SCRATCH : SOURCE_SINK;
+auto VirtualDisplaySurface::fbSourceForCompositionType(CompositionType type) -> Source {
+ return type == CompositionType::Mixed ? SOURCE_SCRATCH : SOURCE_SINK;
}
-const char* VirtualDisplaySurface::dbgStateStr() const {
- switch (mDbgState) {
- case DBG_STATE_IDLE:
- return "IDLE";
- case DBG_STATE_PREPARED:
- return "PREPARED";
- case DBG_STATE_GPU:
- return "GPU";
- case DBG_STATE_GPU_DONE:
- return "GPU_DONE";
- case DBG_STATE_HWC:
- return "HWC";
- default:
- return "INVALID";
- }
+std::string VirtualDisplaySurface::toString(CompositionType type) {
+ using namespace std::literals;
+ return type == CompositionType::Unknown ? "Unknown"s : Flags(type).string();
}
-const char* VirtualDisplaySurface::dbgSourceStr(Source s) {
- switch (s) {
- case SOURCE_SINK: return "SINK";
- case SOURCE_SCRATCH: return "SCRATCH";
- default: return "INVALID";
- }
-}
-
-// ---------------------------------------------------------------------------
} // namespace android
-// ---------------------------------------------------------------------------
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index bbb6306..7720713 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
-#define ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
+#pragma once
#include <optional>
#include <string>
@@ -28,9 +27,7 @@
#include "DisplayIdentification.h"
-// ---------------------------------------------------------------------------
namespace android {
-// ---------------------------------------------------------------------------
class HWComposer;
class IProducerListener;
@@ -94,7 +91,13 @@
virtual const sp<Fence>& getClientTargetAcquireFence() const override;
private:
- enum Source {SOURCE_SINK = 0, SOURCE_SCRATCH = 1};
+ enum Source : size_t {
+ SOURCE_SINK = 0,
+ SOURCE_SCRATCH = 1,
+
+ ftl_first = SOURCE_SINK,
+ ftl_last = SOURCE_SCRATCH,
+ };
virtual ~VirtualDisplaySurface();
@@ -133,6 +136,8 @@
// Utility methods
//
static Source fbSourceForCompositionType(CompositionType);
+ static std::string toString(CompositionType);
+
status_t dequeueBuffer(Source, PixelFormat, uint64_t usage, int* sslot, sp<Fence>*);
void updateQueueBufferOutput(QueueBufferOutput&&);
void resetPerFrameState();
@@ -197,7 +202,7 @@
// Composition type and graphics buffer source for the current frame.
// Valid after prepareFrame(), cleared in onFrameCommitted.
- CompositionType mCompositionType;
+ CompositionType mCompositionType = CompositionType::Unknown;
// mFbFence is the fence HWC should wait for before reading the framebuffer
// target buffer.
@@ -219,47 +224,42 @@
// +-----------+-------------------+-------------+
// | State | Event || Next State |
// +-----------+-------------------+-------------+
- // | IDLE | beginFrame || BEGUN |
- // | BEGUN | prepareFrame || PREPARED |
- // | PREPARED | dequeueBuffer [1] || GPU |
- // | PREPARED | advanceFrame [2] || HWC |
- // | GPU | queueBuffer || GPU_DONE |
- // | GPU_DONE | advanceFrame || HWC |
- // | HWC | onFrameCommitted || IDLE |
+ // | Idle | beginFrame || Begun |
+ // | Begun | prepareFrame || Prepared |
+ // | Prepared | dequeueBuffer [1] || Gpu |
+ // | Prepared | advanceFrame [2] || Hwc |
+ // | Gpu | queueBuffer || GpuDone |
+ // | GpuDone | advanceFrame || Hwc |
+ // | Hwc | onFrameCommitted || Idle |
// +-----------+-------------------++------------+
- // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
- // [2] COMPOSITION_HWC frames.
+ // [1] CompositionType::Gpu and CompositionType::Mixed frames.
+ // [2] CompositionType::Hwc frames.
//
- enum DbgState {
+ enum class DebugState {
// no buffer dequeued, don't know anything about the next frame
- DBG_STATE_IDLE,
+ Idle,
// output buffer dequeued, framebuffer source not yet known
- DBG_STATE_BEGUN,
+ Begun,
// output buffer dequeued, framebuffer source known but not provided
// to GPU yet.
- DBG_STATE_PREPARED,
+ Prepared,
// GPU driver has a buffer dequeued
- DBG_STATE_GPU,
+ Gpu,
// GPU driver has queued the buffer, we haven't sent it to HWC yet
- DBG_STATE_GPU_DONE,
+ GpuDone,
// HWC has the buffer for this frame
- DBG_STATE_HWC,
+ Hwc,
+
+ ftl_last = Hwc
};
- DbgState mDbgState;
- CompositionType mDbgLastCompositionType;
+ DebugState mDebugState = DebugState::Idle;
+ CompositionType mDebugLastCompositionType = CompositionType::Unknown;
- const char* dbgStateStr() const;
- static const char* dbgSourceStr(Source s);
-
- bool mMustRecompose;
+ bool mMustRecompose = false;
compositionengine::impl::HwcBufferCache mHwcBufferCache;
bool mForceHwcCopy;
};
-// ---------------------------------------------------------------------------
} // namespace android
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_SF_VIRTUAL_DISPLAY_SURFACE_H
diff --git a/services/surfaceflinger/FrameTimeline/Android.bp b/services/surfaceflinger/FrameTimeline/Android.bp
index 10a5833..2d4ec04 100644
--- a/services/surfaceflinger/FrameTimeline/Android.bp
+++ b/services/surfaceflinger/FrameTimeline/Android.bp
@@ -13,6 +13,9 @@
srcs: [
"FrameTimeline.cpp",
],
+ header_libs: [
+ "libscheduler_headers",
+ ],
shared_libs: [
"android.hardware.graphics.composer@2.4",
"libbase",
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
index c294ff2..0c4e112 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.cpp
@@ -304,7 +304,7 @@
frametimeline::TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats,
JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode)
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode gameMode)
: mToken(frameTimelineInfo.vsyncId),
mInputEventId(frameTimelineInfo.inputEventId),
mOwnerPid(ownerPid),
@@ -778,7 +778,7 @@
std::shared_ptr<SurfaceFrame> FrameTimeline::createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid, int32_t layerId,
- std::string layerName, std::string debugName, bool isBuffer, int32_t gameMode) {
+ std::string layerName, std::string debugName, bool isBuffer, GameMode gameMode) {
ATRACE_CALL();
if (frameTimelineInfo.vsyncId == FrameTimelineInfo::INVALID_VSYNC_ID) {
return std::make_shared<SurfaceFrame>(frameTimelineInfo, ownerPid, ownerUid, layerId,
diff --git a/services/surfaceflinger/FrameTimeline/FrameTimeline.h b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
index 139f91f..36d6290 100644
--- a/services/surfaceflinger/FrameTimeline/FrameTimeline.h
+++ b/services/surfaceflinger/FrameTimeline/FrameTimeline.h
@@ -16,10 +16,17 @@
#pragma once
-#include <../Fps.h>
-#include <../TimeStats/TimeStats.h>
+#include <atomic>
+#include <chrono>
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <string>
+
#include <gui/ISurfaceComposer.h>
#include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
#include <perfetto/trace/android/frame_timeline_event.pbzero.h>
#include <perfetto/tracing.h>
#include <ui/FenceTime.h>
@@ -28,8 +35,9 @@
#include <utils/Timers.h>
#include <utils/Vector.h>
-#include <deque>
-#include <mutex>
+#include <scheduler/Fps.h>
+
+#include "../TimeStats/TimeStats.h"
namespace android::frametimeline {
@@ -154,7 +162,7 @@
int32_t layerId, std::string layerName, std::string debugName,
PredictionState predictionState, TimelineItem&& predictions,
std::shared_ptr<TimeStats> timeStats, JankClassificationThresholds thresholds,
- TraceCookieCounter* traceCookieCounter, bool isBuffer, int32_t gameMode);
+ TraceCookieCounter* traceCookieCounter, bool isBuffer, GameMode);
~SurfaceFrame() = default;
// Returns std::nullopt if the frame hasn't been classified yet.
@@ -260,7 +268,7 @@
// buffer(animations)
bool mIsBuffer;
// GameMode from the layer. Used in metrics.
- int32_t mGameMode = 0;
+ GameMode mGameMode = GameMode::Unsupported;
};
/*
@@ -281,7 +289,7 @@
virtual std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) = 0;
+ GameMode) = 0;
// Adds a new SurfaceFrame to the current DisplayFrame. Frames from multiple layers can be
// composited into one display frame.
@@ -441,7 +449,7 @@
std::shared_ptr<SurfaceFrame> createSurfaceFrameForToken(
const FrameTimelineInfo& frameTimelineInfo, pid_t ownerPid, uid_t ownerUid,
int32_t layerId, std::string layerName, std::string debugName, bool isBuffer,
- int32_t gameMode) override;
+ GameMode) override;
void addSurfaceFrame(std::shared_ptr<frametimeline::SurfaceFrame> surfaceFrame) override;
void setSfWakeUp(int64_t token, nsecs_t wakeupTime, Fps refreshRate) override;
void setSfPresent(nsecs_t sfPresentTime, const std::shared_ptr<FenceTime>& presentFence,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 968a49d..dfb99cc 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -36,6 +36,7 @@
#include <cutils/compiler.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
+#include <ftl/enum.h>
#include <gui/BufferItem.h>
#include <gui/LayerDebugInfo.h>
#include <gui/Surface.h>
@@ -1293,8 +1294,8 @@
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
- mFlinger->mScheduler->recordLayerHistory(this, systemTime(),
- LayerHistory::LayerUpdateType::SetFrameRate);
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
+ mFlinger->mScheduler->recordLayerHistory(this, systemTime(), LayerUpdateType::SetFrameRate);
return true;
}
@@ -1411,19 +1412,6 @@
result.append("\n");
}
-std::string Layer::frameRateCompatibilityString(Layer::FrameRateCompatibility compatibility) {
- switch (compatibility) {
- case FrameRateCompatibility::Default:
- return "Default";
- case FrameRateCompatibility::ExactOrMultiple:
- return "ExactOrMultiple";
- case FrameRateCompatibility::NoVote:
- return "NoVote";
- case FrameRateCompatibility::Exact:
- return "Exact";
- }
-}
-
void Layer::miniDump(std::string& result, const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (!outputLayer) {
@@ -1462,8 +1450,8 @@
const auto frameRate = getFrameRateForLayerTree();
if (frameRate.rate.isValid() || frameRate.type != FrameRateCompatibility::Default) {
StringAppendF(&result, "%s %15s %17s", to_string(frameRate.rate).c_str(),
- frameRateCompatibilityString(frameRate.type).c_str(),
- toString(frameRate.seamlessness).c_str());
+ ftl::enum_string(frameRate.type).c_str(),
+ ftl::enum_string(frameRate.seamlessness).c_str());
} else {
result.append(41, ' ');
}
@@ -1546,11 +1534,10 @@
return count;
}
-void Layer::setGameModeForTree(int parentGameMode) {
- int gameMode = parentGameMode;
- auto& currentState = getDrawingState();
+void Layer::setGameModeForTree(GameMode gameMode) {
+ const auto& currentState = getDrawingState();
if (currentState.metadata.has(METADATA_GAME_MODE)) {
- gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0);
+ gameMode = static_cast<GameMode>(currentState.metadata.getInt32(METADATA_GAME_MODE, 0));
}
setGameMode(gameMode);
for (const sp<Layer>& child : mCurrentChildren) {
@@ -1576,7 +1563,7 @@
const auto removeResult = mCurrentChildren.remove(layer);
updateTreeHasFrameRateVote();
- layer->setGameModeForTree(0);
+ layer->setGameModeForTree(GameMode::Unsupported);
layer->updateTreeHasFrameRateVote();
return removeResult;
@@ -2182,6 +2169,8 @@
info.frameRight = 0;
info.frameBottom = 0;
info.transform.reset();
+ info.touchableRegion = Region();
+ info.flags = WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::NOT_FOCUSABLE;
return;
}
@@ -2429,8 +2418,7 @@
}
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
- // copy drawing state from cloned layer
- mDrawingState = clonedFrom->mDrawingState;
+ cloneDrawingState(clonedFrom.get());
mClonedFrom = clonedFrom;
}
@@ -2465,7 +2453,7 @@
// since we may be able to pull out other children that are still alive.
if (isClonedFromAlive()) {
sp<Layer> clonedFrom = getClonedFrom();
- mDrawingState = clonedFrom->mDrawingState;
+ cloneDrawingState(clonedFrom.get());
clonedLayersMap.emplace(clonedFrom, this);
}
@@ -2628,15 +2616,21 @@
return true;
}
+void Layer::cloneDrawingState(const Layer* from) {
+ mDrawingState = from->mDrawingState;
+ // Skip callback info since they are not applicable for cloned layers.
+ mDrawingState.releaseBufferListener = nullptr;
+ mDrawingState.callbackHandles = {};
+}
+
// ---------------------------------------------------------------------------
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate) {
- return stream << "{rate=" << rate.rate
- << " type=" << Layer::frameRateCompatibilityString(rate.type)
- << " seamlessness=" << toString(rate.seamlessness) << "}";
+ return stream << "{rate=" << rate.rate << " type=" << ftl::enum_string(rate.type)
+ << " seamlessness=" << ftl::enum_string(rate.seamlessness) << '}';
}
-}; // namespace android
+} // namespace android
#if defined(__gl_h_)
#error "don't include gl/gl.h in this file"
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index bda1c28..3f4d48b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -18,7 +18,6 @@
#pragma once
#include <android/gui/DropInputMode.h>
-#include <compositionengine/LayerFE.h>
#include <gui/BufferQueue.h>
#include <gui/ISurfaceComposerClient.h>
#include <gui/LayerState.h>
@@ -39,6 +38,10 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <compositionengine/LayerFE.h>
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
#include <chrono>
#include <cstdint>
#include <list>
@@ -49,13 +52,11 @@
#include "ClientCache.h"
#include "DisplayHardware/ComposerHal.h"
#include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "RenderArea.h"
#include "Scheduler/LayerInfo.h"
-#include "Scheduler/Seamlessness.h"
#include "SurfaceFlinger.h"
#include "Tracing/LayerTracing.h"
#include "TransactionCallbackInvoker.h"
@@ -327,7 +328,6 @@
static bool isLayerFocusedBasedOnPriority(int32_t priority);
static void miniDumpHeader(std::string& result);
- static std::string frameRateCompatibilityString(FrameRateCompatibility compatibility);
// Provide unique string for each class type in the Layer hierarchy
virtual const char* getType() const = 0;
@@ -853,12 +853,12 @@
*/
bool hasInputInfo() const;
- // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied
- // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with
- // its children) that has the metadata set will use the gameMode from the metadata.
- void setGameModeForTree(int32_t parentGameMode);
- void setGameMode(int32_t gameMode) { mGameMode = gameMode; };
- int32_t getGameMode() const { return mGameMode; }
+ // Sets the GameMode for the tree rooted at this layer. A layer in the tree inherits this
+ // GameMode unless it (or an ancestor) has GAME_MODE_METADATA.
+ void setGameModeForTree(GameMode);
+
+ void setGameMode(GameMode gameMode) { mGameMode = gameMode; }
+ GameMode getGameMode() const { return mGameMode; }
virtual uid_t getOwnerUid() const { return mOwnerUid; }
@@ -922,6 +922,7 @@
bool isClone() { return mClonedFrom != nullptr; }
bool isClonedFromAlive() { return getClonedFrom() != nullptr; }
+ void cloneDrawingState(const Layer* from);
void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void updateClonedChildren(const sp<Layer>& mirrorRoot,
std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
@@ -1109,9 +1110,8 @@
// shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
float mEffectiveShadowRadius = 0.f;
- // Game mode for the layer. Set by WindowManagerShell, game mode is used in
- // metrics(SurfaceFlingerStats).
- int32_t mGameMode = 0;
+ // Game mode for the layer. Set by WindowManagerShell and recorded by SurfaceFlingerStats.
+ GameMode mGameMode = GameMode::Unsupported;
// A list of regions on this layer that should have blurs.
const std::vector<BlurRegion> getBlurRegions() const;
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 2502d66..81c1566 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -203,12 +203,13 @@
return false;
}
- SurfaceComposerClient::Transaction t;
- t.setFrameRate(mSurfaceControl, 0.0f,
- static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote),
- static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless));
- t.setLayer(mSurfaceControl, INT32_MAX - 2);
- t.apply();
+ SurfaceComposerClient::Transaction()
+ .setFrameRate(mSurfaceControl, 0.0f,
+ static_cast<int8_t>(Layer::FrameRateCompatibility::NoVote),
+ static_cast<int8_t>(scheduler::Seamlessness::OnlySeamless))
+ .setLayer(mSurfaceControl, INT32_MAX - 2)
+ .setTrustedOverlay(mSurfaceControl, true)
+ .apply();
return true;
}
@@ -276,7 +277,7 @@
t.apply();
}
-void RefreshRateOverlay::changeRefreshRate(const Fps& fps) {
+void RefreshRateOverlay::changeRefreshRate(Fps fps) {
mCurrentFps = fps.getIntValue();
auto buffer = getOrCreateBuffers(*mCurrentFps)[mFrame];
SurfaceComposerClient::Transaction t;
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 65d446c..381df37 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -16,6 +16,10 @@
#pragma once
+#include <SkCanvas.h>
+#include <SkColor.h>
+#include <unordered_map>
+
#include <math/vec4.h>
#include <renderengine/RenderEngine.h>
#include <ui/LayerStack.h>
@@ -23,11 +27,7 @@
#include <ui/Size.h>
#include <utils/StrongPointer.h>
-#include <SkCanvas.h>
-#include <SkColor.h>
-#include <unordered_map>
-
-#include "Fps.h"
+#include <scheduler/Fps.h>
namespace android {
@@ -45,7 +45,7 @@
void setLayerStack(ui::LayerStack);
void setViewport(ui::Size);
- void changeRefreshRate(const Fps&);
+ void changeRefreshRate(Fps);
void animate();
private:
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index 32585dd..da8c3e0 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -30,7 +30,6 @@
#include <compositionengine/impl/OutputCompositionState.h>
#include <cutils/properties.h>
#include <ftl/future.h>
-#include <gui/IRegionSamplingListener.h>
#include <gui/SyncScreenCaptureListener.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Trace.h>
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index f715309..686b4b1 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -17,6 +17,7 @@
#pragma once
#include <android-base/thread_annotations.h>
+#include <android/gui/IRegionSamplingListener.h>
#include <binder/IBinder.h>
#include <renderengine/ExternalTexture.h>
#include <ui/GraphicBuffer.h>
@@ -34,12 +35,13 @@
namespace android {
-class IRegionSamplingListener;
class Layer;
class Scheduler;
class SurfaceFlinger;
struct SamplingOffsetCallback;
+using gui::IRegionSamplingListener;
+
float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t stride,
uint32_t orientation, const Rect& area);
diff --git a/services/surfaceflinger/Scheduler/Android.bp b/services/surfaceflinger/Scheduler/Android.bp
new file mode 100644
index 0000000..409d098
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/Android.bp
@@ -0,0 +1,35 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_native_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_defaults {
+ name: "libscheduler_defaults",
+ defaults: ["surfaceflinger_defaults"],
+ cflags: [
+ "-DLOG_TAG=\"Scheduler\"",
+ "-DATRACE_TAG=ATRACE_TAG_GRAPHICS",
+ ],
+ shared_libs: [
+ "libbase",
+ "libutils",
+ ],
+}
+
+cc_library_headers {
+ name: "libscheduler_headers",
+ defaults: ["libscheduler_defaults"],
+ export_include_dirs: ["include"],
+}
+
+cc_library_static {
+ name: "libscheduler",
+ defaults: ["libscheduler_defaults"],
+ srcs: [],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 455289f..627c49a 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -170,20 +170,21 @@
mEventThread->registerDisplayEventConnection(this);
}
-status_t EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
+binder::Status EventThreadConnection::stealReceiveChannel(gui::BitTube* outChannel) {
outChannel->setReceiveFd(mChannel.moveReceiveFd());
outChannel->setSendFd(base::unique_fd(dup(mChannel.getSendFd())));
- return NO_ERROR;
+ return binder::Status::ok();
}
-status_t EventThreadConnection::setVsyncRate(uint32_t rate) {
- mEventThread->setVsyncRate(rate, this);
- return NO_ERROR;
+binder::Status EventThreadConnection::setVsyncRate(int rate) {
+ mEventThread->setVsyncRate(static_cast<uint32_t>(rate), this);
+ return binder::Status::ok();
}
-void EventThreadConnection::requestNextVsync() {
+binder::Status EventThreadConnection::requestNextVsync() {
ATRACE_NAME("requestNextVsync");
mEventThread->requestNextVsync(this);
+ return binder::Status::ok();
}
status_t EventThreadConnection::postEvent(const DisplayEventReceiver::Event& event) {
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index de43570..fa9af09 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -17,8 +17,8 @@
#pragma once
#include <android-base/thread_annotations.h>
+#include <android/gui/BnDisplayEventConnection.h>
#include <gui/DisplayEventReceiver.h>
-#include <gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
#include <sys/types.h>
#include <utils/Errors.h>
@@ -80,7 +80,7 @@
virtual void dump(std::string& result) const = 0;
};
-class EventThreadConnection : public BnDisplayEventConnection {
+class EventThreadConnection : public gui::BnDisplayEventConnection {
public:
EventThreadConnection(EventThread*, uid_t callingUid, ResyncCallback,
ISurfaceComposer::EventRegistrationFlags eventRegistration = {});
@@ -88,9 +88,9 @@
virtual status_t postEvent(const DisplayEventReceiver::Event& event);
- status_t stealReceiveChannel(gui::BitTube* outChannel) override;
- status_t setVsyncRate(uint32_t rate) override;
- void requestNextVsync() override; // asynchronous
+ binder::Status stealReceiveChannel(gui::BitTube* outChannel) override;
+ binder::Status setVsyncRate(int rate) override;
+ binder::Status requestNextVsync() override; // asynchronous
// Called in response to requestNextVsync.
const ResyncCallback resyncCallback;
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 92236f5..8d56951 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -31,11 +31,9 @@
namespace android {
class Layer;
-class TestableScheduler;
namespace scheduler {
-class LayerHistoryTest;
class LayerInfo;
class LayerHistory {
@@ -75,8 +73,8 @@
std::string dump() const;
private:
- friend LayerHistoryTest;
- friend TestableScheduler;
+ friend class LayerHistoryTest;
+ friend class TestableScheduler;
using LayerPair = std::pair<Layer*, std::unique_ptr<LayerInfo>>;
using LayerInfos = std::vector<LayerPair>;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 314526a..ae61eeb 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -28,6 +28,7 @@
#include <cutils/compiler.h>
#include <cutils/trace.h>
+#include <ftl/enum.h>
#undef LOG_TAG
#define LOG_TAG "LayerInfo"
@@ -257,10 +258,10 @@
return {LayerHistory::LayerVoteType::Max, Fps()};
}
-const char* LayerInfo::getTraceTag(android::scheduler::LayerHistory::LayerVoteType type) const {
+const char* LayerInfo::getTraceTag(LayerHistory::LayerVoteType type) const {
if (mTraceTags.count(type) == 0) {
- const auto tag = "LFPS " + mName + " " + RefreshRateConfigs::layerVoteTypeString(type);
- mTraceTags.emplace(type, tag);
+ auto tag = "LFPS " + mName + " " + ftl::enum_string(type);
+ mTraceTags.emplace(type, std::move(tag));
}
return mTraceTags.at(type).c_str();
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index 92abbae..690abda 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -16,15 +16,19 @@
#pragma once
+#include <chrono>
+#include <deque>
+#include <optional>
+#include <string>
+#include <unordered_map>
+
#include <ui/Transform.h>
#include <utils/Timers.h>
-#include <chrono>
-#include <deque>
+#include <scheduler/Seamlessness.h>
#include "LayerHistory.h"
#include "RefreshRateConfigs.h"
-#include "Scheduler/Seamlessness.h"
#include "SchedulerUtils.h"
namespace android {
@@ -78,6 +82,8 @@
NoVote, // Layer doesn't have any requirements for the refresh rate and
// should not be considered when the display refresh rate is determined.
+
+ ftl_last = NoVote
};
// Encapsulates the frame rate and compatibility of the layer. This information will be used
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index dd69d60..9532e26 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.h
+++ b/services/surfaceflinger/Scheduler/MessageQueue.h
@@ -22,7 +22,7 @@
#include <utility>
#include <android-base/thread_annotations.h>
-#include <gui/IDisplayEventConnection.h>
+#include <android/gui/IDisplayEventConnection.h>
#include <private/gui/BitTube.h>
#include <utils/Looper.h>
#include <utils/Timers.h>
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 0d17b0c..71d5631 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -21,23 +21,27 @@
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wextra"
-#include "RefreshRateConfigs.h"
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <utils/Trace.h>
#include <chrono>
#include <cmath>
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <utils/Trace.h>
+
#include "../SurfaceFlingerProperties.h"
+#include "RefreshRateConfigs.h"
#undef LOG_TAG
#define LOG_TAG "RefreshRateConfigs"
namespace android::scheduler {
namespace {
+
std::string formatLayerInfo(const RefreshRateConfigs::LayerRequirement& layer, float weight) {
return base::StringPrintf("%s (type=%s, weight=%.2f seamlessness=%s) %s", layer.name.c_str(),
- RefreshRateConfigs::layerVoteTypeString(layer.vote).c_str(), weight,
- toString(layer.seamlessness).c_str(),
+ ftl::enum_string(layer.vote).c_str(), weight,
+ ftl::enum_string(layer.seamlessness).c_str(),
to_string(layer.desiredRefreshRate).c_str());
}
@@ -74,25 +78,6 @@
mode->getWidth(), mode->getHeight(), getModeGroup());
}
-std::string RefreshRateConfigs::layerVoteTypeString(LayerVoteType vote) {
- switch (vote) {
- case LayerVoteType::NoVote:
- return "NoVote";
- case LayerVoteType::Min:
- return "Min";
- case LayerVoteType::Max:
- return "Max";
- case LayerVoteType::Heuristic:
- return "Heuristic";
- case LayerVoteType::ExplicitDefault:
- return "ExplicitDefault";
- case LayerVoteType::ExplicitExactOrMultiple:
- return "ExplicitExactOrMultiple";
- case LayerVoteType::ExplicitExact:
- return "ExplicitExact";
- }
-}
-
std::string RefreshRateConfigs::Policy::toString() const {
return base::StringPrintf("default mode ID: %d, allowGroupSwitching = %d"
", primary range: %s, app request range: %s",
@@ -405,7 +390,7 @@
for (const auto& layer : layers) {
ALOGV("Calculating score for %s (%s, weight %.2f, desired %.2f) ", layer.name.c_str(),
- layerVoteTypeString(layer.vote).c_str(), layer.weight,
+ ftl::enum_string(layer.vote).c_str(), layer.weight,
layer.desiredRefreshRate.getValue());
if (layer.vote == LayerVoteType::NoVote || layer.vote == LayerVoteType::Min) {
continue;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 0584024..4bbdab6 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -16,31 +16,32 @@
#pragma once
-#include <android-base/stringprintf.h>
-#include <gui/DisplayEventReceiver.h>
-
#include <algorithm>
#include <numeric>
#include <optional>
#include <type_traits>
+#include <android-base/stringprintf.h>
+#include <gui/DisplayEventReceiver.h>
+
+#include <scheduler/Fps.h>
+#include <scheduler/Seamlessness.h>
+
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
-#include "Fps.h"
#include "Scheduler/OneShotTimer.h"
#include "Scheduler/SchedulerUtils.h"
-#include "Scheduler/Seamlessness.h"
#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
using namespace std::chrono_literals;
-enum class RefreshRateConfigEvent : unsigned { None = 0b0, Changed = 0b1 };
+enum class DisplayModeEvent : unsigned { None = 0b0, Changed = 0b1 };
-inline RefreshRateConfigEvent operator|(RefreshRateConfigEvent lhs, RefreshRateConfigEvent rhs) {
- using T = std::underlying_type_t<RefreshRateConfigEvent>;
- return static_cast<RefreshRateConfigEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
+inline DisplayModeEvent operator|(DisplayModeEvent lhs, DisplayModeEvent rhs) {
+ using T = std::underlying_type_t<DisplayModeEvent>;
+ return static_cast<DisplayModeEvent>(static_cast<T>(lhs) | static_cast<T>(rhs));
}
using FrameRateOverride = DisplayEventReceiver::Event::FrameRateOverride;
@@ -205,6 +206,7 @@
ExplicitExact, // Specific refresh rate that was provided by the app with
// Exact compatibility
+ ftl_last = ExplicitExact
};
// Captures the layer requirements for a refresh rate. This will be used to determine the
@@ -284,9 +286,6 @@
// Stores the current modeId the device operates at
void setCurrentModeId(DisplayModeId) EXCLUDES(mLock);
- // Returns a string that represents the layer vote type
- static std::string layerVoteTypeString(LayerVoteType vote);
-
// Returns a known frame rate that is the closest to frameRate
Fps findClosestKnownFrameRate(Fps frameRate) const;
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 80aa96f..23ebb06 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -23,7 +23,8 @@
#include <ftl/small_map.h>
#include <utils/Timers.h>
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
#include "Scheduler/SchedulerUtils.h"
#include "TimeStats/TimeStats.h"
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 4d72798..cbe4552 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -46,11 +46,8 @@
#include "OneShotTimer.h"
#include "SchedulerUtils.h"
#include "SurfaceFlingerProperties.h"
-#include "Timer.h"
-#include "VSyncDispatchTimerQueue.h"
#include "VSyncPredictor.h"
#include "VSyncReactor.h"
-#include "VsyncController.h"
#define RETURN_IF_INVALID_HANDLE(handle, ...) \
do { \
@@ -60,68 +57,14 @@
} \
} while (false)
-using namespace std::string_literals;
+namespace android::scheduler {
-namespace android {
-
-using gui::WindowInfo;
-
-namespace {
-
-std::unique_ptr<scheduler::VSyncTracker> createVSyncTracker() {
- // TODO(b/144707443): Tune constants.
- constexpr int kDefaultRate = 60;
- constexpr auto initialPeriod = std::chrono::duration<nsecs_t, std::ratio<1, kDefaultRate>>(1);
- constexpr nsecs_t idealPeriod =
- std::chrono::duration_cast<std::chrono::nanoseconds>(initialPeriod).count();
- constexpr size_t vsyncTimestampHistorySize = 20;
- constexpr size_t minimumSamplesForPrediction = 6;
- constexpr uint32_t discardOutlierPercent = 20;
- return std::make_unique<scheduler::VSyncPredictor>(idealPeriod, vsyncTimestampHistorySize,
- minimumSamplesForPrediction,
- discardOutlierPercent);
-}
-
-std::unique_ptr<scheduler::VSyncDispatch> createVSyncDispatch(scheduler::VSyncTracker& tracker) {
- // TODO(b/144707443): Tune constants.
- constexpr std::chrono::nanoseconds vsyncMoveThreshold = 3ms;
- constexpr std::chrono::nanoseconds timerSlack = 500us;
- return std::make_unique<
- scheduler::VSyncDispatchTimerQueue>(std::make_unique<scheduler::Timer>(), tracker,
- timerSlack.count(), vsyncMoveThreshold.count());
-}
-
-const char* toContentDetectionString(bool useContentDetection) {
- return useContentDetection ? "on" : "off";
-}
-
-} // namespace
-
-class PredictedVsyncTracer {
-public:
- PredictedVsyncTracer(scheduler::VSyncDispatch& dispatch)
- : mRegistration(dispatch, std::bind(&PredictedVsyncTracer::callback, this),
- "PredictedVsyncTracer") {
- scheduleRegistration();
- }
-
-private:
- TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
- scheduler::VSyncCallbackRegistration mRegistration;
-
- void scheduleRegistration() { mRegistration.schedule({0, 0, 0}); }
-
- void callback() {
- mParity = !mParity;
- scheduleRegistration();
- }
-};
-
-Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, Options options)
- : impl::MessageQueue(compositor), mOptions(options), mSchedulerCallback(callback) {}
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
+ : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
void Scheduler::startTimers() {
using namespace sysprop;
+ using namespace std::string_literals;
if (const int64_t millis = set_touch_timer_ms(0); millis > 0) {
// Touch events are coming to SF every 100ms, so the timer needs to be higher than that
@@ -154,27 +97,14 @@
}
}
-void Scheduler::createVsyncSchedule(bool supportKernelTimer) {
- auto clock = std::make_unique<scheduler::SystemClock>();
- auto tracker = createVSyncTracker();
- auto dispatch = createVSyncDispatch(*tracker);
-
- // TODO(b/144707443): Tune constants.
- constexpr size_t pendingFenceLimit = 20;
- auto controller =
- std::make_unique<scheduler::VSyncReactor>(std::move(clock), *tracker, pendingFenceLimit,
- supportKernelTimer);
- mVsyncSchedule = {std::move(controller), std::move(tracker), std::move(dispatch)};
-
- if (base::GetBoolProperty("debug.sf.show_predicted_vsync", false)) {
- mPredictedVsyncTracer = std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch);
- }
+void Scheduler::createVsyncSchedule(FeatureFlags features) {
+ mVsyncSchedule.emplace(features);
}
std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
const char* name, std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration, bool traceVsync) {
- return std::make_unique<scheduler::DispSyncSource>(*mVsyncSchedule.dispatch, workDuration,
+ return std::make_unique<scheduler::DispSyncSource>(getVsyncDispatch(), workDuration,
readyDuration, traceVsync, name);
}
@@ -210,7 +140,7 @@
return true;
}
- return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+ return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -245,7 +175,7 @@
};
}
-Scheduler::ConnectionHandle Scheduler::createConnection(
+ConnectionHandle Scheduler::createConnection(
const char* connectionName, frametimeline::TokenManager* tokenManager,
std::chrono::nanoseconds workDuration, std::chrono::nanoseconds readyDuration,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
@@ -259,7 +189,7 @@
return createConnection(std::move(eventThread));
}
-Scheduler::ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
+ConnectionHandle Scheduler::createConnection(std::unique_ptr<EventThread> eventThread) {
const ConnectionHandle handle = ConnectionHandle{mNextConnectionHandleId++};
ALOGV("Creating a connection handle with ID %" PRIuPTR, handle.id);
@@ -346,24 +276,24 @@
void Scheduler::onPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
// Cache the last reported modes for primary display.
- mFeatures.cachedModeChangedParams = {handle, mode};
+ mPolicy.cachedModeChangedParams = {handle, mode};
// Invalidate content based refresh rate selection so it could be calculated
// again for the new refresh rate.
- mFeatures.contentRequirements.clear();
+ mPolicy.contentRequirements.clear();
}
onNonPrimaryDisplayModeChanged(handle, mode);
}
void Scheduler::dispatchCachedReportedMode() {
// Check optional fields first.
- if (!mFeatures.mode) {
+ if (!mPolicy.mode) {
ALOGW("No mode ID found, not dispatching cached mode.");
return;
}
- if (!mFeatures.cachedModeChangedParams.has_value()) {
+ if (!mPolicy.cachedModeChangedParams) {
ALOGW("No mode changed params found, not dispatching cached mode.");
return;
}
@@ -372,18 +302,18 @@
// mode change is in progress. In that case we shouldn't dispatch an event
// as it will be dispatched when the current mode changes.
if (std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mFeatures.mode) {
+ mRefreshRateConfigs->getCurrentRefreshRate().getMode() != mPolicy.mode) {
return;
}
// If there is no change from cached mode, there is no need to dispatch an event
- if (mFeatures.mode == mFeatures.cachedModeChangedParams->mode) {
+ if (mPolicy.mode == mPolicy.cachedModeChangedParams->mode) {
return;
}
- mFeatures.cachedModeChangedParams->mode = mFeatures.mode;
- onNonPrimaryDisplayModeChanged(mFeatures.cachedModeChangedParams->handle,
- mFeatures.cachedModeChangedParams->mode);
+ mPolicy.cachedModeChangedParams->mode = mPolicy.mode;
+ onNonPrimaryDisplayModeChanged(mPolicy.cachedModeChangedParams->handle,
+ mPolicy.cachedModeChangedParams->mode);
}
void Scheduler::onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
@@ -424,12 +354,12 @@
}
DisplayStatInfo Scheduler::getDisplayStatInfo(nsecs_t now) {
- const auto vsyncTime = mVsyncSchedule.tracker->nextAnticipatedVSyncTimeFrom(now);
- const auto vsyncPeriod = mVsyncSchedule.tracker->currentPeriod();
+ const auto vsyncTime = mVsyncSchedule->getTracker().nextAnticipatedVSyncTimeFrom(now);
+ const auto vsyncPeriod = mVsyncSchedule->getTracker().currentPeriod();
return DisplayStatInfo{.vsyncTime = vsyncTime, .vsyncPeriod = vsyncPeriod};
}
-Scheduler::ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
+ConnectionHandle Scheduler::enableVSyncInjection(bool enable) {
if (mInjectVSyncs == enable) {
return {};
}
@@ -470,7 +400,7 @@
void Scheduler::enableHardwareVsync() {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) {
- mVsyncSchedule.tracker->resetModel();
+ mVsyncSchedule->getTracker().resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -523,10 +453,10 @@
void Scheduler::setVsyncPeriod(nsecs_t period) {
std::lock_guard<std::mutex> lock(mHWVsyncLock);
- mVsyncSchedule.controller->startPeriodTransition(period);
+ mVsyncSchedule->getController().startPeriodTransition(period);
if (!mPrimaryHWVsyncEnabled) {
- mVsyncSchedule.tracker->resetModel();
+ mVsyncSchedule->getTracker().resetModel();
mSchedulerCallback.setVsyncEnabled(true);
mPrimaryHWVsyncEnabled = true;
}
@@ -539,8 +469,9 @@
{ // Scope for the lock
std::lock_guard<std::mutex> lock(mHWVsyncLock);
if (mPrimaryHWVsyncEnabled) {
- needsHwVsync = mVsyncSchedule.controller->addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
- periodFlushed);
+ needsHwVsync =
+ mVsyncSchedule->getController().addHwVsyncTimestamp(timestamp, hwcVsyncPeriod,
+ periodFlushed);
}
}
@@ -551,24 +482,23 @@
}
}
-void Scheduler::addPresentFence(const std::shared_ptr<FenceTime>& fenceTime) {
- if (mVsyncSchedule.controller->addPresentFence(fenceTime)) {
+void Scheduler::addPresentFence(std::shared_ptr<FenceTime> fence) {
+ if (mVsyncSchedule->getController().addPresentFence(std::move(fence))) {
enableHardwareVsync();
} else {
disableHardwareVsync(false);
}
}
-void Scheduler::setIgnorePresentFences(bool ignore) {
- mVsyncSchedule.controller->setIgnorePresentFences(ignore);
-}
-
void Scheduler::registerLayer(Layer* layer) {
+ using WindowType = gui::WindowInfo::Type;
+
scheduler::LayerHistory::LayerVoteType voteType;
- if (!mOptions.useContentDetection || layer->getWindowType() == WindowInfo::Type::STATUS_BAR) {
+ if (!mFeatures.test(Feature::kContentDetection) ||
+ layer->getWindowType() == WindowType::STATUS_BAR) {
voteType = scheduler::LayerHistory::LayerVoteType::NoVote;
- } else if (layer->getWindowType() == WindowInfo::Type::WALLPAPER) {
+ } else if (layer->getWindowType() == WindowType::WALLPAPER) {
// Running Wallpaper at Min is considered as part of content detection.
voteType = scheduler::LayerHistory::LayerVoteType::Min;
} else {
@@ -615,13 +545,13 @@
bool frameRateChanged;
bool frameRateOverridesChanged;
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.contentRequirements = summary;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.contentRequirements = summary;
newMode = calculateRefreshRateModeId(&consideredSignals);
frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
- if (mFeatures.mode == newMode) {
+ if (mPolicy.mode == newMode) {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
@@ -629,15 +559,16 @@
}
frameRateChanged = false;
} else {
- mFeatures.mode = newMode;
+ mPolicy.mode = newMode;
frameRateChanged = true;
}
}
if (frameRateChanged) {
- auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+ const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+
mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ModeEvent::None
- : ModeEvent::Changed);
+ consideredSignals.idle ? DisplayModeEvent::None
+ : DisplayModeEvent::Changed);
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -660,8 +591,8 @@
void Scheduler::setDisplayPowerState(bool normal) {
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.isDisplayPowerStateNormal = normal;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.isDisplayPowerStateNormal = normal;
}
if (mDisplayPowerTimer) {
@@ -703,7 +634,7 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state);
+ handleTimerStateChanged(&mPolicy.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
@@ -713,14 +644,14 @@
// Clear layer history to get fresh FPS detection.
// NOTE: Instead of checking all the layers, we should be checking the layer
// that is currently on top. b/142507166 will give us this capability.
- if (handleTimerStateChanged(&mFeatures.touch, touch)) {
+ if (handleTimerStateChanged(&mPolicy.touch, touch)) {
mLayerHistory.clear();
}
ATRACE_INT("TouchState", static_cast<int>(touch));
}
void Scheduler::displayPowerTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.displayPowerTimer, state);
+ handleTimerStateChanged(&mPolicy.displayPowerTimer, state);
ATRACE_INT("ExpiredDisplayPowerTimer", static_cast<int>(state));
}
@@ -730,7 +661,7 @@
StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Content detection: %s %s\n\n",
- toContentDetectionString(mOptions.useContentDetection),
+ mFeatures.test(Feature::kContentDetection) ? "on" : "off",
mLayerHistory.dump().c_str());
{
@@ -756,13 +687,8 @@
}
}
-void Scheduler::dumpVsync(std::string& s) const {
- using base::StringAppendF;
-
- StringAppendF(&s, "VSyncReactor:\n");
- mVsyncSchedule.controller->dump(s);
- StringAppendF(&s, "VSyncDispatch:\n");
- mVsyncSchedule.dispatch->dump(s);
+void Scheduler::dumpVsync(std::string& out) const {
+ mVsyncSchedule->dump(out);
}
bool Scheduler::updateFrameRateOverrides(
@@ -774,7 +700,7 @@
if (!consideredSignals.idle) {
const auto frameRateOverrides =
- refreshRateConfigs->getFrameRateOverrides(mFeatures.contentRequirements,
+ refreshRateConfigs->getFrameRateOverrides(mPolicy.contentRequirements,
displayRefreshRate, consideredSignals);
std::lock_guard lock(mFrameRateOverridesLock);
if (!std::equal(mFrameRateOverridesByContent.begin(), mFrameRateOverridesByContent.end(),
@@ -797,31 +723,30 @@
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
const auto refreshRateConfigs = holdRefreshRateConfigs();
{
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
if (*currentState == newState) {
return false;
}
*currentState = newState;
newMode = calculateRefreshRateModeId(&consideredSignals);
frameRateOverridesChanged = updateFrameRateOverrides(consideredSignals, newMode->getFps());
- if (mFeatures.mode == newMode) {
+ if (mPolicy.mode == newMode) {
// We don't need to change the display mode, but we might need to send an event
// about a mode change, since it was suppressed due to a previous idleConsidered
if (!consideredSignals.idle) {
dispatchCachedReportedMode();
}
} else {
- mFeatures.mode = newMode;
+ mPolicy.mode = newMode;
refreshRateChanged = true;
}
}
if (refreshRateChanged) {
- const RefreshRate& newRefreshRate =
- refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
+ const auto newRefreshRate = refreshRateConfigs->getRefreshRateFromModeId(newMode->getId());
mSchedulerCallback.changeRefreshRate(newRefreshRate,
- consideredSignals.idle ? ModeEvent::None
- : ModeEvent::Changed);
+ consideredSignals.idle ? DisplayModeEvent::None
+ : DisplayModeEvent::Changed);
}
if (frameRateOverridesChanged) {
mSchedulerCallback.triggerOnFrameRateOverridesChanged();
@@ -838,27 +763,26 @@
// If Display Power is not in normal operation we want to be in performance mode. When coming
// back to normal mode, a grace period is given with DisplayPowerTimer.
if (mDisplayPowerTimer &&
- (!mFeatures.isDisplayPowerStateNormal ||
- mFeatures.displayPowerTimer == TimerState::Reset)) {
+ (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) {
return refreshRateConfigs->getMaxRefreshRateByPolicy().getMode();
}
- const bool touchActive = mTouchTimer && mFeatures.touch == TouchState::Active;
- const bool idle = mFeatures.idleTimer == TimerState::Expired;
+ const bool touchActive = mTouchTimer && mPolicy.touch == TouchState::Active;
+ const bool idle = mPolicy.idleTimer == TimerState::Expired;
return refreshRateConfigs
- ->getBestRefreshRate(mFeatures.contentRequirements,
- {.touch = touchActive, .idle = idle}, consideredSignals)
+ ->getBestRefreshRate(mPolicy.contentRequirements, {.touch = touchActive, .idle = idle},
+ consideredSignals)
.getMode();
}
DisplayModePtr Scheduler::getPreferredDisplayMode() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
// Make sure that the default mode ID is first updated, before returned.
- if (mFeatures.mode) {
- mFeatures.mode = calculateRefreshRateModeId();
+ if (mPolicy.mode) {
+ mPolicy.mode = calculateRefreshRateModeId();
}
- return mFeatures.mode;
+ return mPolicy.mode;
}
void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline) {
@@ -915,8 +839,8 @@
std::chrono::steady_clock::time_point Scheduler::getPreviousVsyncFrom(
nsecs_t expectedPresentTime) const {
const auto presentTime = std::chrono::nanoseconds(expectedPresentTime);
- const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule.tracker->currentPeriod());
+ const auto vsyncPeriod = std::chrono::nanoseconds(mVsyncSchedule->getTracker().currentPeriod());
return std::chrono::steady_clock::time_point(presentTime - vsyncPeriod);
}
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 2a6de54..e127ff7 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -31,40 +31,37 @@
#include <ui/GraphicTypes.h>
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
+#include <scheduler/Features.h>
+
#include "EventThread.h"
#include "LayerHistory.h"
#include "MessageQueue.h"
#include "OneShotTimer.h"
#include "RefreshRateConfigs.h"
#include "SchedulerUtils.h"
+#include "VsyncSchedule.h"
namespace android {
-using namespace std::chrono_literals;
-using scheduler::LayerHistory;
-
class FenceTime;
class InjectVSyncSource;
-class PredictedVsyncTracer;
-
-namespace scheduler {
-class VsyncController;
-class VSyncDispatch;
-class VSyncTracker;
-} // namespace scheduler
namespace frametimeline {
class TokenManager;
} // namespace frametimeline
+namespace scheduler {
+
struct ISchedulerCallback {
// Indicates frame activity, i.e. whether commit and/or composite is taking place.
enum class FrameHint { kNone, kActive };
+ using RefreshRate = RefreshRateConfigs::RefreshRate;
+ using DisplayModeEvent = scheduler::DisplayModeEvent;
+
virtual void scheduleComposite(FrameHint) = 0;
virtual void setVsyncEnabled(bool) = 0;
- virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent) = 0;
+ virtual void changeRefreshRate(const RefreshRate&, DisplayModeEvent) = 0;
virtual void kernelTimerChanged(bool expired) = 0;
virtual void triggerOnFrameRateOverridesChanged() = 0;
@@ -76,18 +73,10 @@
using Impl = impl::MessageQueue;
public:
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
- using ModeEvent = scheduler::RefreshRateConfigEvent;
-
- struct Options {
- // Whether to use content detection at all.
- bool useContentDetection;
- };
-
- Scheduler(ICompositor&, ISchedulerCallback&, Options);
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
~Scheduler();
- void createVsyncSchedule(bool supportKernelIdleTimer);
+ void createVsyncSchedule(FeatureFlags);
void startTimers();
void run();
@@ -107,7 +96,6 @@
return std::move(future);
}
- using ConnectionHandle = scheduler::ConnectionHandle;
ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration,
@@ -119,7 +107,7 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mFeatureStateLock);
+ void onPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr) EXCLUDES(mPolicyLock);
void onNonPrimaryDisplayModeChanged(ConnectionHandle, DisplayModePtr);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -152,8 +140,7 @@
// VsyncController detected that the vsync period changed, and false otherwise.
void addResyncSample(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod,
bool* periodFlushed);
- void addPresentFence(const std::shared_ptr<FenceTime>&);
- void setIgnorePresentFences(bool ignore);
+ void addPresentFence(std::shared_ptr<FenceTime>);
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
@@ -172,7 +159,7 @@
void setDisplayPowerState(bool normal);
- scheduler::VSyncDispatch& getVsyncDispatch() { return *mVsyncSchedule.dispatch; }
+ VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); }
// Returns true if a given vsync timestamp is considered valid vsync
// for a given uid
@@ -211,7 +198,7 @@
std::optional<Fps> getFrameRateOverride(uid_t uid) const
EXCLUDES(mRefreshRateConfigsLock, mFrameRateOverridesLock);
- void setRefreshRateConfigs(std::shared_ptr<scheduler::RefreshRateConfigs> refreshRateConfigs)
+ void setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> refreshRateConfigs)
EXCLUDES(mRefreshRateConfigsLock) {
// We need to stop the idle timer on the previous RefreshRateConfigs instance
// and cleanup the scheduler's state before we switch to the other RefreshRateConfigs.
@@ -220,8 +207,8 @@
if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer();
}
{
- std::scoped_lock lock(mFeatureStateLock);
- mFeatures = {};
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
}
{
std::scoped_lock lock(mRefreshRateConfigsLock);
@@ -251,18 +238,10 @@
using FrameHint = ISchedulerCallback::FrameHint;
- // In order to make sure that the features don't override themselves, we need a state machine
- // to keep track which feature requested the config change.
enum class ContentDetectionState { Off, On };
enum class TimerState { Reset, Expired };
enum class TouchState { Inactive, Active };
- struct VsyncSchedule {
- std::unique_ptr<scheduler::VsyncController> controller;
- std::unique_ptr<scheduler::VSyncTracker> tracker;
- std::unique_ptr<scheduler::VSyncDispatch> dispatch;
- };
-
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(
@@ -284,19 +263,17 @@
// selection were initialized, prioritizes them, and calculates the DisplayModeId
// for the suggested refresh rate.
DisplayModePtr calculateRefreshRateModeId(
- scheduler::RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr)
- REQUIRES(mFeatureStateLock);
+ RefreshRateConfigs::GlobalSignals* consideredSignals = nullptr) REQUIRES(mPolicyLock);
- void dispatchCachedReportedMode() REQUIRES(mFeatureStateLock) EXCLUDES(mRefreshRateConfigsLock);
- bool updateFrameRateOverrides(scheduler::RefreshRateConfigs::GlobalSignals consideredSignals,
- Fps displayRefreshRate) REQUIRES(mFeatureStateLock)
- EXCLUDES(mFrameRateOverridesLock);
+ void dispatchCachedReportedMode() REQUIRES(mPolicyLock) EXCLUDES(mRefreshRateConfigsLock);
+ bool updateFrameRateOverrides(RefreshRateConfigs::GlobalSignals, Fps displayRefreshRate)
+ REQUIRES(mPolicyLock) EXCLUDES(mFrameRateOverridesLock);
impl::EventThread::ThrottleVsyncCallback makeThrottleVsyncCallback() const
EXCLUDES(mRefreshRateConfigsLock);
impl::EventThread::GetVsyncPeriodFunction makeGetVsyncPeriodFunction() const;
- std::shared_ptr<scheduler::RefreshRateConfigs> holdRefreshRateConfigs() const
+ std::shared_ptr<RefreshRateConfigs> holdRefreshRateConfigs() const
EXCLUDES(mRefreshRateConfigsLock) {
std::scoped_lock lock(mRefreshRateConfigsLock);
return mRefreshRateConfigs;
@@ -322,66 +299,63 @@
std::atomic<nsecs_t> mLastResyncTime = 0;
- const Options mOptions;
- VsyncSchedule mVsyncSchedule;
+ const FeatureFlags mFeatures;
+ std::optional<VsyncSchedule> mVsyncSchedule;
// Used to choose refresh rate if content detection is enabled.
LayerHistory mLayerHistory;
// Timer used to monitor touch events.
- std::optional<scheduler::OneShotTimer> mTouchTimer;
+ std::optional<OneShotTimer> mTouchTimer;
// Timer used to monitor display power mode.
- std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
+ std::optional<OneShotTimer> mDisplayPowerTimer;
ISchedulerCallback& mSchedulerCallback;
- // In order to make sure that the features don't override themselves, we need a state machine
- // to keep track which feature requested the config change.
- mutable std::mutex mFeatureStateLock;
+ mutable std::mutex mPolicyLock;
struct {
+ // Policy for choosing the display mode.
+ LayerHistory::Summary contentRequirements;
TimerState idleTimer = TimerState::Reset;
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
-
- DisplayModePtr mode;
- LayerHistory::Summary contentRequirements;
-
bool isDisplayPowerStateNormal = true;
- // Used to cache the last parameters of onPrimaryDisplayModeChanged
+ // Chosen display mode.
+ DisplayModePtr mode;
+
struct ModeChangedParams {
ConnectionHandle handle;
DisplayModePtr mode;
};
+ // Parameters for latest dispatch of mode change event.
std::optional<ModeChangedParams> cachedModeChangedParams;
- } mFeatures GUARDED_BY(mFeatureStateLock);
+ } mPolicy GUARDED_BY(mPolicyLock);
mutable std::mutex mRefreshRateConfigsLock;
- std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs
- GUARDED_BY(mRefreshRateConfigsLock);
+ std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs GUARDED_BY(mRefreshRateConfigsLock);
std::mutex mVsyncTimelineLock;
std::optional<hal::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
GUARDED_BY(mVsyncTimelineLock);
static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
- std::unique_ptr<PredictedVsyncTracer> mPredictedVsyncTracer;
-
// The frame rate override lists need their own mutex as they are being read
// by SurfaceFlinger, Scheduler and EventThread (as a callback) to prevent deadlocks
mutable std::mutex mFrameRateOverridesLock;
// mappings between a UID and a preferred refresh rate that this app would
// run at.
- scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
+ RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesByContent
GUARDED_BY(mFrameRateOverridesLock);
- scheduler::RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
+ RefreshRateConfigs::UidToFrameRateOverride mFrameRateOverridesFromBackdoor
GUARDED_BY(mFrameRateOverridesLock);
// Keeps track of whether the screen is acquired for debug
std::atomic<bool> mScreenAcquired = false;
};
+} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index c9c2d84..22c3a70 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -17,14 +17,18 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerTimer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-#include <android-base/stringprintf.h>
-#include <log/log.h>
+
+#include <chrono>
+#include <cstdint>
+
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <sys/unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <ftl/enum.h>
+#include <log/log.h>
#include <utils/Trace.h>
-#include <chrono>
-#include <cstdint>
#include "SchedulerUtils.h"
#include "Timer.h"
@@ -215,26 +219,9 @@
mDebugState = state;
}
-const char* Timer::strDebugState(DebugState state) const {
- switch (state) {
- case DebugState::Reset:
- return "Reset";
- case DebugState::Running:
- return "Running";
- case DebugState::Waiting:
- return "Waiting";
- case DebugState::Reading:
- return "Reading";
- case DebugState::InCallback:
- return "InCallback";
- case DebugState::Terminated:
- return "Terminated";
- }
-}
-
void Timer::dump(std::string& result) const {
std::lock_guard lock(mMutex);
- StringAppendF(&result, "\t\tDebugState: %s\n", strDebugState(mDebugState));
+ StringAppendF(&result, "\t\tDebugState: %s\n", ftl::enum_string(mDebugState).c_str());
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Timer.h b/services/surfaceflinger/Scheduler/Timer.h
index 69ce079..628d800 100644
--- a/services/surfaceflinger/Scheduler/Timer.h
+++ b/services/surfaceflinger/Scheduler/Timer.h
@@ -37,11 +37,20 @@
void dump(std::string& result) const final;
private:
- enum class DebugState { Reset, Running, Waiting, Reading, InCallback, Terminated };
+ enum class DebugState {
+ Reset,
+ Running,
+ Waiting,
+ Reading,
+ InCallback,
+ Terminated,
+
+ ftl_last = Terminated
+ };
+
void reset();
void cleanup();
void setDebugState(DebugState state) EXCLUDES(mMutex);
- const char* strDebugState(DebugState state) const;
int mTimerFd = -1;
int mEpollFd = -1;
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index ee973f7..1c9de1c 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -47,7 +47,7 @@
VSyncReactor::~VSyncReactor() = default;
-bool VSyncReactor::addPresentFence(const std::shared_ptr<android::FenceTime>& fence) {
+bool VSyncReactor::addPresentFence(std::shared_ptr<FenceTime> fence) {
if (!fence) {
return false;
}
@@ -80,7 +80,7 @@
if (mPendingLimit == mUnfiredFences.size()) {
mUnfiredFences.erase(mUnfiredFences.begin());
}
- mUnfiredFences.push_back(fence);
+ mUnfiredFences.push_back(std::move(fence));
} else {
timestampAccepted &= mTracker.addVsyncTimestamp(signalTime);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 449d4c3..a9d536b 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -37,7 +37,7 @@
bool supportKernelIdleTimer);
~VSyncReactor();
- bool addPresentFence(const std::shared_ptr<android::FenceTime>& fence) final;
+ bool addPresentFence(std::shared_ptr<FenceTime>) final;
void setIgnorePresentFences(bool ignore) final;
void startPeriodTransition(nsecs_t period) final;
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 95750ad..76315d2 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -17,7 +17,9 @@
#pragma once
#include <utils/Timers.h>
-#include "Fps.h"
+
+#include <scheduler/Fps.h>
+
#include "VSyncDispatch.h"
namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/VsyncConfiguration.h b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
index 8447512..02ebd70 100644
--- a/services/surfaceflinger/Scheduler/VsyncConfiguration.h
+++ b/services/surfaceflinger/Scheduler/VsyncConfiguration.h
@@ -23,7 +23,8 @@
#include <ftl/small_map.h>
#include <utils/Timers.h>
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
#include "VsyncModulator.h"
namespace android::scheduler {
diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h
index 0f0df22..59f6537 100644
--- a/services/surfaceflinger/Scheduler/VsyncController.h
+++ b/services/surfaceflinger/Scheduler/VsyncController.h
@@ -17,19 +17,15 @@
#pragma once
#include <cstddef>
+#include <memory>
+#include <ui/FenceTime.h>
#include <utils/Mutex.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
-#include <ui/FenceTime.h>
-
-#include <memory>
-
namespace android::scheduler {
-class FenceTime;
-
class VsyncController {
public:
virtual ~VsyncController();
@@ -43,7 +39,7 @@
* an accurate prediction,
* False otherwise
*/
- virtual bool addPresentFence(const std::shared_ptr<android::FenceTime>&) = 0;
+ virtual bool addPresentFence(std::shared_ptr<FenceTime>) = 0;
/*
* Adds a hw sync timestamp to the model. The controller will use the timestamp
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
new file mode 100644
index 0000000..77d1223
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <scheduler/Fps.h>
+
+#include "VsyncSchedule.h"
+
+#include "Timer.h"
+#include "VSyncDispatchTimerQueue.h"
+#include "VSyncPredictor.h"
+#include "VSyncReactor.h"
+
+#include "../TracedOrdinal.h"
+
+namespace android::scheduler {
+
+class VsyncSchedule::PredictedVsyncTracer {
+ // Invoked from the thread of the VsyncDispatch owned by this VsyncSchedule.
+ constexpr auto makeVsyncCallback() {
+ return [this](nsecs_t, nsecs_t, nsecs_t) {
+ mParity = !mParity;
+ schedule();
+ };
+ }
+
+public:
+ explicit PredictedVsyncTracer(VsyncDispatch& dispatch)
+ : mRegistration(dispatch, makeVsyncCallback(), __func__) {
+ schedule();
+ }
+
+private:
+ void schedule() { mRegistration.schedule({0, 0, 0}); }
+
+ TracedOrdinal<bool> mParity = {"VSYNC-predicted", 0};
+ VSyncCallbackRegistration mRegistration;
+};
+
+VsyncSchedule::VsyncSchedule(FeatureFlags features)
+ : mTracker(createTracker()),
+ mDispatch(createDispatch(*mTracker)),
+ mController(createController(*mTracker, features)) {
+ if (features.test(Feature::kTracePredictedVsync)) {
+ mTracer = std::make_unique<PredictedVsyncTracer>(*mDispatch);
+ }
+}
+
+VsyncSchedule::VsyncSchedule(TrackerPtr tracker, DispatchPtr dispatch, ControllerPtr controller)
+ : mTracker(std::move(tracker)),
+ mDispatch(std::move(dispatch)),
+ mController(std::move(controller)) {}
+
+VsyncSchedule::VsyncSchedule(VsyncSchedule&&) = default;
+VsyncSchedule::~VsyncSchedule() = default;
+
+void VsyncSchedule::dump(std::string& out) const {
+ out.append("VsyncController:\n");
+ mController->dump(out);
+
+ out.append("VsyncDispatch:\n");
+ mDispatch->dump(out);
+}
+
+VsyncSchedule::TrackerPtr VsyncSchedule::createTracker() {
+ // TODO(b/144707443): Tune constants.
+ constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs();
+ constexpr size_t kHistorySize = 20;
+ constexpr size_t kMinSamplesForPrediction = 6;
+ constexpr uint32_t kDiscardOutlierPercent = 20;
+
+ return std::make_unique<VSyncPredictor>(kInitialPeriod, kHistorySize, kMinSamplesForPrediction,
+ kDiscardOutlierPercent);
+}
+
+VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(VsyncTracker& tracker) {
+ using namespace std::chrono_literals;
+
+ // TODO(b/144707443): Tune constants.
+ constexpr std::chrono::nanoseconds kGroupDispatchWithin = 500us;
+ constexpr std::chrono::nanoseconds kSnapToSameVsyncWithin = 3ms;
+
+ return std::make_unique<VSyncDispatchTimerQueue>(std::make_unique<Timer>(), tracker,
+ kGroupDispatchWithin.count(),
+ kSnapToSameVsyncWithin.count());
+}
+
+VsyncSchedule::ControllerPtr VsyncSchedule::createController(VsyncTracker& tracker,
+ FeatureFlags features) {
+ // TODO(b/144707443): Tune constants.
+ constexpr size_t kMaxPendingFences = 20;
+ const bool hasKernelIdleTimer = features.test(Feature::kKernelIdleTimer);
+
+ auto reactor = std::make_unique<VSyncReactor>(std::make_unique<SystemClock>(), tracker,
+ kMaxPendingFences, hasKernelIdleTimer);
+
+ reactor->setIgnorePresentFences(!features.test(Feature::kPresentFences));
+ return reactor;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h
new file mode 100644
index 0000000..0d9b114
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <scheduler/Features.h>
+
+namespace android::scheduler {
+
+// TODO(b/185535769): Rename classes, and remove aliases.
+class VSyncDispatch;
+class VSyncTracker;
+
+class VsyncController;
+using VsyncDispatch = VSyncDispatch;
+using VsyncTracker = VSyncTracker;
+
+// Schedule that synchronizes to hardware VSYNC of a physical display.
+class VsyncSchedule {
+public:
+ explicit VsyncSchedule(FeatureFlags);
+ VsyncSchedule(VsyncSchedule&&);
+ ~VsyncSchedule();
+
+ // TODO(b/185535769): Hide behind API.
+ const VsyncTracker& getTracker() const { return *mTracker; }
+ VsyncTracker& getTracker() { return *mTracker; }
+ VsyncController& getController() { return *mController; }
+
+ // TODO(b/185535769): Remove once VsyncSchedule owns all registrations.
+ VsyncDispatch& getDispatch() { return *mDispatch; }
+
+ void dump(std::string&) const;
+
+private:
+ friend class TestableScheduler;
+
+ using TrackerPtr = std::unique_ptr<VsyncTracker>;
+ using DispatchPtr = std::unique_ptr<VsyncDispatch>;
+ using ControllerPtr = std::unique_ptr<VsyncController>;
+
+ // For tests.
+ VsyncSchedule(TrackerPtr, DispatchPtr, ControllerPtr);
+
+ static TrackerPtr createTracker();
+ static DispatchPtr createDispatch(VsyncTracker&);
+ static ControllerPtr createController(VsyncTracker&, FeatureFlags);
+
+ class PredictedVsyncTracer;
+ using TracerPtr = std::unique_ptr<PredictedVsyncTracer>;
+
+ // Effectively const except in move constructor.
+ TrackerPtr mTracker;
+ DispatchPtr mDispatch;
+ ControllerPtr mController;
+ TracerPtr mTracer;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Features.h b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
new file mode 100644
index 0000000..0e96678
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Features.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <ftl/Flags.h>
+
+#include <cstdint>
+
+namespace android::scheduler {
+
+enum class Feature : std::uint8_t {
+ kPresentFences = 0b1,
+ kKernelIdleTimer = 0b10,
+ kContentDetection = 0b100,
+ kTracePredictedVsync = 0b1000,
+};
+
+using FeatureFlags = Flags<Feature>;
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
similarity index 100%
rename from services/surfaceflinger/Fps.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Fps.h
diff --git a/services/surfaceflinger/Scheduler/Seamlessness.h b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
similarity index 67%
rename from services/surfaceflinger/Scheduler/Seamlessness.h
rename to services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
index 3e42a4d..93bf726 100644
--- a/services/surfaceflinger/Scheduler/Seamlessness.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Seamlessness.h
@@ -16,11 +16,11 @@
#pragma once
-#include <cstring>
#include <ostream>
-namespace android {
-namespace scheduler {
+#include <ftl/enum.h>
+
+namespace android::scheduler {
// The seamlessness requirement of a Layer.
enum class Seamlessness {
@@ -31,24 +31,14 @@
// Indicates no preference for seamlessness. For such layers the system will
// prefer seamless switches, but also non-seamless switches to the group of the
// default config are allowed.
- Default
+ Default,
+
+ ftl_last = Default
};
-inline std::string toString(Seamlessness seamlessness) {
- switch (seamlessness) {
- case Seamlessness::OnlySeamless:
- return "OnlySeamless";
- case Seamlessness::SeamedAndSeamless:
- return "SeamedAndSeamless";
- case Seamlessness::Default:
- return "Default";
- }
-}
-
// Used by gtest
-inline std::ostream& operator<<(std::ostream& os, Seamlessness val) {
- return os << toString(val);
+inline std::ostream& operator<<(std::ostream& os, Seamlessness s) {
+ return os << ftl::enum_string(s);
}
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 45e0a3f..5f5f1f6 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -26,6 +26,7 @@
#include <android-base/properties.h>
#include <android/configuration.h>
+#include <android/gui/IDisplayEventConnection.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>
@@ -51,7 +52,6 @@
#include <ftl/future.h>
#include <gui/BufferQueue.h>
#include <gui/DebugEGLImageTracker.h>
-#include <gui/IDisplayEventConnection.h>
#include <gui/IProducerListener.h>
#include <gui/LayerDebugInfo.h>
#include <gui/LayerMetadata.h>
@@ -169,6 +169,7 @@
using android::hardware::power::Boost;
using base::StringAppendF;
using gui::DisplayInfo;
+using gui::IDisplayEventConnection;
using gui::IWindowInfosListener;
using gui::WindowInfo;
using ui::ColorMode;
@@ -357,10 +358,11 @@
SurfaceFlinger::SurfaceFlinger(Factory& factory, SkipInitializationTag)
: mFactory(factory),
+ mPid(getpid()),
mInterceptor(mFactory.createSurfaceInterceptor()),
mTimeStats(std::make_shared<impl::TimeStats>()),
mFrameTracer(mFactory.createFrameTracer()),
- mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, getpid())),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
@@ -489,6 +491,11 @@
enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
+
+ mTransactionTracingEnabled = property_get_bool("debug.sf.enable_transaction_tracing", false);
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.enable();
+ }
}
LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
@@ -772,6 +779,25 @@
ATRACE_INT("TexturePoolSize", mTexturePool.size());
}
+static std::optional<renderengine::RenderEngine::RenderEngineType>
+chooseRenderEngineTypeViaSysProp() {
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
+
+ if (strcmp(prop, "gles") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::GLES;
+ } else if (strcmp(prop, "threaded") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::THREADED;
+ } else if (strcmp(prop, "skiagl") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL;
+ } else if (strcmp(prop, "skiaglthreaded") == 0) {
+ return renderengine::RenderEngine::RenderEngineType::SKIA_GL_THREADED;
+ } else {
+ ALOGE("Unrecognized RenderEngineType %s; ignoring!", prop);
+ return {};
+ }
+}
+
// Do not call property_set on main thread which will be blocked by init
// Use StartPropertySetThread instead.
void SurfaceFlinger::init() {
@@ -782,19 +808,21 @@
// Get a RenderEngine for the given display / config (can't fail)
// TODO(b/77156734): We need to stop casting and use HAL types when possible.
// Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display.
- mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(
- renderengine::RenderEngineCreationArgs::Builder()
- .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
- .setImageCacheSize(maxFrameBufferAcquiredBuffers)
- .setUseColorManagerment(useColorManagement)
- .setEnableProtectedContext(enable_protected_contents(false))
- .setPrecacheToneMapperShaderOnly(false)
- .setSupportsBackgroundBlur(mSupportsBlur)
- .setContextPriority(
- useContextPriority
- ? renderengine::RenderEngine::ContextPriority::REALTIME
- : renderengine::RenderEngine::ContextPriority::MEDIUM)
- .build()));
+ auto builder = renderengine::RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int32_t>(defaultCompositionPixelFormat))
+ .setImageCacheSize(maxFrameBufferAcquiredBuffers)
+ .setUseColorManagerment(useColorManagement)
+ .setEnableProtectedContext(enable_protected_contents(false))
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(mSupportsBlur)
+ .setContextPriority(
+ useContextPriority
+ ? renderengine::RenderEngine::ContextPriority::REALTIME
+ : renderengine::RenderEngine::ContextPriority::MEDIUM);
+ if (auto type = chooseRenderEngineTypeViaSysProp()) {
+ builder.setRenderEngineType(type.value());
+ }
+ mCompositionEngine->setRenderEngine(renderengine::RenderEngine::create(builder.build()));
mMaxRenderTargetSize =
std::min(getRenderEngine().getMaxTextureSize(), getRenderEngine().getMaxViewportDims());
@@ -1102,7 +1130,7 @@
}
}
-status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
ATRACE_CALL();
if (!displayToken) {
@@ -1143,7 +1171,7 @@
return future.get();
}
-void SurfaceFlinger::setActiveModeInternal() {
+void SurfaceFlinger::updateInternalStateWithChangedMode() {
ATRACE_CALL();
const auto display = getDefaultDisplayDeviceLocked();
@@ -1179,7 +1207,7 @@
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
- if (upcomingModeInfo.event != Scheduler::ModeEvent::None) {
+ if (upcomingModeInfo.event != DisplayModeEvent::None) {
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
}
}
@@ -1198,9 +1226,10 @@
updatePhaseConfiguration(refreshRate);
}
-void SurfaceFlinger::performSetActiveMode() {
+void SurfaceFlinger::setActiveModeInHwcIfNeeded() {
ATRACE_CALL();
- ALOGV("%s", __FUNCTION__);
+
+ std::optional<PhysicalDisplayId> displayToUpdateImmediately;
for (const auto& iter : mDisplays) {
const auto& display = iter.second;
@@ -1235,8 +1264,7 @@
to_string(display->getId()).c_str());
if (display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
- // display is not valid or we are already in the requested mode
- // on both cases there is nothing left to do
+ // we are already in the requested mode, there is nothing left to do
desiredActiveModeChangeDone(display);
continue;
}
@@ -1267,8 +1295,26 @@
}
mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
- // Scheduler will submit an empty frame to HWC if needed.
- mSetActiveModePending = true;
+ if (outTimeline.refreshRequired) {
+ // Scheduler will submit an empty frame to HWC.
+ mSetActiveModePending = true;
+ } else {
+ // Updating the internal state should be done outside the loop,
+ // because it can recreate a DisplayDevice and modify mDisplays
+ // which will invalidate the iterator.
+ displayToUpdateImmediately = display->getPhysicalId();
+ }
+ }
+
+ if (displayToUpdateImmediately) {
+ updateInternalStateWithChangedMode();
+
+ const auto display = getDisplayDeviceLocked(*displayToUpdateImmediately);
+ const auto desiredActiveMode = display->getDesiredActiveMode();
+ if (desiredActiveMode &&
+ display->getActiveMode()->getId() == desiredActiveMode->mode->getId()) {
+ desiredActiveModeChangeDone(display);
+ }
}
}
@@ -1935,7 +1981,7 @@
// We received the present fence from the HWC, so we assume it successfully updated
// the mode, hence we update SF.
mSetActiveModePending = false;
- ON_MAIN_THREAD(setActiveModeInternal());
+ ON_MAIN_THREAD(updateInternalStateWithChangedMode());
}
if (framePending) {
@@ -1946,7 +1992,7 @@
}
if (mTracingEnabledChanged) {
- mTracingEnabled = mLayerTracing.isEnabled();
+ mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
}
@@ -1964,7 +2010,7 @@
bool needsTraversal = false;
if (clearTransactionFlags(eTransactionFlushNeeded)) {
- needsTraversal = flushTransactionQueues();
+ needsTraversal = flushTransactionQueues(vsyncId);
}
const bool shouldCommit =
@@ -2002,7 +2048,7 @@
mScheduler->chooseRefreshRateForContent();
}
- ON_MAIN_THREAD(performSetActiveMode());
+ ON_MAIN_THREAD(setActiveModeInHwcIfNeeded());
updateCursorAsync();
updateInputFlinger();
@@ -2095,7 +2141,7 @@
modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
- if (mTracingEnabled) {
+ if (mLayerTracingEnabled) {
// This will block and should only be used for debugging.
if (mVisibleRegionsDirty) {
mLayerTracing.notify("visibleRegionsDirty");
@@ -3080,7 +3126,7 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
-void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, Scheduler::ModeEvent event) {
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate, DisplayModeEvent event) {
// If this is called from the main thread mStateLock must be locked before
// Currently the only way to call this function from the main thread is from
// Scheduler::chooseRefreshRateForContent
@@ -3128,17 +3174,32 @@
mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
- const Scheduler::Options options = {
- .useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)};
+ using Feature = scheduler::Feature;
+ scheduler::FeatureFlags features;
- mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this),
- static_cast<ISchedulerCallback&>(*this), options);
- {
- auto configs = display->holdRefreshRateConfigs();
- mScheduler->createVsyncSchedule(configs->supportsKernelIdleTimer());
- mScheduler->setRefreshRateConfigs(std::move(configs));
+ if (sysprop::use_content_detection_for_refresh_rate(false)) {
+ features |= Feature::kContentDetection;
+ }
+ if (base::GetBoolProperty("debug.sf.show_predicted_vsync"s, false)) {
+ features |= Feature::kTracePredictedVsync;
+ }
+ if (!base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false) &&
+ !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE)) {
+ features |= Feature::kPresentFences;
}
+ mScheduler = std::make_unique<scheduler::Scheduler>(static_cast<ICompositor&>(*this),
+ static_cast<ISchedulerCallback&>(*this),
+ features);
+ {
+ auto configs = display->holdRefreshRateConfigs();
+ if (configs->supportsKernelIdleTimer()) {
+ features |= Feature::kKernelIdleTimer;
+ }
+
+ mScheduler->createVsyncSchedule(features);
+ mScheduler->setRefreshRateConfigs(std::move(configs));
+ }
setVsyncEnabled(false);
mScheduler->startTimers();
@@ -3171,11 +3232,6 @@
// classes from EventThread, and there should be no run-time binder cost
// anyway since there are no connected apps at this point.
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, display->getActiveMode());
- static auto ignorePresentFences =
- base::GetBoolProperty("debug.sf.vsync_reactor_ignore_present_fences"s, false);
- mScheduler->setIgnorePresentFences(
- ignorePresentFences ||
- getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE));
}
void SurfaceFlinger::updatePhaseConfiguration(const Fps& refreshRate) {
@@ -3374,10 +3430,11 @@
client->attachLayer(handle, lbc);
}
+ int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
return setTransactionState(FrameTimelineInfo{}, states, displays, 0 /* flags */, nullptr,
InputWindowCommands{}, -1 /* desiredPresentTime */,
true /* isAutoTimestamp */, {}, false /* hasListenerCallbacks */, {},
- 0 /* Undefined transactionId */);
+ transactionId);
}
uint32_t SurfaceFlinger::getTransactionFlags() const {
@@ -3400,7 +3457,7 @@
return old;
}
-bool SurfaceFlinger::flushTransactionQueues() {
+bool SurfaceFlinger::flushTransactionQueues(int64_t vsyncId) {
// to prevent onHandleDestroyed from being called while the lock is held,
// we must keep a copy of the transactions (specifically the composer
// states) around outside the scope of the lock
@@ -3486,12 +3543,13 @@
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
- return applyTransactions(transactions);
+ return applyTransactions(transactions, vsyncId);
}
}
}
-bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions) {
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
+ int64_t vsyncId) {
bool needsTraversal = false;
// Now apply all transactions.
for (const auto& transaction : transactions) {
@@ -3509,6 +3567,10 @@
std::move(transaction.transactionCommittedSignal));
}
}
+
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+ }
return needsTraversal;
}
@@ -3762,6 +3824,10 @@
state.traverseStatesWithBuffers([&](const layer_state_t& state) {
mBufferCountTracker.increment(state.surface->localBinder());
});
+
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.addQueuedTransaction(state);
+ }
queueTransaction(state);
// Check the pending state to make sure the transaction is synchronous.
@@ -3799,10 +3865,11 @@
clientStateFlags |= setClientStateLocked(frameTimelineInfo, state, desiredPresentTime,
isAutoTimestamp, postTime, permissions);
if ((flags & eAnimation) && state.state.surface) {
- if (const auto layer = fromHandle(state.state.surface).promote(); layer) {
+ if (const auto layer = fromHandle(state.state.surface).promote()) {
+ using LayerUpdateType = scheduler::LayerHistory::LayerUpdateType;
mScheduler->recordLayerHistory(layer.get(),
isAutoTimestamp ? 0 : desiredPresentTime,
- LayerHistory::LayerUpdateType::AnimationTX);
+ LayerUpdateType::AnimationTX);
}
}
}
@@ -4139,13 +4206,14 @@
std::optional<nsecs_t> dequeueBufferTimestamp;
if (what & layer_state_t::eMetadataChanged) {
dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
- auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1);
- if (gameMode != -1) {
+
+ if (const int32_t gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1); gameMode != -1) {
// The transaction will be received on the Task layer and needs to be applied to all
// child layers. Child layers that are added at a later point will obtain the game mode
// info through addChild().
- layer->setGameModeForTree(gameMode);
+ layer->setGameModeForTree(static_cast<GameMode>(gameMode));
}
+
if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eColorSpaceAgnosticChanged) {
@@ -4339,6 +4407,16 @@
return result;
}
+ int parentId = -1;
+ // We can safely promote the layer in binder thread because we have a strong reference
+ // to the layer's handle inside this scope or we were passed in a sp reference to the layer.
+ sp<Layer> parentSp = parent.promote();
+ if (parentSp != nullptr) {
+ parentId = parentSp->getSequence();
+ }
+ mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+ args.flags, parentId);
+
setTransactionFlags(eTransactionNeeded);
*outLayerId = layer->sequence;
return result;
@@ -4448,10 +4526,12 @@
displays.add(d);
nsecs_t now = systemTime();
+
+ int64_t transactionId = (((int64_t)mPid) << 32) | mUniqueTransactionId++;
// It should be on the main thread, apply it directly.
applyTransactionState(FrameTimelineInfo{}, state, displays, 0, mInputWindowCommands,
/* desiredPresentTime */ now, true, {}, /* postTime */ now, true, false,
- {}, getpid(), getuid(), 0 /* Undefined transactionId */);
+ {}, mPid, getuid(), transactionId);
setPowerModeInternal(display, hal::PowerMode::ON);
const nsecs_t vsyncPeriod =
@@ -4650,8 +4730,9 @@
}
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
- if (asProto && mLayerTracing.isEnabled()) {
+ if (asProto) {
mLayerTracing.writeToFile();
+ mTransactionTracing.writeToFile();
}
return doDump(fd, DumpArgs(), asProto);
@@ -4848,7 +4929,6 @@
}
LayersProto SurfaceFlinger::dumpDrawingStateProto(uint32_t traceFlags) const {
- // If context is SurfaceTracing thread, mTracingLock blocks display transactions on main thread.
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
LayersProto layersProto;
@@ -4872,6 +4952,7 @@
});
LayerProtoHelper::writeTransformToProto(display->getTransform(),
displayProto->mutable_transform());
+ displayProto->set_is_virtual(display->isVirtual());
}
}
@@ -5049,6 +5130,8 @@
*/
mLayerTracing.dump(result);
result.append("\n");
+ mTransactionTracing.dump(result);
+ result.append("\n");
/*
* HWC layer minidump
@@ -5281,9 +5364,9 @@
code == IBinder::SYSPROPS_TRANSACTION) {
return OK;
}
- // Numbers from 1000 to 1040 are currently used for backdoors. The code
+ // Numbers from 1000 to 1041 are currently used for backdoors. The code
// in onTransact verifies that the user is root, and has access to use SF.
- if (code >= 1000 && code <= 1040) {
+ if (code >= 1000 && code <= 1041) {
ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code);
return OK;
}
@@ -5611,7 +5694,7 @@
}();
mDebugDisplayModeSetByBackdoor = false;
- const status_t result = setActiveMode(display, modeId);
+ const status_t result = setActiveModeFromBackdoor(display, modeId);
mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
return result;
}
@@ -5722,6 +5805,20 @@
scheduleRepaint();
return NO_ERROR;
}
+ case 1041: { // Transaction tracing
+ if (data.readInt32()) {
+ // Transaction tracing is always running but allow the user to temporarily
+ // increase the buffer when actively debugging.
+ mTransactionTracing.setBufferSize(
+ TransactionTracing::ACTIVE_TRACING_BUFFER_SIZE);
+ } else {
+ mTransactionTracing.setBufferSize(
+ TransactionTracing::CONTINUOUS_TRACING_BUFFER_SIZE);
+ mTransactionTracing.writeToFile();
+ }
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
}
}
return err;
@@ -6459,7 +6556,7 @@
if (display->refreshRateConfigs().isModeAllowed(preferredDisplayMode->getId())) {
ALOGV("switching to Scheduler preferred display mode %d",
preferredDisplayMode->getId().value());
- setDesiredActiveMode({preferredDisplayMode, Scheduler::ModeEvent::Changed});
+ setDesiredActiveMode({preferredDisplayMode, DisplayModeEvent::Changed});
} else {
LOG_ALWAYS_FATAL("Desired display mode not allowed: %d",
preferredDisplayMode->getId().value());
@@ -6556,6 +6653,7 @@
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
+ mTransactionTracing.onLayerRemoved(layer->getSequence());
}
void SurfaceFlinger::onLayerUpdate() {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 6093be9..9794639 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -23,7 +23,6 @@
*/
#include <android-base/thread_annotations.h>
-#include <compositionengine/OutputColorSetting.h>
#include <cutils/atomic.h>
#include <cutils/compiler.h>
#include <gui/BufferQueue.h>
@@ -47,13 +46,15 @@
#include <utils/Trace.h>
#include <utils/threads.h>
+#include <compositionengine/OutputColorSetting.h>
+#include <scheduler/Fps.h>
+
#include "ClientCache.h"
#include "DisplayDevice.h"
#include "DisplayHardware/HWC2.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "DisplayIdGenerator.h"
#include "Effects/Daltonizer.h"
-#include "Fps.h"
#include "FrameTracker.h"
#include "LayerVector.h"
#include "Scheduler/RefreshRateConfigs.h"
@@ -63,6 +64,7 @@
#include "SurfaceFlingerFactory.h"
#include "TracedOrdinal.h"
#include "Tracing/LayerTracing.h"
+#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
#include "TransactionState.h"
@@ -104,6 +106,7 @@
class FrameTracer;
class WindowInfosListenerInvoker;
+using gui::IRegionSamplingListener;
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -164,7 +167,7 @@
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ICompositor,
- private ISchedulerCallback {
+ private scheduler::ISchedulerCallback {
public:
struct SkipInitializationTag {};
@@ -355,7 +358,6 @@
friend class TransactionApplicationTest;
friend class TunnelModeEnabledReporterTest;
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using VsyncModulator = scheduler::VsyncModulator;
using TransactionSchedule = scheduler::TransactionSchedule;
using TraverseLayersFunction = std::function<void(const LayerVector::Visitor&)>;
@@ -642,7 +644,7 @@
// Toggles hardware VSYNC by calling into HWC.
void setVsyncEnabled(bool) override;
// Initiates a refresh rate change to be applied on commit.
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override;
// Called when kernel idle timer has expired. Used to update the refresh rate overlay.
void kernelTimerChanged(bool expired) override;
// Called when the frame rate override list changed to trigger an event.
@@ -659,13 +661,12 @@
void onInitializeDisplays() REQUIRES(mStateLock);
// Sets the desired active mode bit. It obtains the lock, and sets mDesiredActiveMode.
void setDesiredActiveMode(const ActiveModeInfo& info) REQUIRES(mStateLock);
- status_t setActiveMode(const sp<IBinder>& displayToken, int id);
- // Once HWC has returned the present fence, this sets the active mode and a new refresh
- // rate in SF.
- void setActiveModeInternal() REQUIRES(mStateLock);
+ status_t setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int id);
+ // Sets the active mode and a new refresh rate in SF.
+ void updateInternalStateWithChangedMode() REQUIRES(mStateLock);
// Calls to setActiveMode on the main thread if there is a pending mode change
// that needs to be applied.
- void performSetActiveMode() REQUIRES(mStateLock);
+ void setActiveModeInHwcIfNeeded() REQUIRES(mStateLock);
void clearDesiredActiveModeState(const sp<DisplayDevice>&) REQUIRES(mStateLock);
// Called when active mode is no longer is progress
void desiredActiveModeChangeDone(const sp<DisplayDevice>&) REQUIRES(mStateLock);
@@ -711,7 +712,7 @@
int originPid, int originUid, uint64_t transactionId)
REQUIRES(mStateLock);
// flush pending transaction that was presented after desiredPresentTime.
- bool flushTransactionQueues();
+ bool flushTransactionQueues(int64_t vsyncId);
// Returns true if there is at least one transaction that needs to be flushed
bool transactionFlushNeeded();
@@ -747,7 +748,8 @@
bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
REQUIRES(mStateLock);
- bool applyTransactions(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
+ bool applyTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId)
+ REQUIRES(mStateLock);
uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock);
uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands)
REQUIRES(mStateLock);
@@ -1082,7 +1084,7 @@
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
-
+ pid_t mPid;
std::future<void> mRenderEnginePrimeCacheFuture;
// access must be protected by mStateLock
@@ -1091,6 +1093,7 @@
std::atomic<int32_t> mTransactionFlags = 0;
std::vector<std::shared_ptr<CountDownLatch>> mTransactionCommittedSignals;
bool mAnimTransactionPending = false;
+ std::atomic<uint32_t> mUniqueTransactionId = 1;
SortedVector<sp<Layer>> mLayersPendingRemoval;
// global color transform states
@@ -1181,8 +1184,10 @@
sp<SurfaceInterceptor> mInterceptor;
LayerTracing mLayerTracing{*this};
- std::mutex mTracingLock;
- bool mTracingEnabled = false;
+ bool mLayerTracingEnabled = false;
+
+ TransactionTracing mTransactionTracing;
+ bool mTransactionTracingEnabled = false;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
@@ -1260,7 +1265,7 @@
/*
* Scheduler
*/
- std::unique_ptr<Scheduler> mScheduler;
+ std::unique_ptr<scheduler::Scheduler> mScheduler;
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index e670f37..6153e8e 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -16,16 +16,16 @@
#pragma once
-#include "Fps.h"
-
-#include <cutils/compiler.h>
-#include <utils/StrongPointer.h>
-
#include <cinttypes>
#include <functional>
#include <memory>
#include <string>
+#include <cutils/compiler.h>
+#include <utils/StrongPointer.h>
+
+#include <scheduler/Fps.h>
+
namespace android {
typedef int32_t PixelFormat;
@@ -42,16 +42,12 @@
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
class Layer;
-class MessageQueue;
-class Scheduler;
class StartPropertySetThread;
class SurfaceFlinger;
class SurfaceInterceptor;
class TimeStats;
struct DisplayDeviceCreationArgs;
-struct ICompositor;
-struct ISchedulerCallback;
struct LayerCreationArgs;
namespace compositionengine {
diff --git a/services/surfaceflinger/TimeStats/Android.bp b/services/surfaceflinger/TimeStats/Android.bp
index bcc3e4e..4686eed 100644
--- a/services/surfaceflinger/TimeStats/Android.bp
+++ b/services/surfaceflinger/TimeStats/Android.bp
@@ -12,6 +12,9 @@
srcs: [
"TimeStats.cpp",
],
+ header_libs: [
+ "libscheduler_headers",
+ ],
shared_libs: [
"android.hardware.graphics.composer@2.4",
"libbase",
@@ -24,6 +27,9 @@
"libutils",
],
export_include_dirs: ["."],
+ export_header_lib_headers: [
+ "libscheduler_headers",
+ ],
export_shared_lib_headers: [
"libtimestats_proto",
],
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index bf2038b..b1a2bda 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-#include <unordered_map>
#undef LOG_TAG
#define LOG_TAG "TimeStats"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
@@ -28,6 +27,7 @@
#include <algorithm>
#include <chrono>
+#include <unordered_map>
#include "TimeStats.h"
#include "timestatsproto/TimeStatsHelper.h"
@@ -58,15 +58,15 @@
return histogramProto;
}
-SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(int32_t gameMode) {
+SurfaceflingerStatsLayerInfo_GameMode gameModeToProto(GameMode gameMode) {
switch (gameMode) {
- case TimeStatsHelper::GameModeUnsupported:
+ case GameMode::Unsupported:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSUPPORTED;
- case TimeStatsHelper::GameModeStandard:
+ case GameMode::Standard:
return SurfaceflingerStatsLayerInfo::GAME_MODE_STANDARD;
- case TimeStatsHelper::GameModePerformance:
+ case GameMode::Performance:
return SurfaceflingerStatsLayerInfo::GAME_MODE_PERFORMANCE;
- case TimeStatsHelper::GameModeBattery:
+ case GameMode::Battery:
return SurfaceflingerStatsLayerInfo::GAME_MODE_BATTERY;
default:
return SurfaceflingerStatsLayerInfo::GAME_MODE_UNSPECIFIED;
@@ -454,7 +454,7 @@
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
std::optional<Fps> renderRate,
SetFrameRateVote frameRateVote,
- int32_t gameMode) {
+ GameMode gameMode) {
ATRACE_CALL();
ALOGV("[%d]-flushAvailableRecordsToStatsLocked", layerId);
@@ -554,7 +554,7 @@
}
bool TimeStats::canAddNewAggregatedStats(uid_t uid, const std::string& layerName,
- int32_t gameMode) {
+ GameMode gameMode) {
uint32_t layerRecords = 0;
for (const auto& record : mTimeStats.stats) {
if (record.second.stats.count({uid, layerName, gameMode}) > 0) {
@@ -568,7 +568,7 @@
}
void TimeStats::setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime, int32_t gameMode) {
+ uid_t uid, nsecs_t postTime, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -718,7 +718,7 @@
void TimeStats::setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) {
+ SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -744,7 +744,7 @@
void TimeStats::setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) {
+ SetFrameRateVote frameRateVote, GameMode gameMode) {
if (!mEnabled.load()) return;
ATRACE_CALL();
@@ -823,7 +823,7 @@
// the first jank record is not dropped.
static const std::string kDefaultLayerName = "none";
- static constexpr int32_t kDefaultGameMode = TimeStatsHelper::GameModeUnsupported;
+ constexpr GameMode kDefaultGameMode = GameMode::Unsupported;
const int32_t refreshRateBucket =
clampToNearestBucket(info.refreshRate, REFRESH_RATE_BUCKET_WIDTH);
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index bdeaeb8..77c7973 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -17,21 +17,22 @@
#pragma once
#include <cstdint>
+#include <deque>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <variant>
-#include <../Fps.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
#include <gui/JankInfo.h>
+#include <gui/LayerMetadata.h>
#include <timestatsproto/TimeStatsHelper.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <ui/FenceTime.h>
#include <utils/String16.h>
#include <utils/Vector.h>
-#include <deque>
-#include <mutex>
-#include <optional>
-#include <unordered_map>
-#include <variant>
+#include <scheduler/Fps.h>
using namespace android::surfaceflinger;
@@ -79,7 +80,7 @@
const std::shared_ptr<FenceTime>& readyFence) = 0;
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
- uid_t uid, nsecs_t postTime, int32_t gameMode) = 0;
+ uid_t uid, nsecs_t postTime, GameMode) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
// Reasons why latching a particular buffer may be skipped
enum class LatchSkipReason {
@@ -101,11 +102,11 @@
// rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+ SetFrameRateVote frameRateVote, GameMode) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence,
Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) = 0;
+ SetFrameRateVote frameRateVote, GameMode) = 0;
// Increments janky frames, blamed to the provided {refreshRate, renderRate, uid, layerName}
// key, with JankMetadata as supplementary reasons for the jank. Because FrameTimeline is the
@@ -123,7 +124,7 @@
std::optional<Fps> renderRate;
uid_t uid = 0;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
int32_t reasons = 0;
nsecs_t displayDeadlineDelta = 0;
nsecs_t displayPresentJitter = 0;
@@ -194,7 +195,7 @@
struct LayerRecord {
uid_t uid;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
// This is the index in timeRecords, at which the timestamps for that
// specific frame are still not fully received. This is not waiting for
// fences to signal, but rather waiting to receive those fences/timestamps.
@@ -247,7 +248,7 @@
const std::shared_ptr<FenceTime>& readyFence) override;
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName, uid_t uid,
- nsecs_t postTime, int32_t gameMode) override;
+ nsecs_t postTime, GameMode) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
void incrementLatchSkipped(int32_t layerId, LatchSkipReason reason) override;
void incrementBadDesiredPresent(int32_t layerId) override;
@@ -256,12 +257,11 @@
void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) override;
void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime,
- Fps displayRefreshRate, std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode) override;
+ Fps displayRefreshRate, std::optional<Fps> renderRate, SetFrameRateVote,
+ GameMode) override;
void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence, Fps displayRefreshRate,
- std::optional<Fps> renderRate, SetFrameRateVote frameRateVote,
- int32_t gameMode) override;
+ std::optional<Fps> renderRate, SetFrameRateVote, GameMode) override;
void incrementJankyFrames(const JankyFramesInfo& info) override;
// Clean up the layer record
@@ -282,11 +282,11 @@
bool populateLayerAtom(std::string* pulledData);
bool recordReadyLocked(int32_t layerId, TimeRecord* timeRecord);
void flushAvailableRecordsToStatsLocked(int32_t layerId, Fps displayRefreshRate,
- std::optional<Fps> renderRate,
- SetFrameRateVote frameRateVote, int32_t gameMode);
+ std::optional<Fps> renderRate, SetFrameRateVote,
+ GameMode);
void flushPowerTimeLocked();
void flushAvailableGlobalRecordsToStatsLocked();
- bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, int32_t gameMode);
+ bool canAddNewAggregatedStats(uid_t uid, const std::string& layerName, GameMode);
void enable();
void disable();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index ffb2f09..69afa2a 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -16,9 +16,10 @@
#include "timestatsproto/TimeStatsHelper.h"
#include <android-base/stringprintf.h>
-#include <inttypes.h>
+#include <ftl/enum.h>
#include <array>
+#include <cinttypes>
#define HISTOGRAM_SIZE 85
@@ -91,51 +92,15 @@
return result;
}
-std::string TimeStatsHelper::SetFrameRateVote::toString(FrameRateCompatibility compatibility) {
- switch (compatibility) {
- case FrameRateCompatibility::Undefined:
- return "Undefined";
- case FrameRateCompatibility::Default:
- return "Default";
- case FrameRateCompatibility::ExactOrMultiple:
- return "ExactOrMultiple";
- }
-}
-
-std::string TimeStatsHelper::SetFrameRateVote::toString(Seamlessness seamlessness) {
- switch (seamlessness) {
- case Seamlessness::Undefined:
- return "Undefined";
- case Seamlessness::ShouldBeSeamless:
- return "ShouldBeSeamless";
- case Seamlessness::NotRequired:
- return "NotRequired";
- }
-}
-
std::string TimeStatsHelper::SetFrameRateVote::toString() const {
std::string result;
StringAppendF(&result, "frameRate = %.2f\n", frameRate);
StringAppendF(&result, "frameRateCompatibility = %s\n",
- toString(frameRateCompatibility).c_str());
- StringAppendF(&result, "seamlessness = %s\n", toString(seamlessness).c_str());
+ ftl::enum_string(frameRateCompatibility).c_str());
+ StringAppendF(&result, "seamlessness = %s\n", ftl::enum_string(seamlessness).c_str());
return result;
}
-std::string TimeStatsHelper::TimeStatsLayer::toString(int32_t gameMode) const {
- switch (gameMode) {
- case TimeStatsHelper::GameModeUnsupported:
- return "GameModeUnsupported";
- case TimeStatsHelper::GameModeStandard:
- return "GameModeStandard";
- case TimeStatsHelper::GameModePerformance:
- return "GameModePerformance";
- case TimeStatsHelper::GameModeBattery:
- return "GameModeBattery";
- default:
- return "GameModeUnspecified";
- }
-}
std::string TimeStatsHelper::TimeStatsLayer::toString() const {
std::string result = "\n";
StringAppendF(&result, "displayRefreshRate = %d fps\n", displayRefreshRateBucket);
@@ -143,7 +108,7 @@
StringAppendF(&result, "uid = %d\n", uid);
StringAppendF(&result, "layerName = %s\n", layerName.c_str());
StringAppendF(&result, "packageName = %s\n", packageName.c_str());
- StringAppendF(&result, "gameMode = %s\n", toString(gameMode).c_str());
+ StringAppendF(&result, "gameMode = %s\n", ftl::enum_string(gameMode).c_str());
StringAppendF(&result, "totalFrames = %d\n", totalFrames);
StringAppendF(&result, "droppedFrames = %d\n", droppedFrames);
StringAppendF(&result, "lateAcquireFrames = %d\n", lateAcquireFrames);
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index 2afff8d..438561c 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -15,6 +15,7 @@
*/
#pragma once
+#include <gui/LayerMetadata.h>
#include <timestatsproto/TimeStatsProtoHeader.h>
#include <utils/Timers.h>
@@ -63,6 +64,8 @@
Undefined = 0,
Default = 1,
ExactOrMultiple = 2,
+
+ ftl_last = ExactOrMultiple
} frameRateCompatibility = FrameRateCompatibility::Undefined;
// Needs to be in sync with atoms.proto
@@ -70,25 +73,13 @@
Undefined = 0,
ShouldBeSeamless = 1,
NotRequired = 2,
+
+ ftl_last = NotRequired
} seamlessness = Seamlessness::Undefined;
- static std::string toString(FrameRateCompatibility);
- static std::string toString(Seamlessness);
std::string toString() const;
};
- /**
- * GameMode of the layer. GameModes are set by SysUI through WMShell.
- * Actual game mode definitions are managed by GameManager.java
- * The values defined here should always be in sync with the ones in GameManager.
- */
- enum GameMode {
- GameModeUnsupported = 0,
- GameModeStandard = 1,
- GameModePerformance = 2,
- GameModeBattery = 3,
- };
-
class TimeStatsLayer {
public:
uid_t uid;
@@ -96,7 +87,7 @@
std::string packageName;
int32_t displayRefreshRateBucket = 0;
int32_t renderRateBucket = 0;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
int32_t totalFrames = 0;
int32_t droppedFrames = 0;
int32_t lateAcquireFrames = 0;
@@ -106,7 +97,6 @@
std::unordered_map<std::string, Histogram> deltas;
std::string toString() const;
- std::string toString(int32_t gameMode) const;
SFTimeStatsLayerProto toProto() const;
};
@@ -137,13 +127,14 @@
struct LayerStatsKey {
uid_t uid = 0;
std::string layerName;
- int32_t gameMode = 0;
+ GameMode gameMode = GameMode::Unsupported;
struct Hasher {
size_t operator()(const LayerStatsKey& key) const {
size_t uidHash = std::hash<uid_t>{}(key.uid);
size_t layerNameHash = std::hash<std::string>{}(key.layerName);
- size_t gameModeHash = std::hash<int32_t>{}(key.gameMode);
+ using T = std::underlying_type_t<GameMode>;
+ size_t gameModeHash = std::hash<T>{}(static_cast<T>(key.gameMode));
return HashCombine(uidHash, HashCombine(layerNameHash, gameModeHash));
}
};
diff --git a/services/surfaceflinger/Tracing/LayerTracing.cpp b/services/surfaceflinger/Tracing/LayerTracing.cpp
index 84890ee..d136e0b 100644
--- a/services/surfaceflinger/Tracing/LayerTracing.cpp
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -53,6 +53,7 @@
mEnabled = false;
LayersTraceFileProto fileProto = createTraceFileProto();
mBuffer->writeToFile(fileProto, FILE_NAME);
+ mBuffer->reset();
return true;
}
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
index d0fb3f2..281cd19 100644
--- a/services/surfaceflinger/Tracing/RingBuffer.h
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -21,8 +21,9 @@
#include <log/log.h>
#include <utils/Errors.h>
-#include <utils/SystemClock.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
+#include <chrono>
#include <queue>
namespace android {
@@ -38,27 +39,27 @@
void setSize(size_t newSize) { mSizeInBytes = newSize; }
EntryProto& front() { return mStorage.front(); }
const EntryProto& front() const { return mStorage.front(); }
+ const EntryProto& back() const { return mStorage.back(); }
- void reset(size_t newSize) {
+ void reset() {
// use the swap trick to make sure memory is released
- std::queue<EntryProto>().swap(mStorage);
- mSizeInBytes = newSize;
+ std::deque<EntryProto>().swap(mStorage);
mUsedInBytes = 0U;
}
- void flush(FileProto& fileProto) {
- fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
- while (!mStorage.empty()) {
- auto entry = fileProto.add_entry();
- entry->Swap(&mStorage.front());
- mStorage.pop();
+
+ void writeToProto(FileProto& fileProto) {
+ fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+ fileProto.entry().size());
+ for (const EntryProto& entry : mStorage) {
+ EntryProto* entryProto = fileProto.add_entry();
+ *entryProto = entry;
}
}
status_t writeToFile(FileProto& fileProto, std::string filename) {
ATRACE_CALL();
+ writeToProto(fileProto);
std::string output;
- flush(fileProto);
- reset(mSizeInBytes);
if (!fileProto.SerializeToString(&output)) {
ALOGE("Could not serialize proto.");
return UNKNOWN_ERROR;
@@ -82,10 +83,10 @@
}
mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
replacedEntries.emplace_back(mStorage.front());
- mStorage.pop();
+ mStorage.pop_front();
}
mUsedInBytes += protoSize;
- mStorage.emplace();
+ mStorage.emplace_back();
mStorage.back().Swap(&proto);
return replacedEntries;
}
@@ -99,14 +100,14 @@
const int64_t durationCount = duration.count();
base::StringAppendF(&result,
" number of entries: %zu (%.2fMB / %.2fMB) duration: %" PRIi64 "ms\n",
- frameCount(), float(used()) / 1024.f * 1024.f,
- float(size()) / 1024.f * 1024.f, durationCount);
+ frameCount(), float(used()) / (1024.f * 1024.f),
+ float(size()) / (1024.f * 1024.f), durationCount);
}
private:
size_t mUsedInBytes = 0U;
size_t mSizeInBytes = 0U;
- std::queue<EntryProto> mStorage;
+ std::deque<EntryProto> mStorage;
};
} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index d1dc076..7e12313 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -43,7 +43,7 @@
}
proto::TransactionState TransactionProtoParser::toProto(
- std::vector<std::pair<int32_t /* layerId */, TracingLayerState>> states) {
+ const std::unordered_map<int32_t /* layerId */, TracingLayerState> states) {
proto::TransactionState proto;
for (auto& [layerId, state] : states) {
proto::LayerState layerProto = toProto(state, nullptr);
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index e8a139f..619ee05 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -51,7 +51,7 @@
static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
DisplayHandleToIdFn getDisplayIdFn);
static proto::TransactionState toProto(
- std::vector<std::pair<int32_t /* layerId */, TracingLayerState>>);
+ const std::unordered_map<int32_t /* layerId */, TracingLayerState>);
static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
new file mode 100644
index 0000000..cf488c2
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TransactionTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "RingBuffer.h"
+#include "TransactionTracing.h"
+
+namespace android {
+
+TransactionTracing::TransactionTracing() {
+ mBuffer = std::make_unique<
+ RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>>();
+}
+
+TransactionTracing::~TransactionTracing() = default;
+
+bool TransactionTracing::enable() {
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ return false;
+ }
+ mBuffer->setSize(mBufferSizeInBytes);
+ mStartingTimestamp = systemTime();
+ mEnabled = true;
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ mDone = false;
+ mThread = std::thread(&TransactionTracing::loop, this);
+ }
+ return true;
+}
+
+bool TransactionTracing::disable() {
+ std::thread thread;
+ {
+ std::scoped_lock lock(mMainThreadLock);
+ mDone = true;
+ mTransactionsAvailableCv.notify_all();
+ thread = std::move(mThread);
+ }
+ if (thread.joinable()) {
+ thread.join();
+ }
+
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+
+ writeToFileLocked();
+ mBuffer->reset();
+ mQueuedTransactions.clear();
+ mStartingStates.clear();
+ mLayerHandles.clear();
+ return true;
+}
+
+bool TransactionTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
+ return mEnabled;
+}
+
+status_t TransactionTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ return writeToFileLocked();
+}
+
+status_t TransactionTracing::writeToFileLocked() {
+ proto::TransactionTraceFile fileProto = createTraceFileProto();
+ addStartingStateToProtoLocked(fileProto);
+ return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void TransactionTracing::setBufferSize(size_t bufferSizeInBytes) {
+ std::scoped_lock lock(mTraceLock);
+ mBufferSizeInBytes = bufferSizeInBytes;
+ mBuffer->setSize(mBufferSizeInBytes);
+}
+
+proto::TransactionTraceFile TransactionTracing::createTraceFileProto() const {
+ proto::TransactionTraceFile proto;
+ proto.set_magic_number(uint64_t(proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ proto::TransactionTraceFile_MagicNumber_MAGIC_NUMBER_L);
+ return proto;
+}
+
+void TransactionTracing::dump(std::string& result) const {
+ std::scoped_lock lock(mTraceLock);
+ base::StringAppendF(&result, "Transaction tracing state: %s\n",
+ mEnabled ? "enabled" : "disabled");
+ base::StringAppendF(&result,
+ " queued transactions=%zu created layers=%zu handles=%zu states=%zu\n",
+ mQueuedTransactions.size(), mCreatedLayers.size(), mLayerHandles.size(),
+ mStartingStates.size());
+ mBuffer->dump(result);
+}
+
+void TransactionTracing::addQueuedTransaction(const TransactionState& transaction) {
+ std::scoped_lock lock(mTraceLock);
+ ATRACE_CALL();
+ if (!mEnabled) {
+ return;
+ }
+ mQueuedTransactions[transaction.id] =
+ TransactionProtoParser::toProto(transaction,
+ std::bind(&TransactionTracing::getLayerIdLocked, this,
+ std::placeholders::_1),
+ nullptr);
+}
+
+void TransactionTracing::addCommittedTransactions(std::vector<TransactionState>& transactions,
+ int64_t vsyncId) {
+ CommittedTransactions committedTransactions;
+ committedTransactions.vsyncId = vsyncId;
+ committedTransactions.timestamp = systemTime();
+ committedTransactions.transactionIds.reserve(transactions.size());
+ for (const auto& transaction : transactions) {
+ committedTransactions.transactionIds.emplace_back(transaction.id);
+ }
+
+ mPendingTransactions.emplace_back(committedTransactions);
+ tryPushToTracingThread();
+}
+
+void TransactionTracing::loop() {
+ while (true) {
+ std::vector<CommittedTransactions> committedTransactions;
+ std::vector<int32_t> removedLayers;
+ {
+ std::unique_lock<std::mutex> lock(mMainThreadLock);
+ base::ScopedLockAssertion assumeLocked(mMainThreadLock);
+ mTransactionsAvailableCv.wait(lock, [&]() REQUIRES(mMainThreadLock) {
+ return mDone || !mCommittedTransactions.empty();
+ });
+ if (mDone) {
+ mCommittedTransactions.clear();
+ mRemovedLayers.clear();
+ break;
+ }
+
+ removedLayers = std::move(mRemovedLayers);
+ mRemovedLayers.clear();
+ committedTransactions = std::move(mCommittedTransactions);
+ mCommittedTransactions.clear();
+ } // unlock mMainThreadLock
+
+ addEntry(committedTransactions, removedLayers);
+ }
+}
+
+void TransactionTracing::addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+ const std::vector<int32_t>& removedLayers) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mTraceLock);
+ std::vector<proto::TransactionTraceEntry> removedEntries;
+ for (const CommittedTransactions& entry : committedTransactions) {
+ proto::TransactionTraceEntry entryProto;
+ entryProto.set_elapsed_realtime_nanos(entry.timestamp);
+ entryProto.set_vsync_id(entry.vsyncId);
+ entryProto.mutable_added_layers()->Reserve(static_cast<int32_t>(mCreatedLayers.size()));
+ for (auto& newLayer : mCreatedLayers) {
+ entryProto.mutable_added_layers()->Add(std::move(newLayer));
+ }
+ entryProto.mutable_removed_layers()->Reserve(static_cast<int32_t>(removedLayers.size()));
+ for (auto& removedLayer : removedLayers) {
+ entryProto.mutable_removed_layers()->Add(removedLayer);
+ }
+ mCreatedLayers.clear();
+ entryProto.mutable_transactions()->Reserve(
+ static_cast<int32_t>(entry.transactionIds.size()));
+ for (const uint64_t& id : entry.transactionIds) {
+ auto it = mQueuedTransactions.find(id);
+ if (it != mQueuedTransactions.end()) {
+ entryProto.mutable_transactions()->Add(std::move(it->second));
+ mQueuedTransactions.erase(it);
+ } else {
+ ALOGE("Could not find transaction id %" PRIu64, id);
+ }
+ }
+ std::vector<proto::TransactionTraceEntry> entries = mBuffer->emplace(std::move(entryProto));
+ removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+ std::make_move_iterator(entries.end()));
+ }
+
+ for (const proto::TransactionTraceEntry& removedEntry : removedEntries) {
+ updateStartingStateLocked(removedEntry);
+ }
+ mTransactionsAddedToBufferCv.notify_one();
+}
+
+void TransactionTracing::flush(int64_t vsyncId) {
+ while (!mPendingTransactions.empty() || !mPendingRemovedLayers.empty()) {
+ tryPushToTracingThread();
+ }
+ std::unique_lock<std::mutex> lock(mTraceLock);
+ base::ScopedLockAssertion assumeLocked(mTraceLock);
+ mTransactionsAddedToBufferCv.wait(lock, [&]() REQUIRES(mTraceLock) {
+ return mBuffer->used() > 0 && mBuffer->back().vsync_id() >= vsyncId;
+ });
+}
+
+void TransactionTracing::onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+ uint32_t flags, int parentId) {
+ std::scoped_lock lock(mTraceLock);
+ TracingLayerCreationArgs args{layerId, name, flags, parentId};
+ mLayerHandles[layerHandle] = layerId;
+ mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+ mPendingRemovedLayers.emplace_back(layerId);
+ tryPushToTracingThread();
+}
+
+void TransactionTracing::tryPushToTracingThread() {
+ // Try to acquire the lock from main thread.
+ if (mMainThreadLock.try_lock()) {
+ // We got the lock! Collect any pending transactions and continue.
+ mCommittedTransactions.insert(mCommittedTransactions.end(),
+ std::make_move_iterator(mPendingTransactions.begin()),
+ std::make_move_iterator(mPendingTransactions.end()));
+ mPendingTransactions.clear();
+ mRemovedLayers.insert(mRemovedLayers.end(), mPendingRemovedLayers.begin(),
+ mPendingRemovedLayers.end());
+ mPendingRemovedLayers.clear();
+ mTransactionsAvailableCv.notify_one();
+ mMainThreadLock.unlock();
+ } else {
+ ALOGV("Couldn't get lock");
+ }
+}
+
+int32_t TransactionTracing::getLayerIdLocked(const sp<IBinder>& layerHandle) {
+ if (layerHandle == nullptr) {
+ return -1;
+ }
+ auto it = mLayerHandles.find(layerHandle->localBinder());
+ return it == mLayerHandles.end() ? -1 : it->second;
+}
+
+void TransactionTracing::updateStartingStateLocked(
+ const proto::TransactionTraceEntry& removedEntry) {
+ // Keep track of layer starting state so we can reconstruct the layer state as we purge
+ // transactions from the buffer.
+ for (const proto::LayerCreationArgs& addedLayer : removedEntry.added_layers()) {
+ TracingLayerState& startingState = mStartingStates[addedLayer.layer_id()];
+ startingState.layerId = addedLayer.layer_id();
+ startingState.name = addedLayer.name();
+ startingState.layerCreationFlags = addedLayer.flags();
+ startingState.parentId = addedLayer.parent_id();
+ }
+
+ // Merge layer states to starting transaction state.
+ for (const proto::TransactionState& transaction : removedEntry.transactions()) {
+ for (const proto::LayerState& layerState : transaction.layer_changes()) {
+ auto it = mStartingStates.find(layerState.layer_id());
+ if (it == mStartingStates.end()) {
+ ALOGE("Could not find layer id %d", layerState.layer_id());
+ continue;
+ }
+ TransactionProtoParser::fromProto(layerState, nullptr, it->second);
+ }
+ }
+
+ // Clean up stale starting states since the layer has been removed and the buffer does not
+ // contain any references to the layer.
+ for (const int32_t removedLayerId : removedEntry.removed_layers()) {
+ auto it = std::find_if(mLayerHandles.begin(), mLayerHandles.end(),
+ [removedLayerId](auto& layer) {
+ return layer.second == removedLayerId;
+ });
+ if (it != mLayerHandles.end()) {
+ mLayerHandles.erase(it);
+ }
+ mStartingStates.erase(removedLayerId);
+ }
+}
+
+void TransactionTracing::addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) {
+ proto::TransactionTraceEntry* entryProto = proto.add_entry();
+ entryProto->set_elapsed_realtime_nanos(mStartingTimestamp);
+ entryProto->set_vsync_id(0);
+ entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+ for (auto& [layerId, state] : mStartingStates) {
+ TracingLayerCreationArgs args{layerId, state.name, state.layerCreationFlags,
+ state.parentId};
+ entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(args));
+ }
+
+ proto::TransactionState transactionProto = TransactionProtoParser::toProto(mStartingStates);
+ transactionProto.set_vsync_id(0);
+ transactionProto.set_post_time(mStartingTimestamp);
+ entryProto->mutable_transactions()->Add(std::move(transactionProto));
+}
+
+proto::TransactionTraceFile TransactionTracing::writeToProto() {
+ std::scoped_lock<std::mutex> lock(mTraceLock);
+ proto::TransactionTraceFile proto = createTraceFileProto();
+ addStartingStateToProtoLocked(proto);
+ mBuffer->writeToProto(proto);
+ return proto;
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.h b/services/surfaceflinger/Tracing/TransactionTracing.h
new file mode 100644
index 0000000..0aa22ed
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <layerproto/TransactionProto.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+#include <thread>
+
+#include "TransactionProtoParser.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+class TransactionTracingTest;
+/*
+ * Records all committed transactions into a ring bufffer.
+ *
+ * Transactions come in via the binder thread. They are serialized to proto
+ * and stored in a map using the transaction id as key. Main thread will
+ * pass the list of transaction ids that are committed every vsync and notify
+ * the tracing thread. The tracing thread will then wake up and add the
+ * committed transactions to the ring buffer.
+ *
+ * When generating SF dump state, we will flush the buffer to a file which
+ * will then be included in the bugreport.
+ *
+ */
+class TransactionTracing {
+public:
+ TransactionTracing();
+ ~TransactionTracing();
+
+ bool enable();
+ bool disable();
+ bool isEnabled() const;
+
+ void addQueuedTransaction(const TransactionState&);
+ void addCommittedTransactions(std::vector<TransactionState>& transactions, int64_t vsyncId);
+ status_t writeToFile();
+ void setBufferSize(size_t bufferSizeInBytes);
+ void onLayerAdded(BBinder* layerHandle, int layerId, const std::string& name, uint32_t flags,
+ int parentId);
+ void onLayerRemoved(int layerId);
+ void dump(std::string&) const;
+ static constexpr auto CONTINUOUS_TRACING_BUFFER_SIZE = 512 * 1024;
+ static constexpr auto ACTIVE_TRACING_BUFFER_SIZE = 100 * 1024 * 1024;
+
+private:
+ friend class TransactionTracingTest;
+
+ static constexpr auto FILE_NAME = "/data/misc/wmtrace/transactions_trace.winscope";
+
+ mutable std::mutex mTraceLock;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ std::unique_ptr<RingBuffer<proto::TransactionTraceFile, proto::TransactionTraceEntry>> mBuffer
+ GUARDED_BY(mTraceLock);
+ size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = CONTINUOUS_TRACING_BUFFER_SIZE;
+ std::unordered_map<uint64_t, proto::TransactionState> mQueuedTransactions
+ GUARDED_BY(mTraceLock);
+ nsecs_t mStartingTimestamp GUARDED_BY(mTraceLock);
+ std::vector<proto::LayerCreationArgs> mCreatedLayers GUARDED_BY(mTraceLock);
+ std::unordered_map<BBinder* /* layerHandle */, int32_t /* layerId */> mLayerHandles
+ GUARDED_BY(mTraceLock);
+ std::unordered_map<int32_t /* layerId */, TracingLayerState> mStartingStates
+ GUARDED_BY(mTraceLock);
+
+ // We do not want main thread to block so main thread will try to acquire mMainThreadLock,
+ // otherwise will push data to temporary container.
+ std::mutex mMainThreadLock;
+ std::thread mThread GUARDED_BY(mMainThreadLock);
+ bool mDone GUARDED_BY(mMainThreadLock) = false;
+ std::condition_variable mTransactionsAvailableCv;
+ std::condition_variable mTransactionsAddedToBufferCv;
+ struct CommittedTransactions {
+ std::vector<uint64_t> transactionIds;
+ int64_t vsyncId;
+ int64_t timestamp;
+ };
+ std::vector<CommittedTransactions> mCommittedTransactions GUARDED_BY(mMainThreadLock);
+ std::vector<CommittedTransactions> mPendingTransactions; // only accessed by main thread
+
+ std::vector<int32_t /* layerId */> mRemovedLayers GUARDED_BY(mMainThreadLock);
+ std::vector<int32_t /* layerId */> mPendingRemovedLayers; // only accessed by main thread
+
+ proto::TransactionTraceFile createTraceFileProto() const;
+ void loop();
+ void addEntry(const std::vector<CommittedTransactions>& committedTransactions,
+ const std::vector<int32_t>& removedLayers) EXCLUDES(mTraceLock);
+ int32_t getLayerIdLocked(const sp<IBinder>& layerHandle) REQUIRES(mTraceLock);
+ void tryPushToTracingThread() EXCLUDES(mMainThreadLock);
+ void addStartingStateToProtoLocked(proto::TransactionTraceFile& proto) REQUIRES(mTraceLock);
+ void updateStartingStateLocked(const proto::TransactionTraceEntry& entry) REQUIRES(mTraceLock);
+ status_t writeToFileLocked() REQUIRES(mTraceLock);
+
+ // TEST
+ // Wait until all the committed transactions for the specified vsync id are added to the buffer.
+ void flush(int64_t vsyncId) EXCLUDES(mMainThreadLock);
+ // Return buffer contents as trace file proto
+ proto::TransactionTraceFile writeToProto() EXCLUDES(mMainThreadLock);
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.cpp b/services/surfaceflinger/TransactionCallbackInvoker.cpp
index f3d46ea..b705d9c 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.cpp
+++ b/services/surfaceflinger/TransactionCallbackInvoker.cpp
@@ -24,6 +24,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "TransactionCallbackInvoker.h"
+#include "BackgroundExecutor.h"
#include <cinttypes>
@@ -49,31 +50,6 @@
return !callbacks.empty() && callbacks.front().type == CallbackId::Type::ON_COMMIT;
}
-TransactionCallbackInvoker::TransactionCallbackInvoker() {
- mThread = std::thread([&]() {
- std::unique_lock lock(mCallbackThreadMutex);
-
- while (mKeepRunning) {
- while (mCallbackThreadWork.size() > 0) {
- mCallbackThreadWork.front()();
- mCallbackThreadWork.pop();
- }
- mCallbackConditionVariable.wait(lock);
- }
- });
-}
-
-TransactionCallbackInvoker::~TransactionCallbackInvoker() {
- {
- std::unique_lock lock(mCallbackThreadMutex);
- mKeepRunning = false;
- mCallbackConditionVariable.notify_all();
- }
- if (mThread.joinable()) {
- mThread.join();
- }
-}
-
void TransactionCallbackInvoker::addEmptyTransaction(const ListenerCallbacks& listenerCallbacks) {
auto& [listener, callbackIds] = listenerCallbacks;
auto& transactionStatsDeque = mCompletedTransactions[listener];
@@ -242,15 +218,10 @@
// keep it as an IBinder due to consistency reasons: if we
// interface_cast at the IPC boundary when reading a Parcel,
// we get pointers that compare unequal in the SF process.
- {
- std::unique_lock lock(mCallbackThreadMutex);
- mCallbackThreadWork.push(
- [stats = std::move(listenerStats)]() {
- interface_cast<ITransactionCompletedListener>(stats.listener)
- ->onTransactionCompleted(stats);
- });
- mCallbackConditionVariable.notify_all();
- }
+ BackgroundExecutor::getInstance().execute([stats = std::move(listenerStats)]() {
+ interface_cast<ITransactionCompletedListener>(stats.listener)
+ ->onTransactionCompleted(stats);
+ });
}
}
completedTransactionsItr++;
diff --git a/services/surfaceflinger/TransactionCallbackInvoker.h b/services/surfaceflinger/TransactionCallbackInvoker.h
index e203d41..5ef5475 100644
--- a/services/surfaceflinger/TransactionCallbackInvoker.h
+++ b/services/surfaceflinger/TransactionCallbackInvoker.h
@@ -61,9 +61,6 @@
class TransactionCallbackInvoker {
public:
- TransactionCallbackInvoker();
- ~TransactionCallbackInvoker();
-
status_t addCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
const std::vector<JankData>& jankData);
status_t addOnCommitCallbackHandles(const std::deque<sp<CallbackHandle>>& handles,
@@ -94,12 +91,6 @@
mCompletedTransactions;
sp<Fence> mPresentFence;
-
- std::mutex mCallbackThreadMutex;
- std::condition_variable mCallbackConditionVariable;
- std::thread mThread;
- bool mKeepRunning = true;
- std::queue<std::function<void()>> mCallbackThreadWork;
};
} // namespace android
diff --git a/services/surfaceflinger/layerproto/display.proto b/services/surfaceflinger/layerproto/display.proto
index ee8830e..c8cd926 100644
--- a/services/surfaceflinger/layerproto/display.proto
+++ b/services/surfaceflinger/layerproto/display.proto
@@ -33,4 +33,6 @@
RectProto layer_stack_space_rect = 5;
TransformProto transform = 6;
+
+ bool is_virtual = 7;
}
diff --git a/services/surfaceflinger/layerproto/transactions.proto b/services/surfaceflinger/layerproto/transactions.proto
index edeacfa..10222cc 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -42,8 +42,10 @@
int64 elapsed_realtime_nanos = 1;
int64 vsync_id = 2;
repeated TransactionState transactions = 3;
- repeated LayerCreationArgs new_layers = 4;
- repeated DisplayState new_displays = 5;
+ repeated LayerCreationArgs added_layers = 4;
+ repeated int32 removed_layers = 5;
+ repeated DisplayState added_displays = 6;
+ repeated int32 removed_displays = 7;
}
message LayerCreationArgs {
diff --git a/services/surfaceflinger/tests/WindowInfosListener_test.cpp b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
index 0069111..bb52245 100644
--- a/services/surfaceflinger/tests/WindowInfosListener_test.cpp
+++ b/services/surfaceflinger/tests/WindowInfosListener_test.cpp
@@ -112,11 +112,12 @@
sp<SurfaceControl> surfaceControl =
mClient->createSurface(String8(name.c_str()), 100, 100, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eFXSurfaceBufferState);
-
+ const Rect crop(0, 0, 100, 100);
Transaction()
.setLayerStack(surfaceControl, ui::DEFAULT_LAYER_STACK)
.show(surfaceControl)
.setLayer(surfaceControl, INT32_MAX - 1)
+ .setCrop(surfaceControl, crop)
.setInputWindowInfo(surfaceControl, windowInfo)
.apply();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 3dc6d8b..561d9e2 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -71,6 +71,7 @@
"MessageQueueTest.cpp",
"SurfaceFlinger_CreateDisplayTest.cpp",
"SurfaceFlinger_DestroyDisplayTest.cpp",
+ "SurfaceFlinger_DisplayModeSwitching.cpp",
"SurfaceFlinger_DisplayTransactionCommitTest.cpp",
"SurfaceFlinger_GetDisplayNativePrimariesTest.cpp",
"SurfaceFlinger_HotplugTest.cpp",
@@ -93,6 +94,7 @@
"TransactionFrameTracerTest.cpp",
"TransactionProtoParserTest.cpp",
"TransactionSurfaceFrameTest.cpp",
+ "TransactionTracingTest.cpp",
"TunnelModeEnabledReporterTest.cpp",
"StrongTypingTest.cpp",
"VSyncDispatchTimerQueueTest.cpp",
@@ -135,8 +137,9 @@
"libgui_mocks",
"liblayers_proto",
"libperfetto_client_experimental",
- "librenderengine_mocks",
"librenderengine",
+ "librenderengine_mocks",
+ "libscheduler",
"libserviceutils",
"libtimestats",
"libtimestats_atoms_proto",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 8d2c078..f1e6e48 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -138,7 +138,7 @@
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
- constexpr ISchedulerCallback* kCallback = nullptr;
+ constexpr scheduler::ISchedulerCallback* kCallback = nullptr;
constexpr bool kHasMultipleConfigs = true;
mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
std::move(eventThread), std::move(sfEventThread), kCallback,
@@ -370,7 +370,7 @@
EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_HWC))
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc))
.Times(1);
}
@@ -384,7 +384,7 @@
static void setupRECompositionCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
+ prepareFrame(compositionengine::DisplaySurface::CompositionType::Gpu))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
index d4cfbbb..5a0033e 100644
--- a/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayDevice_InitiateModeChange.cpp
@@ -29,7 +29,7 @@
class InitiateModeChangeTest : public DisplayTransactionTest {
public:
- using Event = scheduler::RefreshRateConfigEvent;
+ using Event = scheduler::DisplayModeEvent;
void SetUp() override {
injectFakeBufferQueueFactory();
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index de5e9df..0a3437a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -121,7 +121,7 @@
mock::VsyncController* mVsyncController = new mock::VsyncController;
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker;
- mock::SchedulerCallback mSchedulerCallback;
+ scheduler::mock::SchedulerCallback mSchedulerCallback;
mock::EventThread* mEventThread = new mock::EventThread;
mock::EventThread* mSFEventThread = new mock::EventThread;
diff --git a/services/surfaceflinger/tests/unittests/FpsOps.h b/services/surfaceflinger/tests/unittests/FpsOps.h
index 23c2841..7c737dc 100644
--- a/services/surfaceflinger/tests/unittests/FpsOps.h
+++ b/services/surfaceflinger/tests/unittests/FpsOps.h
@@ -16,7 +16,7 @@
#pragma once
-#include "Fps.h"
+#include <scheduler/Fps.h>
namespace android {
diff --git a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
index cd2fc74..bb1f432 100644
--- a/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsReporterTest.cpp
@@ -17,6 +17,8 @@
#undef LOG_TAG
#define LOG_TAG "FpsReporterTest"
+#include <chrono>
+
#include <android/gui/BnFpsListener.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -36,6 +38,8 @@
namespace android {
+using namespace std::chrono_literals;
+
using testing::_;
using testing::DoAll;
using testing::Mock;
diff --git a/services/surfaceflinger/tests/unittests/FpsTest.cpp b/services/surfaceflinger/tests/unittests/FpsTest.cpp
index b44dd89..88b74d2 100644
--- a/services/surfaceflinger/tests/unittests/FpsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FpsTest.cpp
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-#include "Fps.h"
-#include "FpsOps.h"
-
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <scheduler/Fps.h>
+
+#include "FpsOps.h"
+
namespace android {
TEST(FpsTest, construct) {
diff --git a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
index 9fbaece..397c619 100644
--- a/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTimelineTest.cpp
@@ -169,13 +169,14 @@
static const std::string sLayerNameOne = "layer1";
static const std::string sLayerNameTwo = "layer2";
-static constexpr const uid_t sUidOne = 0;
-static constexpr pid_t sPidOne = 10;
-static constexpr pid_t sPidTwo = 20;
-static constexpr int32_t sInputEventId = 5;
-static constexpr int32_t sLayerIdOne = 1;
-static constexpr int32_t sLayerIdTwo = 2;
-static constexpr int32_t sGameMode = 0;
+
+constexpr const uid_t sUidOne = 0;
+constexpr pid_t sPidOne = 10;
+constexpr pid_t sPidTwo = 20;
+constexpr int32_t sInputEventId = 5;
+constexpr int32_t sLayerIdOne = 1;
+constexpr int32_t sLayerIdTwo = 2;
+constexpr GameMode sGameMode = GameMode::Unsupported;
TEST_F(FrameTimelineTest, tokenManagerRemovesStalePredictions) {
int64_t token1 = mTokenManager->generateTokenForPredictions({0, 0, 0});
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
index d645942..981ca1d 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -91,8 +91,8 @@
}
// Mocks the behavior of applying a transaction from WMShell
- void setGameModeMetadata(sp<Layer> layer, int gameMode) {
- mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode);
+ void setGameModeMetadata(sp<Layer> layer, GameMode gameMode) {
+ mLayerMetadata.setInt32(METADATA_GAME_MODE, static_cast<int32_t>(gameMode));
layer->setMetadata(mLayerMetadata);
layer->setGameModeForTree(gameMode);
}
@@ -109,51 +109,52 @@
sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
rootLayer->addChild(childLayer1);
rootLayer->addChild(childLayer2);
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer1->getGameMode(), 2);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer1->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
}
TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
rootLayer->addChild(childLayer);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
}
TEST_F(GameModeTest, RemoveChildResetsGameMode) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 2);
+ rootLayer->setGameModeForTree(GameMode::Performance);
rootLayer->addChild(childLayer);
- EXPECT_EQ(rootLayer->getGameMode(), 2);
- EXPECT_EQ(childLayer->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Performance);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Performance);
rootLayer->removeChild(childLayer);
- EXPECT_EQ(childLayer->getGameMode(), 0);
+ EXPECT_EQ(childLayer->getGameMode(), GameMode::Unsupported);
}
TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
sp<BufferStateLayer> rootLayer = createBufferStateLayer();
sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
- rootLayer->setGameModeForTree(/*gameMode*/ 1);
+ rootLayer->setGameModeForTree(GameMode::Standard);
rootLayer->addChild(childLayer1);
- setGameModeMetadata(childLayer2, /*gameMode*/ 2);
+ setGameModeMetadata(childLayer2, GameMode::Performance);
rootLayer->addChild(childLayer2);
- EXPECT_EQ(rootLayer->getGameMode(), 1);
- EXPECT_EQ(childLayer1->getGameMode(), 1);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(rootLayer->getGameMode(), GameMode::Standard);
+ EXPECT_EQ(childLayer1->getGameMode(), GameMode::Standard);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
rootLayer->removeChild(childLayer2);
- EXPECT_EQ(childLayer2->getGameMode(), 2);
+ EXPECT_EQ(childLayer2->getGameMode(), GameMode::Performance);
}
-} // namespace android
\ No newline at end of file
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index 4993a2d..00687ad 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -38,9 +38,9 @@
using testing::Return;
using testing::ReturnRef;
-namespace android {
+namespace android::scheduler {
-namespace scheduler {
+using MockLayer = android::mock::MockLayer;
class LayerHistoryTest : public testing::Test {
protected:
@@ -93,12 +93,12 @@
}
}
- auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
+ auto createLayer() { return sp<MockLayer>::make(mFlinger.flinger()); }
auto createLayer(std::string name) {
- return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger(), std::move(name)));
+ return sp<MockLayer>::make(mFlinger.flinger(), std::move(name));
}
- void recordFramesAndExpect(const sp<mock::MockLayer>& layer, nsecs_t& time, Fps frameRate,
+ void recordFramesAndExpect(const sp<MockLayer>& layer, nsecs_t& time, Fps frameRate,
Fps desiredRefreshRate, int numFrames) {
LayerHistory::Summary summary;
for (int i = 0; i < numFrames; i++) {
@@ -768,8 +768,7 @@
::testing::Values(1s, 2s, 3s, 4s, 5s));
} // namespace
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wextra"
diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
index f25994e..5c2d2e1 100644
--- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp
@@ -19,7 +19,8 @@
#include <gtest/gtest.h>
-#include "Fps.h"
+#include <scheduler/Fps.h>
+
#include "FpsOps.h"
#include "Scheduler/LayerHistory.h"
#include "Scheduler/LayerInfo.h"
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index fc84d48..98746bc 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -21,10 +21,9 @@
#undef LOG_TAG
#define LOG_TAG "SchedulerUnittests"
+#include <ftl/enum.h>
#include <gmock/gmock.h>
#include <log/log.h>
-#include <thread>
-
#include <ui/Size.h>
#include "DisplayHardware/HWC2.h"
@@ -1975,7 +1974,7 @@
layers[0].vote = vote;
EXPECT_EQ(fps.getIntValue(),
refreshRateConfigs->getBestRefreshRate(layers, {}).getFps().getIntValue())
- << "Failed for " << RefreshRateConfigs::layerVoteTypeString(vote);
+ << "Failed for " << ftl::enum_string(vote);
};
for (int fps = kMinRefreshRate; fps < kMaxRefreshRate; fps++) {
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index e558f3b..f48abb7 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -28,12 +28,16 @@
#include "mock/MockLayer.h"
#include "mock/MockSchedulerCallback.h"
+namespace android::scheduler {
+
using testing::_;
using testing::Return;
-namespace android {
namespace {
+using MockEventThread = android::mock::EventThread;
+using MockLayer = android::mock::MockLayer;
+
constexpr PhysicalDisplayId PHYSICAL_DISPLAY_ID = PhysicalDisplayId::fromPort(255u);
class SchedulerTest : public testing::Test {
@@ -44,9 +48,9 @@
: EventThreadConnection(eventThread, /*callingUid=*/0, ResyncCallback()) {}
~MockEventThreadConnection() = default;
- MOCK_METHOD1(stealReceiveChannel, status_t(gui::BitTube* outChannel));
- MOCK_METHOD1(setVsyncRate, status_t(uint32_t count));
- MOCK_METHOD0(requestNextVsync, void());
+ MOCK_METHOD1(stealReceiveChannel, binder::Status(gui::BitTube* outChannel));
+ MOCK_METHOD1(setVsyncRate, binder::Status(int count));
+ MOCK_METHOD0(requestNextVsync, binder::Status());
};
SchedulerTest();
@@ -64,21 +68,21 @@
.setGroup(0)
.build();
- std::shared_ptr<scheduler::RefreshRateConfigs> mConfigs =
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
+ std::shared_ptr<RefreshRateConfigs> mConfigs =
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60}, mode60->getId());
mock::SchedulerCallback mSchedulerCallback;
TestableScheduler* mScheduler = new TestableScheduler{mConfigs, mSchedulerCallback};
- Scheduler::ConnectionHandle mConnectionHandle;
- mock::EventThread* mEventThread;
+ ConnectionHandle mConnectionHandle;
+ MockEventThread* mEventThread;
sp<MockEventThreadConnection> mEventThreadConnection;
TestableSurfaceFlinger mFlinger;
};
SchedulerTest::SchedulerTest() {
- auto eventThread = std::make_unique<mock::EventThread>();
+ auto eventThread = std::make_unique<MockEventThread>();
mEventThread = eventThread.get();
EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)).WillOnce(Return(0));
@@ -98,7 +102,7 @@
} // namespace
TEST_F(SchedulerTest, invalidConnectionHandle) {
- Scheduler::ConnectionHandle handle;
+ ConnectionHandle handle;
const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
@@ -155,7 +159,7 @@
TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSupported) {
// The layer is registered at creation time and deregistered at destruction time.
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
// recordLayerHistory should be a noop
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
@@ -174,24 +178,22 @@
TEST_F(SchedulerTest, updateDisplayModes) {
ASSERT_EQ(0u, mScheduler->layerHistorySize());
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
ASSERT_EQ(1u, mScheduler->layerHistorySize());
mScheduler->setRefreshRateConfigs(
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
- mode60->getId()));
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
ASSERT_EQ(1u, mScheduler->getNumActiveLayers());
}
-TEST_F(SchedulerTest, testDispatchCachedReportedMode) {
- // If the optional fields are cleared, the function should return before
- // onModeChange is called.
- mScheduler->clearOptionalFieldsInFeatures();
- EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
+TEST_F(SchedulerTest, dispatchCachedReportedMode) {
+ mScheduler->clearCachedReportedMode();
+
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
+ EXPECT_NO_FATAL_FAILURE(mScheduler->dispatchCachedReportedMode());
}
TEST_F(SchedulerTest, onNonPrimaryDisplayModeChanged_invalidParameters) {
@@ -203,7 +205,7 @@
// If the handle is incorrect, the function should return before
// onModeChange is called.
- Scheduler::ConnectionHandle invalidHandle = {.id = 123};
+ ConnectionHandle invalidHandle = {.id = 123};
EXPECT_NO_FATAL_FAILURE(mScheduler->onNonPrimaryDisplayModeChanged(invalidHandle, mode));
EXPECT_CALL(*mEventThread, onModeChanged(_)).Times(0);
}
@@ -224,10 +226,9 @@
TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) {
mScheduler->setRefreshRateConfigs(
- std::make_shared<scheduler::RefreshRateConfigs>(DisplayModes{mode60, mode120},
- mode60->getId()));
+ std::make_shared<RefreshRateConfigs>(DisplayModes{mode60, mode120}, mode60->getId()));
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
+ sp<MockLayer> layer = sp<MockLayer>::make(mFlinger.flinger());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
@@ -241,4 +242,4 @@
mScheduler->chooseRefreshRateForContent();
}
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
index eed62a7..fe5f9e0 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -48,6 +48,8 @@
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using scheduler::LayerHistory;
+
using FrameRate = Layer::FrameRate;
using FrameRateCompatibility = Layer::FrameRateCompatibility;
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
new file mode 100644
index 0000000..9796a70
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mock/MockEventThread.h"
+#undef LOG_TAG
+#define LOG_TAG "LibSurfaceFlingerUnittests"
+
+#include "DisplayTransactionTestHelpers.h"
+
+#include <scheduler/Fps.h>
+
+namespace android {
+namespace {
+
+using android::hardware::graphics::composer::V2_4::Error;
+using android::hardware::graphics::composer::V2_4::VsyncPeriodChangeTimeline;
+
+class DisplayModeSwitchingTest : public DisplayTransactionTest {
+public:
+ void SetUp() override {
+ injectFakeBufferQueueFactory();
+ injectFakeNativeWindowSurfaceFactory();
+
+ PrimaryDisplayVariant::setupHwcHotplugCallExpectations(this);
+ PrimaryDisplayVariant::setupFramebufferConsumerBufferQueueCallExpectations(this);
+ PrimaryDisplayVariant::setupFramebufferProducerBufferQueueCallExpectations(this);
+ PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+ PrimaryDisplayVariant::setupHwcGetActiveConfigCallExpectations(this);
+
+ mFlinger.onComposerHalHotplug(PrimaryDisplayVariant::HWC_DISPLAY_ID, Connection::CONNECTED);
+
+ mDisplay = PrimaryDisplayVariant::makeFakeExistingDisplayInjector(this)
+ .setSupportedModes({kDisplayMode60, kDisplayMode90, kDisplayMode120,
+ kDisplayMode90DifferentResolution})
+ .setActiveMode(kDisplayModeId60)
+ .inject();
+
+ setupScheduler();
+
+ // isVsyncPeriodSwitchSupported should return true, otherwise the SF's HWC proxy
+ // will call setActiveConfig instead of setActiveConfigWithConstraints.
+ ON_CALL(*mComposer, isVsyncPeriodSwitchSupported()).WillByDefault(Return(true));
+ }
+
+protected:
+ void setupScheduler();
+ void testChangeRefreshRate(bool isDisplayActive, bool isRefreshRequired);
+
+ sp<DisplayDevice> mDisplay;
+ mock::EventThread* mAppEventThread;
+
+ const DisplayModeId kDisplayModeId60 = DisplayModeId(0);
+ const DisplayModePtr kDisplayMode60 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId60.value()))
+ .setId(kDisplayModeId60)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((60_Hz).getPeriodNsecs())
+ .setGroup(0)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId90 = DisplayModeId(1);
+ const DisplayModePtr kDisplayMode90 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90.value()))
+ .setId(kDisplayModeId90)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((90_Hz).getPeriodNsecs())
+ .setGroup(1)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId120 = DisplayModeId(2);
+ const DisplayModePtr kDisplayMode120 =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId120.value()))
+ .setId(kDisplayModeId120)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((120_Hz).getPeriodNsecs())
+ .setGroup(2)
+ .setHeight(1000)
+ .setWidth(1000)
+ .build();
+
+ const DisplayModeId kDisplayModeId90DifferentResolution = DisplayModeId(3);
+ const DisplayModePtr kDisplayMode90DifferentResolution =
+ DisplayMode::Builder(hal::HWConfigId(kDisplayModeId90DifferentResolution.value()))
+ .setId(kDisplayModeId90DifferentResolution)
+ .setPhysicalDisplayId(PrimaryDisplayVariant::DISPLAY_ID::get())
+ .setVsyncPeriod((90_Hz).getPeriodNsecs())
+ .setGroup(3)
+ .setHeight(2000)
+ .setWidth(2000)
+ .build();
+};
+
+void DisplayModeSwitchingTest::setupScheduler() {
+ auto eventThread = std::make_unique<mock::EventThread>();
+ mAppEventThread = eventThread.get();
+ auto sfEventThread = std::make_unique<mock::EventThread>();
+
+ EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*eventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+ EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+ .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+ ResyncCallback())));
+
+ auto vsyncController = std::make_unique<mock::VsyncController>();
+ auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ EXPECT_CALL(*vsyncTracker, currentPeriod())
+ .WillRepeatedly(
+ Return(TestableSurfaceFlinger::FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+ mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+ std::move(eventThread), std::move(sfEventThread), /*callback*/ nullptr,
+ /*hasMultipleModes*/ true);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ Mock::VerifyAndClearExpectations(mComposer);
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that the next commit will complete the mode change and send
+ // a onModeChanged event to the framework.
+
+ EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+ mFlinger.commit();
+ Mock::VerifyAndClearExpectations(mAppEventThread);
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeRefreshRate_OnActiveDisplay_WithoutRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), true, 0.f, 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ // and complete the mode change.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mAppEventThread, onModeChanged(kDisplayMode90));
+
+ mFlinger.commit();
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90);
+}
+
+TEST_F(DisplayModeSwitchingTest, twoConsecutiveSetDesiredDisplayModeSpecs) {
+ // Test that if we call setDesiredDisplayModeSpecs while a previous mode change
+ // is still being processed the later call will be respected.
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90.value(), false, 0.f, 120.f, 0.f, 120.f);
+
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = true};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId90.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId120.value(), false, 0.f, 180.f, 0.f, 180.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(kDisplayModeId120.value()), _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ mFlinger.commit();
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId120);
+
+ mFlinger.commit();
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId120);
+}
+
+TEST_F(DisplayModeSwitchingTest, changeResolution_OnActiveDisplay_WithoutRefreshRequired) {
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ mFlinger.onActiveDisplayChanged(mDisplay);
+
+ mFlinger.setDesiredDisplayModeSpecs(mDisplay->getDisplayToken().promote(),
+ kDisplayModeId90DifferentResolution.value(), false, 0.f,
+ 120.f, 0.f, 120.f);
+
+ ASSERT_TRUE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getDesiredActiveMode()->mode->getId(), kDisplayModeId90DifferentResolution);
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId60);
+
+ // Verify that next commit will call setActiveConfigWithConstraints in HWC
+ // and complete the mode change.
+ const VsyncPeriodChangeTimeline timeline{.refreshRequired = false};
+ EXPECT_CALL(*mComposer,
+ setActiveConfigWithConstraints(PrimaryDisplayVariant::HWC_DISPLAY_ID,
+ hal::HWConfigId(
+ kDisplayModeId90DifferentResolution.value()),
+ _, _))
+ .WillOnce(DoAll(SetArgPointee<3>(timeline), Return(Error::NONE)));
+
+ EXPECT_CALL(*mAppEventThread, onHotplugReceived(mDisplay->getPhysicalId(), true));
+
+ // Misc expecations. We don't need to enforce these method calls, but since the helper methods
+ // already set expectations we should add new ones here, otherwise the test will fail.
+ EXPECT_CALL(*mConsumer, setDefaultBufferSize(2000, 2000)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mConsumer, consumerConnect(_, false)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(*mComposer, setClientTargetSlotCount(_)).WillOnce(Return(hal::Error::NONE));
+
+ // Create a new native surface to be used by the recreated display.
+ mNativeWindowSurface = nullptr;
+ injectFakeNativeWindowSurfaceFactory();
+ PrimaryDisplayVariant::setupNativeWindowSurfaceCreationCallExpectations(this);
+
+ const auto displayToken = mDisplay->getDisplayToken().promote();
+
+ mFlinger.commit();
+
+ // The DisplayDevice will be destroyed and recreated,
+ // so we need to update with the new instance.
+ mDisplay = mFlinger.getDisplay(displayToken);
+
+ ASSERT_FALSE(mDisplay->getDesiredActiveMode().has_value());
+ ASSERT_EQ(mDisplay->getActiveMode()->getId(), kDisplayModeId90DifferentResolution);
+}
+
+} // namespace
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
index 69e0501..ec7e8a7 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_NotifyPowerBoostTest.cpp
@@ -17,6 +17,9 @@
#undef LOG_TAG
#define LOG_TAG "LibSurfaceFlingerUnittests"
+#include <chrono>
+#include <thread>
+
#include "DisplayTransactionTestHelpers.h"
#include <android/hardware/power/Boost.h>
@@ -27,6 +30,8 @@
using android::hardware::power::Boost;
TEST_F(DisplayTransactionTest, notifyPowerBoostNotifiesTouchEvent) {
+ using namespace std::chrono_literals;
+
mFlinger.scheduler()->replaceTouchTimer(100);
std::this_thread::sleep_for(10ms); // wait for callback to be triggered
EXPECT_TRUE(mFlinger.scheduler()->isTouchActive()); // Starting timer activates touch
@@ -47,4 +52,4 @@
}
} // namespace
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 9d1fc98..dabd2d2 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,22 +28,20 @@
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
-namespace android {
+namespace android::scheduler {
class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(std::shared_ptr<scheduler::RefreshRateConfigs> configs,
- ISchedulerCallback& callback)
+ TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
std::make_unique<mock::VSyncTracker>(), std::move(configs),
callback) {}
- TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
- std::shared_ptr<scheduler::RefreshRateConfigs> configs,
- ISchedulerCallback& callback)
- : Scheduler(*this, callback, {.useContentDetection = true}) {
- mVsyncSchedule = {std::move(vsyncController), std::move(vsyncTracker), nullptr};
+ TestableScheduler(std::unique_ptr<VsyncController> controller,
+ std::unique_ptr<VSyncTracker> tracker,
+ std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
+ : Scheduler(*this, callback, Feature::kContentDetection) {
+ mVsyncSchedule.emplace(VsyncSchedule(std::move(tracker), nullptr, std::move(controller)));
setRefreshRateConfigs(std::move(configs));
ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
@@ -86,33 +84,24 @@
}
bool isTouchActive() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- return mFeatures.touch == Scheduler::TouchState::Active;
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ return mPolicy.touch == Scheduler::TouchState::Active;
}
void dispatchCachedReportedMode() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ std::lock_guard<std::mutex> lock(mPolicyLock);
return Scheduler::dispatchCachedReportedMode();
}
- void clearOptionalFieldsInFeatures() {
- std::lock_guard<std::mutex> lock(mFeatureStateLock);
- mFeatures.cachedModeChangedParams.reset();
+ void clearCachedReportedMode() {
+ std::lock_guard<std::mutex> lock(mPolicyLock);
+ mPolicy.cachedModeChangedParams.reset();
}
void onNonPrimaryDisplayModeChanged(ConnectionHandle handle, DisplayModePtr mode) {
return Scheduler::onNonPrimaryDisplayModeChanged(handle, mode);
}
- ~TestableScheduler() {
- // All these pointer and container clears help ensure that GMock does
- // not report a leaked object, since the Scheduler instance may
- // still be referenced by something despite our best efforts to destroy
- // it after each test is done.
- mVsyncSchedule.controller.reset();
- mConnections.clear();
- }
-
private:
// ICompositor overrides:
bool commit(nsecs_t, int64_t, nsecs_t) override { return false; }
@@ -120,4 +109,4 @@
void sample() override {}
};
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 4c5789e..100a78d 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -24,6 +24,7 @@
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <compositionengine/mock/DisplaySurface.h>
#include <gui/ScreenCaptureResults.h>
+#include <algorithm>
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -169,12 +170,12 @@
} // namespace surfaceflinger::test
-class TestableSurfaceFlinger final : private ISchedulerCallback {
+class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback {
public:
using HotplugEvent = SurfaceFlinger::HotplugEvent;
SurfaceFlinger* flinger() { return mFlinger.get(); }
- TestableScheduler* scheduler() { return mScheduler; }
+ scheduler::TestableScheduler* scheduler() { return mScheduler; }
// Extend this as needed for accessing SurfaceFlinger private (and public)
// functions.
@@ -197,7 +198,8 @@
std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread,
- ISchedulerCallback* callback = nullptr, bool hasMultipleModes = false) {
+ scheduler::ISchedulerCallback* callback = nullptr,
+ bool hasMultipleModes = false) {
DisplayModes modes{DisplayMode::Builder(0)
.setId(DisplayModeId(0))
.setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
@@ -224,17 +226,18 @@
std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mTimeStats, currFps,
/*powerMode=*/hal::PowerMode::OFF);
- mScheduler = new TestableScheduler(std::move(vsyncController), std::move(vsyncTracker),
- mRefreshRateConfigs, *(callback ?: this));
+ mScheduler = new scheduler::TestableScheduler(std::move(vsyncController),
+ std::move(vsyncTracker), mRefreshRateConfigs,
+ *(callback ?: this));
mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread));
mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread));
resetScheduler(mScheduler);
}
- void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
+ void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
- TestableScheduler& mutableScheduler() const { return *mScheduler; }
+ scheduler::TestableScheduler& mutableScheduler() const { return *mScheduler; }
using CreateBufferQueueFunction = surfaceflinger::test::Factory::CreateBufferQueueFunction;
void setCreateBufferQueueFunction(CreateBufferQueueFunction f) {
@@ -305,6 +308,11 @@
return mFlinger->destroyDisplay(displayToken);
}
+ auto getDisplay(const sp<IBinder>& displayToken) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ return mFlinger->getDisplayDeviceLocked(displayToken);
+ }
+
void enableHalVirtualDisplays(bool enable) { mFlinger->enableHalVirtualDisplays(enable); }
auto setupNewDisplayDeviceInternal(
@@ -380,7 +388,7 @@
listenerCallbacks, transactionId);
}
- auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(); };
+ auto flushTransactionQueues() { return mFlinger->flushTransactionQueues(0); };
auto onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
return mFlinger->onTransact(code, data, reply, flags);
@@ -393,6 +401,21 @@
return SurfaceFlinger::calculateMaxAcquiredBufferCount(refreshRate, presentLatency);
}
+ auto setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken, ui::DisplayModeId defaultMode,
+ bool allowGroupSwitching, float primaryRefreshRateMin,
+ float primaryRefreshRateMax, float appRequestRefreshRateMin,
+ float appRequestRefreshRateMax) {
+ return mFlinger->setDesiredDisplayModeSpecs(displayToken, defaultMode, allowGroupSwitching,
+ primaryRefreshRateMin, primaryRefreshRateMax,
+ appRequestRefreshRateMin,
+ appRequestRefreshRateMax);
+ }
+
+ void onActiveDisplayChanged(const sp<DisplayDevice>& activeDisplay) {
+ Mutex::Autolock lock(mFlinger->mStateLock);
+ mFlinger->onActiveDisplayChangedLocked(activeDisplay);
+ }
+
/* ------------------------------------------------------------------------
* Read-only access to private data to assert post-conditions.
*/
@@ -480,7 +503,7 @@
static constexpr hal::HWDisplayId DEFAULT_HWC_DISPLAY_ID = 1000;
static constexpr int32_t DEFAULT_WIDTH = 1920;
static constexpr int32_t DEFAULT_HEIGHT = 1280;
- static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'666;
+ static constexpr int32_t DEFAULT_VSYNC_PERIOD = 16'666'667;
static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr hal::HWConfigId DEFAULT_ACTIVE_CONFIG = 0;
@@ -634,10 +657,10 @@
mCreationArgs.connectionType = connectionType;
mCreationArgs.isPrimary = isPrimary;
- mActiveModeId = DisplayModeId(0);
+ mCreationArgs.activeModeId = DisplayModeId(0);
DisplayModePtr activeMode =
DisplayMode::Builder(FakeHwcDisplayInjector::DEFAULT_ACTIVE_CONFIG)
- .setId(mActiveModeId)
+ .setId(mCreationArgs.activeModeId)
.setPhysicalDisplayId(PhysicalDisplayId::fromPort(0))
.setWidth(FakeHwcDisplayInjector::DEFAULT_WIDTH)
.setHeight(FakeHwcDisplayInjector::DEFAULT_HEIGHT)
@@ -673,7 +696,7 @@
auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
auto& setActiveMode(DisplayModeId mode) {
- mActiveModeId = mode;
+ mCreationArgs.activeModeId = mode;
return *this;
}
@@ -728,14 +751,29 @@
const auto physicalId = PhysicalDisplayId::tryCast(*displayId);
LOG_ALWAYS_FATAL_IF(!physicalId);
LOG_ALWAYS_FATAL_IF(!mHwcDisplayId);
- state.physical = {.id = *physicalId, .type = *type, .hwcDisplayId = *mHwcDisplayId};
+
+ const DisplayModePtr activeModePtr =
+ *std::find_if(mCreationArgs.supportedModes.begin(),
+ mCreationArgs.supportedModes.end(), [&](DisplayModePtr mode) {
+ return mode->getId() == mCreationArgs.activeModeId;
+ });
+ state.physical = {.id = *physicalId,
+ .type = *type,
+ .hwcDisplayId = *mHwcDisplayId,
+ .deviceProductInfo = {},
+ .supportedModes = mCreationArgs.supportedModes,
+ .activeMode = activeModePtr};
}
state.isSecure = mCreationArgs.isSecure;
+ mCreationArgs.refreshRateConfigs =
+ std::make_shared<scheduler::RefreshRateConfigs>(mCreationArgs.supportedModes,
+ mCreationArgs.activeModeId);
+
sp<DisplayDevice> device = new DisplayDevice(mCreationArgs);
if (!device->isVirtual()) {
- device->setActiveMode(mActiveModeId);
+ device->setActiveMode(mCreationArgs.activeModeId);
}
mFlinger.mutableDisplays().emplace(mDisplayToken, device);
mFlinger.mutableCurrentState().displays.add(mDisplayToken, state);
@@ -753,19 +791,18 @@
sp<BBinder> mDisplayToken = new BBinder();
DisplayDeviceCreationArgs mCreationArgs;
const std::optional<hal::HWDisplayId> mHwcDisplayId;
- DisplayModeId mActiveModeId;
};
private:
void scheduleComposite(FrameHint) override {}
void setVsyncEnabled(bool) override {}
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override {}
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() {}
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
- TestableScheduler* mScheduler = nullptr;
+ scheduler::TestableScheduler* mScheduler = nullptr;
std::shared_ptr<scheduler::RefreshRateConfigs> mRefreshRateConfigs;
};
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 1487a96..0ef8456 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -70,9 +70,9 @@
#define NUM_LAYERS 1
#define NUM_LAYERS_INVALID "INVALID"
-const constexpr Fps kRefreshRate0 = 61_Hz;
-const constexpr Fps kRenderRate0 = 31_Hz;
-static constexpr int32_t kGameMode = TimeStatsHelper::GameModeUnsupported;
+constexpr Fps kRefreshRate0 = 61_Hz;
+constexpr Fps kRenderRate0 = 31_Hz;
+constexpr GameMode kGameMode = GameMode::Unsupported;
enum InputCommand : int32_t {
ENABLE = 0,
@@ -145,14 +145,14 @@
std::string inputCommand(InputCommand cmd, bool useProto);
void setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode);
+ TimeStats::SetFrameRateVote, GameMode);
int32_t genRandomInt32(int32_t begin, int32_t end);
template <size_t N>
void insertTimeRecord(const TimeStamp (&sequence)[N], int32_t id, uint64_t frameNumber,
nsecs_t ts, TimeStats::SetFrameRateVote frameRateVote = {},
- int32_t gameMode = kGameMode) {
+ GameMode gameMode = kGameMode) {
for (size_t i = 0; i < N; i++, ts += 1000000) {
setTimeStamp(sequence[i], id, frameNumber, ts, frameRateVote, gameMode);
}
@@ -203,7 +203,7 @@
}
void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts,
- TimeStats::SetFrameRateVote frameRateVote, int32_t gameMode) {
+ TimeStats::SetFrameRateVote frameRateVote, GameMode gameMode) {
switch (type) {
case TimeStamp::POST:
ASSERT_NO_FATAL_FAILURE(mTimeStats->setPostTime(id, frameNumber, genLayerName(id),
@@ -1168,8 +1168,7 @@
constexpr nsecs_t APP_DEADLINE_DELTA_3MS = 3'000'000;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
}
@@ -1182,42 +1181,39 @@
TimeStats::SetFrameRateVote::FrameRateCompatibility::ExactOrMultiple,
.seamlessness = TimeStats::SetFrameRateVote::Seamlessness::NotRequired,
};
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60,
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, frameRate60, GameMode::Standard);
- mTimeStats->incrementJankyFrames(
- {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerCpuDeadlineMissed,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames(
- {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::SurfaceFlingerGpuDeadlineMissed,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::DisplayHAL,
+ GameMode::Standard, JankType::SurfaceFlingerCpuDeadlineMissed,
DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
APP_DEADLINE_DELTA_3MS});
mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::AppDeadlineMissed, DISPLAY_DEADLINE_DELTA,
+ GameMode::Standard, JankType::SurfaceFlingerGpuDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::DisplayHAL,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::AppDeadlineMissed,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_3MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::SurfaceFlingerScheduling,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::PredictionError,
+ DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
+ APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames(
+ {kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0), GameMode::Standard,
+ JankType::AppDeadlineMissed | JankType::BufferStuffing, DISPLAY_DEADLINE_DELTA,
+ APP_DEADLINE_DELTA_2MS, APP_DEADLINE_DELTA_2MS});
+ mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
+ GameMode::Standard, JankType::None, DISPLAY_DEADLINE_DELTA,
DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_3MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::SurfaceFlingerScheduling, DISPLAY_DEADLINE_DELTA,
- DISPLAY_PRESENT_JITTER, APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::PredictionError,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
- APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard,
- JankType::AppDeadlineMissed | JankType::BufferStuffing,
- DISPLAY_DEADLINE_DELTA, APP_DEADLINE_DELTA_2MS,
- APP_DEADLINE_DELTA_2MS});
- mTimeStats->incrementJankyFrames({kRefreshRate0, kRenderRate0, UID_0, genLayerName(LAYER_ID_0),
- TimeStatsHelper::GameModeStandard, JankType::None,
- DISPLAY_DEADLINE_DELTA, DISPLAY_PRESENT_JITTER,
- APP_DEADLINE_DELTA_3MS});
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
@@ -1293,22 +1289,18 @@
constexpr size_t BAD_DESIRED_PRESENT_FRAMES = 3;
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {},
- TimeStatsHelper::GameModeStandard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 1, 1000000, {}, GameMode::Standard);
for (size_t i = 0; i < LATE_ACQUIRE_FRAMES; i++) {
mTimeStats->incrementLatchSkipped(LAYER_ID_0, TimeStats::LatchSkipReason::LateAcquire);
}
for (size_t i = 0; i < BAD_DESIRED_PRESENT_FRAMES; i++) {
mTimeStats->incrementBadDesiredPresent(LAYER_ID_0);
}
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {},
- TimeStatsHelper::GameModeStandard);
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {},
- TimeStatsHelper::GameModePerformance);
-
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, TimeStatsHelper::GameModeBattery);
- insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, TimeStatsHelper::GameModeBattery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 2, 2000000, {}, GameMode::Standard);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 3, 3000000, {}, GameMode::Performance);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 4, 4000000, {}, GameMode::Battery);
+ insertTimeRecord(NORMAL_SEQUENCE, LAYER_ID_0, 5, 4000000, {}, GameMode::Battery);
std::string pulledData;
EXPECT_TRUE(mTimeStats->onPullAtom(10063 /*SURFACEFLINGER_STATS_LAYER_INFO*/, &pulledData));
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index ec19100..16d4b59 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -26,7 +26,7 @@
#include <log/log.h>
#include <ui/MockFence.h>
#include <utils/String8.h>
-#include "TestableScheduler.h"
+
#include "TestableSurfaceFlinger.h"
#include "mock/MockEventThread.h"
#include "mock/MockVsyncController.h"
@@ -85,11 +85,8 @@
std::move(eventThread), std::move(sfEventThread));
}
- TestableScheduler* mScheduler;
TestableSurfaceFlinger mFlinger;
- std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-
mock::VsyncController* mVsyncController = new mock::VsyncController();
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
mock::MockFence* mFenceUnsignaled = new mock::MockFence();
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
new file mode 100644
index 0000000..ffe5671
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <gui/SurfaceComposerClient.h>
+
+#include "Tracing/RingBuffer.h"
+#include "Tracing/TransactionTracing.h"
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+class TransactionTracingTest : public testing::Test {
+protected:
+ static constexpr size_t SMALL_BUFFER_SIZE = 1024;
+ std::unique_ptr<android::TransactionTracing> mTracing;
+ void SetUp() override { mTracing = std::make_unique<android::TransactionTracing>(); }
+
+ void TearDown() override {
+ mTracing->disable();
+ mTracing.reset();
+ }
+
+ auto getCommittedTransactions() {
+ std::scoped_lock<std::mutex> lock(mTracing->mMainThreadLock);
+ return mTracing->mCommittedTransactions;
+ }
+
+ auto getQueuedTransactions() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mQueuedTransactions;
+ }
+
+ auto getUsedBufferSize() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mBuffer->used();
+ }
+
+ auto flush(int64_t vsyncId) { return mTracing->flush(vsyncId); }
+
+ auto bufferFront() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mBuffer->front();
+ }
+
+ bool threadIsJoinable() {
+ std::scoped_lock lock(mTracing->mMainThreadLock);
+ return mTracing->mThread.joinable();
+ }
+
+ proto::TransactionTraceFile writeToProto() { return mTracing->writeToProto(); }
+
+ auto getCreatedLayers() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mCreatedLayers;
+ }
+
+ auto getStartingStates() {
+ std::scoped_lock<std::mutex> lock(mTracing->mTraceLock);
+ return mTracing->mStartingStates;
+ }
+
+ void queueAndCommitTransaction(int64_t vsyncId) {
+ TransactionState transaction;
+ transaction.id = static_cast<uint64_t>(vsyncId * 3);
+ transaction.originUid = 1;
+ transaction.originPid = 2;
+ mTracing->addQueuedTransaction(transaction);
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ mTracing->addCommittedTransactions(transactions, vsyncId);
+ flush(vsyncId);
+ }
+
+ // Test that we clean up the tracing thread and free any memory allocated.
+ void verifyDisabledTracingState() {
+ EXPECT_FALSE(mTracing->isEnabled());
+ EXPECT_FALSE(threadIsJoinable());
+ EXPECT_EQ(getCommittedTransactions().size(), 0u);
+ EXPECT_EQ(getQueuedTransactions().size(), 0u);
+ EXPECT_EQ(getUsedBufferSize(), 0u);
+ EXPECT_EQ(getStartingStates().size(), 0u);
+ }
+
+ void verifyEntry(const proto::TransactionTraceEntry& actualProto,
+ const std::vector<TransactionState> expectedTransactions,
+ int64_t expectedVsyncId) {
+ EXPECT_EQ(actualProto.vsync_id(), expectedVsyncId);
+ EXPECT_EQ(actualProto.transactions().size(),
+ static_cast<int32_t>(expectedTransactions.size()));
+ for (uint32_t i = 0; i < expectedTransactions.size(); i++) {
+ EXPECT_EQ(actualProto.transactions(static_cast<int32_t>(i)).pid(),
+ expectedTransactions[i].originPid);
+ }
+ }
+};
+
+TEST_F(TransactionTracingTest, enable) {
+ EXPECT_FALSE(mTracing->isEnabled());
+ mTracing->enable();
+ EXPECT_TRUE(mTracing->isEnabled());
+ mTracing->disable();
+ verifyDisabledTracingState();
+}
+
+TEST_F(TransactionTracingTest, addTransactions) {
+ mTracing->enable();
+ std::vector<TransactionState> transactions;
+ transactions.reserve(100);
+ for (uint64_t i = 0; i < 100; i++) {
+ TransactionState transaction;
+ transaction.id = i;
+ transaction.originPid = static_cast<int32_t>(i);
+ transactions.emplace_back(transaction);
+ mTracing->addQueuedTransaction(transaction);
+ }
+
+ // Split incoming transactions into two and commit them in reverse order to test out of order
+ // commits.
+ std::vector<TransactionState> firstTransactionSet =
+ std::vector<TransactionState>(transactions.begin() + 50, transactions.end());
+ int64_t firstTransactionSetVsyncId = 42;
+ mTracing->addCommittedTransactions(firstTransactionSet, firstTransactionSetVsyncId);
+
+ int64_t secondTransactionSetVsyncId = 43;
+ std::vector<TransactionState> secondTransactionSet =
+ std::vector<TransactionState>(transactions.begin(), transactions.begin() + 50);
+ mTracing->addCommittedTransactions(secondTransactionSet, secondTransactionSetVsyncId);
+ flush(secondTransactionSetVsyncId);
+
+ proto::TransactionTraceFile proto = writeToProto();
+ EXPECT_EQ(proto.entry().size(), 3);
+ // skip starting entry
+ verifyEntry(proto.entry(1), firstTransactionSet, firstTransactionSetVsyncId);
+ verifyEntry(proto.entry(2), secondTransactionSet, secondTransactionSetVsyncId);
+
+ mTracing->disable();
+ verifyDisabledTracingState();
+}
+
+class TransactionTracingLayerHandlingTest : public TransactionTracingTest {
+protected:
+ void SetUp() override {
+ TransactionTracingTest::SetUp();
+ mTracing->enable();
+ // add layers
+ mTracing->setBufferSize(SMALL_BUFFER_SIZE);
+ const sp<IBinder> fakeLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeLayerHandle->localBinder(), mParentLayerId, "parent",
+ 123 /* flags */, -1 /* parentId */);
+ const sp<IBinder> fakeChildLayerHandle = new BBinder();
+ mTracing->onLayerAdded(fakeChildLayerHandle->localBinder(), 2 /* layerId */, "child",
+ 456 /* flags */, mParentLayerId);
+
+ // add some layer transaction
+ {
+ TransactionState transaction;
+ transaction.id = 50;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged;
+ layerState.state.z = 42;
+ transaction.states.add(layerState);
+ ComposerState childState;
+ childState.state.surface = fakeChildLayerHandle;
+ layerState.state.z = 43;
+ transaction.states.add(childState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_FIRST_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_FIRST_LAYER_CHANGE);
+ flush(VSYNC_ID_FIRST_LAYER_CHANGE);
+ }
+
+ // add transactions that modify the layer state further so we can test that layer state
+ // gets merged
+ {
+ TransactionState transaction;
+ transaction.id = 51;
+ ComposerState layerState;
+ layerState.state.surface = fakeLayerHandle;
+ layerState.state.what = layer_state_t::eLayerChanged | layer_state_t::ePositionChanged;
+ layerState.state.z = 41;
+ layerState.state.x = 22;
+ transaction.states.add(layerState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ VSYNC_ID_SECOND_LAYER_CHANGE = ++mVsyncId;
+ mTracing->addCommittedTransactions(transactions, VSYNC_ID_SECOND_LAYER_CHANGE);
+ flush(VSYNC_ID_SECOND_LAYER_CHANGE);
+ }
+
+ // remove child layer
+ mTracing->onLayerRemoved(2);
+ VSYNC_ID_CHILD_LAYER_REMOVED = ++mVsyncId;
+ queueAndCommitTransaction(VSYNC_ID_CHILD_LAYER_REMOVED);
+
+ // remove layer
+ mTracing->onLayerRemoved(1);
+ queueAndCommitTransaction(++mVsyncId);
+ }
+
+ void TearDown() override {
+ mTracing->disable();
+ verifyDisabledTracingState();
+ TransactionTracingTest::TearDown();
+ }
+
+ int mParentLayerId = 1;
+ int64_t mVsyncId = 0;
+ int64_t VSYNC_ID_FIRST_LAYER_CHANGE;
+ int64_t VSYNC_ID_SECOND_LAYER_CHANGE;
+ int64_t VSYNC_ID_CHILD_LAYER_REMOVED;
+};
+
+TEST_F(TransactionTracingLayerHandlingTest, addStartingState) {
+ // add transactions until we drop the transaction with the first layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_FIRST_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we can still retrieve the layer change from the first entry containing starting
+ // states.
+ EXPECT_GT(proto.entry().size(), 0);
+ EXPECT_GT(proto.entry(0).transactions().size(), 0);
+ EXPECT_GT(proto.entry(0).added_layers().size(), 0);
+ EXPECT_GT(proto.entry(0).transactions(0).layer_changes().size(), 0);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, updateStartingState) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify starting states are updated correctly
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 41);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, removeStartingState) {
+ // add transactions until we drop the transaction which removes the child layer
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify the child layer has been removed from the trace
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+TEST_F(TransactionTracingLayerHandlingTest, startingStateSurvivesBufferFlush) {
+ // add transactions until we drop the transaction with the second layer change
+ while (bufferFront().vsync_id() <= VSYNC_ID_SECOND_LAYER_CHANGE) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto::TransactionTraceFile proto = writeToProto();
+ // verify we have two starting states
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+
+ // Continue adding transactions until child layer is removed
+ while (bufferFront().vsync_id() <= VSYNC_ID_CHILD_LAYER_REMOVED) {
+ queueAndCommitTransaction(++mVsyncId);
+ }
+ proto = writeToProto();
+ // verify we still have the parent layer state
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes().size(), 1);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
index e241dc9..849e308 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockSchedulerCallback.h
@@ -20,25 +20,22 @@
#include "Scheduler/Scheduler.h"
-namespace android::mock {
+namespace android::scheduler::mock {
struct SchedulerCallback final : ISchedulerCallback {
MOCK_METHOD(void, scheduleComposite, (FrameHint), (override));
- MOCK_METHOD1(setVsyncEnabled, void(bool));
- MOCK_METHOD2(changeRefreshRate,
- void(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent));
- MOCK_METHOD1(kernelTimerChanged, void(bool));
- MOCK_METHOD0(triggerOnFrameRateOverridesChanged, void());
+ MOCK_METHOD(void, setVsyncEnabled, (bool), (override));
+ MOCK_METHOD(void, changeRefreshRate, (const RefreshRate&, DisplayModeEvent), (override));
+ MOCK_METHOD(void, kernelTimerChanged, (bool), (override));
+ MOCK_METHOD(void, triggerOnFrameRateOverridesChanged, (), (override));
};
struct NoOpSchedulerCallback final : ISchedulerCallback {
void scheduleComposite(FrameHint) override {}
void setVsyncEnabled(bool) override {}
- void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
- scheduler::RefreshRateConfigEvent) override {}
+ void changeRefreshRate(const RefreshRate&, DisplayModeEvent) override {}
void kernelTimerChanged(bool) override {}
void triggerOnFrameRateOverridesChanged() {}
};
-} // namespace android::mock
+} // namespace android::scheduler::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index 5aebd2f..0a69b56 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -41,19 +41,21 @@
MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD6(setPostTime, void(int32_t, uint64_t, const std::string&, uid_t, nsecs_t, int32_t));
+ MOCK_METHOD(void, setPostTime,
+ (int32_t, uint64_t, const std::string&, uid_t, nsecs_t, GameMode), (override));
MOCK_METHOD2(incrementLatchSkipped, void(int32_t layerId, LatchSkipReason reason));
MOCK_METHOD1(incrementBadDesiredPresent, void(int32_t layerId));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setAcquireFence, void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&));
- MOCK_METHOD7(setPresentTime,
- void(int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote,
- int32_t));
- MOCK_METHOD7(setPresentFence,
- void(int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
- SetFrameRateVote, int32_t));
+ MOCK_METHOD(void, setPresentTime,
+ (int32_t, uint64_t, nsecs_t, Fps, std::optional<Fps>, SetFrameRateVote, GameMode),
+ (override));
+ MOCK_METHOD(void, setPresentFence,
+ (int32_t, uint64_t, const std::shared_ptr<FenceTime>&, Fps, std::optional<Fps>,
+ SetFrameRateVote, GameMode),
+ (override));
MOCK_METHOD1(incrementJankyFrames, void(const JankyFramesInfo&));
MOCK_METHOD1(onDestroy, void(int32_t));
MOCK_METHOD2(removeTimeRecord, void(int32_t, uint64_t));
diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
index 94d9966..314f681 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h
@@ -27,7 +27,7 @@
VsyncController();
~VsyncController() override;
- MOCK_METHOD1(addPresentFence, bool(const std::shared_ptr<FenceTime>&));
+ MOCK_METHOD(bool, addPresentFence, (std::shared_ptr<FenceTime>), (override));
MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*));
MOCK_METHOD1(startPeriodTransition, void(nsecs_t));
MOCK_METHOD1(setIgnorePresentFences, void(bool));
diff --git a/vulkan/libvulkan/api.cpp b/vulkan/libvulkan/api.cpp
index d1cd397..fa3b260 100644
--- a/vulkan/libvulkan/api.cpp
+++ b/vulkan/libvulkan/api.cpp
@@ -965,6 +965,13 @@
VkResult result = EnumerateDeviceExtensionProperties(physical_dev, nullptr,
&count, nullptr);
if (result == VK_SUCCESS && count) {
+ // Work-around a race condition during Android start-up, that can result
+ // in the second call to EnumerateDeviceExtensionProperties having
+ // another extension. That causes the second call to return
+ // VK_INCOMPLETE. A work-around is to add 1 to "count" and ask for one
+ // more extension property. See: http://anglebug.com/6715 and
+ // internal-to-Google b/206733351.
+ count++;
driver_extensions_ = AllocateDriverExtensionArray(count);
result = (driver_extensions_)
? EnumerateDeviceExtensionProperties(
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index e89a49b..b5a0bdf 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -537,30 +537,6 @@
}
}
-int get_min_buffer_count(ANativeWindow* window,
- uint32_t* out_min_buffer_count) {
- constexpr int kExtraBuffers = 2;
-
- int err;
- int min_undequeued_buffers;
- err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &min_undequeued_buffers);
- if (err != android::OK || min_undequeued_buffers < 0) {
- ALOGE(
- "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
- "value=%d",
- strerror(-err), err, min_undequeued_buffers);
- if (err == android::OK) {
- err = android::UNKNOWN_ERROR;
- }
- return err;
- }
-
- *out_min_buffer_count =
- static_cast<uint32_t>(min_undequeued_buffers + kExtraBuffers);
- return android::OK;
-}
-
} // anonymous namespace
VKAPI_ATTR
@@ -675,7 +651,7 @@
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- capabilities->minImageCount = max_buffer_count == 1 ? 1 : 2;
+ capabilities->minImageCount = std::min(max_buffer_count, 3);
capabilities->maxImageCount = static_cast<uint32_t>(max_buffer_count);
capabilities->currentExtent =
@@ -720,10 +696,10 @@
if (err) {
return VK_ERROR_SURFACE_LOST_KHR;
}
- ALOGV("wide_color_support is: %d", wide_color_support);
- wide_color_support =
- wide_color_support &&
+ bool swapchain_ext =
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
+ ALOGV("wide_color_support is: %d", wide_color_support);
+ wide_color_support = wide_color_support && swapchain_ext;
AHardwareBuffer_Desc desc = {};
desc.width = 1;
@@ -736,8 +712,12 @@
// We must support R8G8B8A8
std::vector<VkSurfaceFormatKHR> all_formats = {
{VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT}};
+ {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+
+ if (swapchain_ext) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_BT709_LINEAR_EXT});
+ }
if (wide_color_support) {
all_formats.emplace_back(VkSurfaceFormatKHR{
@@ -873,13 +853,18 @@
int err;
int query_value;
- uint32_t min_buffer_count;
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
- err = get_min_buffer_count(window, &min_buffer_count);
- if (err != android::OK) {
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &query_value);
+ if (err != android::OK || query_value < 0) {
+ ALOGE(
+ "NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) "
+ "value=%d",
+ strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
}
+ uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
if (err != android::OK || query_value < 0) {
@@ -890,7 +875,7 @@
uint32_t max_buffer_count = static_cast<uint32_t>(query_value);
std::vector<VkPresentModeKHR> present_modes;
- if (min_buffer_count < max_buffer_count)
+ if (min_undequeued_buffers + 1 < max_buffer_count)
present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);
present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);
@@ -1211,14 +1196,19 @@
}
}
- uint32_t min_buffer_count;
- err = get_min_buffer_count(window, &min_buffer_count);
- if (err != android::OK) {
+ int query_value;
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &query_value);
+ if (err != android::OK || query_value < 0) {
+ ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
+ query_value);
return VK_ERROR_SURFACE_LOST_KHR;
}
-
- uint32_t num_images =
- std::max(min_buffer_count, create_info->minImageCount);
+ uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
+ const auto mailbox_num_images = std::max(3u, create_info->minImageCount);
+ const auto requested_images =
+ swap_interval ? create_info->minImageCount : mailbox_num_images;
+ uint32_t num_images = requested_images - 1 + min_undequeued_buffers;
// Lower layer insists that we have at least two buffers. This is wasteful
// and we'd like to relax it in the shared case, but not all the pieces are
@@ -1232,12 +1222,6 @@
return VK_ERROR_SURFACE_LOST_KHR;
}
- // In shared mode the num_images must be one regardless of how many
- // buffers were allocated for the buffer queue.
- if (swapchain_image_usage & VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID) {
- num_images = 1;
- }
-
int32_t legacy_usage = 0;
if (dispatch.GetSwapchainGrallocUsage2ANDROID) {
uint64_t consumer_usage, producer_usage;