Merge "Periodically free cache below high storage threshold"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 4dd4f79..6d837c2 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,11 +1,12 @@
[Builtin Hooks]
-clang_format = true
bpfmt = true
+clang_format = true
[Builtin Hooks Options]
# 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/atrace/atrace.rc b/cmds/atrace/atrace.rc
index 01c4723..34ccb21 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -282,6 +282,21 @@
chmod 0666 /sys/kernel/debug/tracing/per_cpu/cpu23/trace
chmod 0666 /sys/kernel/tracing/per_cpu/cpu23/trace
+# Setup synthetic events
+ chmod 0666 /sys/kernel/tracing/synthetic_events
+ chmod 0666 /sys/kernel/debug/tracing/synthetic_events
+
+ # rss_stat_throttled
+ write /sys/kernel/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
+ write /sys/kernel/debug/tracing/synthetic_events "rss_stat_throttled unsigned int mm_id; unsigned int curr; int member; long size"
+
+# Set up histogram triggers
+ # rss_stat_throttled (bucket size == 512KB)
+ chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/trigger
+ chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger
+ write /sys/kernel/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)"
+ write /sys/kernel/debug/tracing/events/kmem/rss_stat/trigger "hist:keys=mm_id,member:bucket=size/0x80000:onchange($$bucket).rss_stat_throttled(mm_id,curr,member,size)"
+
# Only create the tracing instance if persist.mm_events.enabled
# Attempting to remove the tracing instance after it has been created
# will likely fail with EBUSY as it would be in use by traced_probes.
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..00babc3 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -28,6 +28,7 @@
"dexopt.cpp",
"execv_helper.cpp",
"globals.cpp",
+ "restorable_file.cpp",
"run_dex2oat.cpp",
"unique_file.cpp",
"utils.cpp",
@@ -45,6 +46,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "libziparchive",
"server_configurable_flags",
],
static_libs: [
@@ -79,7 +81,7 @@
"-cert-err58-cpp",
],
tidy_flags: [
- "-warnings-as-errors=clang-analyzer-security*,cert-*"
+ "-warnings-as-errors=clang-analyzer-security*,cert-*",
],
}
@@ -131,7 +133,10 @@
"unique_file.cpp",
"execv_helper.cpp",
],
- cflags: ["-Wall", "-Werror"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
shared_libs: [
"libbase",
"server_configurable_flags",
@@ -169,7 +174,7 @@
// Needs to be wherever installd is as it's execed by
// installd.
- required: [ "migrate_legacy_obb_data.sh" ],
+ required: ["migrate_legacy_obb_data.sh"],
}
// OTA chroot tool
@@ -193,7 +198,7 @@
"libutils",
],
required: [
- "apexd"
+ "apexd",
],
}
@@ -212,7 +217,7 @@
name: "libotapreoptparameters",
cflags: [
"-Wall",
- "-Werror"
+ "-Werror",
],
srcs: ["otapreopt_parameters.cpp"],
@@ -236,7 +241,7 @@
name: "otapreopt",
cflags: [
"-Wall",
- "-Werror"
+ "-Werror",
],
srcs: [
@@ -245,6 +250,7 @@
"globals.cpp",
"otapreopt.cpp",
"otapreopt_utils.cpp",
+ "restorable_file.cpp",
"run_dex2oat.cpp",
"unique_file.cpp",
"utils.cpp",
@@ -267,6 +273,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "libziparchive",
"server_configurable_flags",
],
}
@@ -294,5 +301,5 @@
// Script to migrate legacy obb data.
sh_binary {
name: "migrate_legacy_obb_data.sh",
- src: "migrate_legacy_obb_data.sh"
+ src: "migrate_legacy_obb_data.sh",
}
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 fffcdd1..289c2ae 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");
@@ -321,10 +419,17 @@
int res = 0;
char* before = nullptr;
char* after = nullptr;
+ if (!existing) {
+ if (selinux_android_restorecon_pkgdir(path.c_str(), seInfo.c_str(), uid,
+ SELINUX_ANDROID_RESTORECON_RECURSE) < 0) {
+ PLOG(ERROR) << "Failed recursive restorecon for " << path;
+ goto fail;
+ }
+ return res;
+ }
// Note that SELINUX_ANDROID_RESTORECON_DATADATA flag is set by
// libselinux. Not needed here.
-
if (lgetfilecon(path.c_str(), &before) < 0) {
PLOG(ERROR) << "Failed before getfilecon for " << path;
goto fail;
@@ -361,12 +466,6 @@
return res;
}
-static int restorecon_app_data_lazy(const std::string& parent, const char* name,
- const std::string& seInfo, uid_t uid, bool existing) {
- return restorecon_app_data_lazy(StringPrintf("%s/%s", parent.c_str(), name), seInfo, uid,
- existing);
-}
-
static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t uid) {
if (fs_prepare_dir_strict(path.c_str(), target_mode, uid, uid) != 0) {
PLOG(ERROR) << "Failed to prepare " << path;
@@ -422,13 +521,152 @@
return true;
}
-binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid,
- const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) {
+static bool chown_app_dir(const std::string& path, uid_t uid, uid_t previousUid, gid_t cacheGid) {
+ FTS* fts;
+ char *argv[] = { (char*) path.c_str(), nullptr };
+ if (!(fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr))) {
+ return false;
+ }
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 1
+ && (strcmp(p->fts_name, "cache") == 0
+ || strcmp(p->fts_name, "code_cache") == 0)) {
+ // Mark cache dirs
+ p->fts_number = 1;
+ } else {
+ // Inherit parent's number
+ p->fts_number = p->fts_parent->fts_number;
+ }
+
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (p->fts_statp->st_uid == previousUid) {
+ if (lchown(p->fts_path, uid, p->fts_number ? cacheGid : uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ } else {
+ LOG(WARNING) << "Ignoring " << p->fts_path << " with unexpected UID "
+ << p->fts_statp->st_uid << " instead of " << previousUid;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ return true;
+}
+
+static void chown_app_profile_dir(const std::string &packageName, int32_t appId, int32_t userId) {
+ uid_t uid = multiuser_get_uid(userId, appId);
+ gid_t sharedGid = multiuser_get_shared_gid(userId, appId);
+
+ const std::string profile_dir =
+ create_primary_current_profile_package_dir_path(userId, packageName);
+ char *argv[] = { (char*) profile_dir.c_str(), nullptr };
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, uid, uid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+
+ const std::string ref_profile_path =
+ create_primary_reference_profile_package_dir_path(packageName);
+ argv[0] = (char *) ref_profile_path.c_str();
+ if (FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr)) {
+ for (FTSENT* p; (p = fts_read(fts)) != nullptr;) {
+ if (p->fts_info == FTS_D && p->fts_level == 0) {
+ if (chown(p->fts_path, AID_SYSTEM, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to chown " << p->fts_path;
+ }
+ continue;
+ }
+ switch (p->fts_info) {
+ case FTS_D:
+ case FTS_F:
+ case FTS_SL:
+ case FTS_SLNONE:
+ if (lchown(p->fts_path, sharedGid, sharedGid) != 0) {
+ PLOG(WARNING) << "Failed to lchown " << p->fts_path;
+ }
+ break;
+ }
+ }
+ fts_close(fts);
+ }
+}
+
+static binder::Status createAppDataDirs(const std::string& path,
+ int32_t uid, int32_t* previousUid, int32_t cacheGid,
+ const std::string& seInfo, mode_t targetMode) {
+ struct stat st{};
+ bool parent_dir_exists = (stat(path.c_str(), &st) == 0);
+
+ auto cache_path = StringPrintf("%s/%s", path.c_str(), "cache");
+ auto code_cache_path = StringPrintf("%s/%s", path.c_str(), "code_cache");
+ bool cache_exists = (access(cache_path.c_str(), F_OK) == 0);
+ bool code_cache_exists = (access(code_cache_path.c_str(), F_OK) == 0);
+
+ if (parent_dir_exists) {
+ if (*previousUid < 0) {
+ // If previousAppId is -1 in CreateAppDataArgs, we will assume the current owner
+ // of the directory as previousUid. This is required because it is not always possible
+ // to chown app data during app upgrade (e.g. secondary users' CE storage not unlocked)
+ *previousUid = st.st_uid;
+ }
+ if (*previousUid != uid) {
+ if (!chown_app_dir(path, uid, *previousUid, cacheGid)) {
+ return error("Failed to chown " + path);
+ }
+ }
+ }
+
+ // Prepare only the parent app directory
+ if (prepare_app_dir(path, targetMode, uid) ||
+ prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
+ prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
+ return error("Failed to prepare " + path);
+ }
+
+ // Consider restorecon over contents if label changed
+ if (restorecon_app_data_lazy(path, seInfo, uid, parent_dir_exists)) {
+ return error("Failed to restorecon " + path);
+ }
+
+ // If the parent dir exists, the restorecon would already have been done
+ // as a part of the recursive restorecon above
+ if (parent_dir_exists && !cache_exists
+ && restorecon_app_data_lazy(cache_path, seInfo, uid, false)) {
+ return error("Failed to restorecon " + cache_path);
+ }
+
+ // If the parent dir exists, the restorecon would already have been done
+ // as a part of the recursive restorecon above
+ if (parent_dir_exists && !code_cache_exists
+ && restorecon_app_data_lazy(code_cache_path, seInfo, uid, false)) {
+ return error("Failed to restorecon " + code_cache_path);
+ }
+ return ok();
+}
+
+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();
@@ -437,6 +675,14 @@
if (_aidl_return != nullptr) *_aidl_return = -1;
int32_t uid = multiuser_get_uid(userId, appId);
+
+ // If previousAppId < 0, we will use the existing app data owner as previousAppUid
+ // If previousAppId == 0, we use uid as previousUid (no data migration will happen)
+ // if previousAppId > 0, an app is upgrading and changing its app ID
+ int32_t previousUid = previousAppId > 0
+ ? (int32_t) multiuser_get_uid(userId, previousAppId)
+ : (previousAppId == 0 ? uid : -1);
+
int32_t cacheGid = multiuser_get_cache_gid(userId, appId);
mode_t targetMode = targetSdkVersion >= MIN_RESTRICTED_HOME_SDK_VERSION ? 0700 : 0751;
@@ -447,19 +693,13 @@
if (flags & FLAG_STORAGE_CE) {
auto path = create_data_user_ce_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ if (previousUid != uid) {
+ chown_app_profile_dir(packageName, appId, userId);
}
// Remember inode numbers of cache directories so that we can clear
@@ -481,19 +721,10 @@
}
if (flags & FLAG_STORAGE_DE) {
auto path = create_data_user_de_package_path(uuid_, userId, pkgname);
- bool existing = (access(path.c_str(), F_OK) == 0);
- if (prepare_app_dir(path, targetMode, uid) ||
- prepare_app_cache_dir(path, "cache", 02771, uid, cacheGid) ||
- prepare_app_cache_dir(path, "code_cache", 02771, uid, cacheGid)) {
- return error("Failed to prepare " + path);
- }
-
- // Consider restorecon over contents if label changed
- if (restorecon_app_data_lazy(path, seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "cache", seInfo, uid, existing) ||
- restorecon_app_data_lazy(path, "code_cache", seInfo, uid, existing)) {
- return error("Failed to restorecon " + path);
+ auto status = createAppDataDirs(path, uid, &previousUid, cacheGid, seInfo, targetMode);
+ if (!status.isOk()) {
+ return status;
}
if (!prepare_app_profile_dir(packageName, appId, userId)) {
@@ -503,16 +734,27 @@
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) {
+ 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,
- args.seInfo, args.targetSdkVersion, &ceDataInode);
+ args.previousAppId, args.seInfo, args.targetSdkVersion, &ceDataInode);
_aidl_return->ceDataInode = ceDataInode;
_aidl_return->exceptionCode = status.exceptionCode();
_aidl_return->exceptionMessage = status.exceptionMessage();
@@ -523,10 +765,10 @@
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 (auto arg : args) {
+ for (const auto &arg : args) {
android::os::CreateAppDataResult result;
createAppData(arg, &result);
results.push_back(result);
@@ -540,7 +782,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();
@@ -584,7 +826,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)) {
@@ -601,7 +843,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();
@@ -624,14 +866,11 @@
}
}
if (flags & FLAG_STORAGE_DE) {
- std::string suffix = "";
- bool only_cache = false;
+ std::string suffix;
if (flags & FLAG_CLEAR_CACHE_ONLY) {
suffix = CACHE_DIR_POSTFIX;
- only_cache = true;
} else if (flags & FLAG_CLEAR_CODE_CACHE_ONLY) {
suffix = CODE_CACHE_DIR_POSTFIX;
- only_cache = true;
}
auto path = create_data_user_de_package_path(uuid_, userId, pkgname) + suffix;
@@ -701,7 +940,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);
@@ -721,7 +960,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();
@@ -792,15 +1031,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");
@@ -907,14 +1146,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();
@@ -927,19 +1166,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;
}
@@ -949,10 +1188,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) {
@@ -976,15 +1216,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.
@@ -992,8 +1232,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.
@@ -1001,10 +1242,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) {
@@ -1023,8 +1265,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);
@@ -1039,20 +1282,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);
@@ -1072,14 +1315,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);
@@ -1089,11 +1332,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;
@@ -1106,24 +1349,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) {
@@ -1132,8 +1375,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);
@@ -1143,15 +1387,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) {
@@ -1169,8 +1413,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);
@@ -1188,7 +1432,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;
@@ -1216,24 +1460,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,
- 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) {
@@ -1242,8 +1487,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) {
@@ -1252,8 +1497,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;
}
@@ -1271,15 +1517,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;
}
@@ -1292,7 +1539,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) {
@@ -1310,7 +1557,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();
@@ -1347,7 +1594,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;
@@ -1377,13 +1626,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))) {
@@ -1519,7 +1779,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];
@@ -1749,7 +2008,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,
@@ -1804,8 +2074,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,
@@ -2235,7 +2507,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;
@@ -2281,7 +2553,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>>();
@@ -2338,7 +2610,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();
@@ -2350,7 +2622,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();
}
@@ -2360,7 +2632,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();
@@ -2371,7 +2643,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();
@@ -2381,7 +2653,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)) {
@@ -2394,35 +2666,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);
@@ -2463,7 +2734,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();
@@ -2554,7 +2825,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();
@@ -2580,11 +2860,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();
@@ -2606,10 +2888,11 @@
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_PATH(packageDir);
- std::lock_guard<std::recursive_mutex> lock(mLock);
+ LOCK_PACKAGE();
if (validate_apk_path(packageDir.c_str())) {
return error("Invalid path " + packageDir);
@@ -2620,12 +2903,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();
@@ -2650,12 +2936,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();
@@ -2665,13 +2954,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();
@@ -2700,11 +2992,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();
@@ -2782,11 +3077,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();
@@ -2825,7 +3122,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);
@@ -2905,8 +3203,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");
}
@@ -2975,8 +3274,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(),
@@ -3025,7 +3325,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 e156b5e..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>
@@ -47,7 +48,13 @@
binder::Status createAppData(const std::optional<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
- const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return);
+ 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,
@@ -59,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,
@@ -109,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);
@@ -142,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);
@@ -180,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/CreateAppDataArgs.aidl b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
index 96d7faa..d5e8ee5 100644
--- a/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
+++ b/cmds/installd/binder/android/os/CreateAppDataArgs.aidl
@@ -23,6 +23,7 @@
int userId;
int flags;
int appId;
+ int previousAppId;
@utf8InCpp String seInfo;
int targetSdkVersion;
}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 3b6b354..684a311 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..b6f42ad 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -52,14 +52,16 @@
#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"
#include "execv_helper.h"
#include "globals.h"
-#include "installd_deps.h"
#include "installd_constants.h"
+#include "installd_deps.h"
#include "otapreopt_utils.h"
+#include "restorable_file.h"
#include "run_dex2oat.h"
#include "unique_file.h"
#include "utils.h"
@@ -308,12 +310,6 @@
return profile_boot_class_path == "true";
}
-static void UnlinkIgnoreResult(const std::string& path) {
- if (unlink(path.c_str()) < 0) {
- PLOG(ERROR) << "Failed to unlink " << path;
- }
-}
-
/*
* Whether dexopt should use a swap file when compiling an APK.
*
@@ -459,8 +455,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);
}
@@ -987,42 +983,34 @@
}
// (re)Creates the app image if needed.
-UniqueFile maybe_open_app_image(const std::string& out_oat_path,
- bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) {
-
+RestorableFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image,
+ bool is_public, int uid, bool is_secondary_dex) {
const std::string image_path = create_image_filename(out_oat_path);
if (image_path.empty()) {
// Happens when the out_oat_path has an unknown extension.
- return UniqueFile();
+ return RestorableFile();
}
- // In case there is a stale image, remove it now. Ignore any error.
- unlink(image_path.c_str());
-
// Not enabled, exit.
if (!generate_app_image) {
- return UniqueFile();
+ RestorableFile::RemoveAllFiles(image_path);
+ return RestorableFile();
}
std::string app_image_format = GetProperty("dalvik.vm.appimageformat", "");
if (app_image_format.empty()) {
- return UniqueFile();
+ RestorableFile::RemoveAllFiles(image_path);
+ return RestorableFile();
}
- // Recreate is true since we do not want to modify a mapped image. If the app is
- // already running and we modify the image file, it can cause crashes (b/27493510).
- UniqueFile image_file(
- open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/),
- image_path,
- UnlinkIgnoreResult);
+ // If the app is already running and we modify the image file, it can cause crashes
+ // (b/27493510).
+ RestorableFile image_file = RestorableFile::CreateWritableFile(image_path,
+ /*permissions*/ 0600);
if (image_file.fd() < 0) {
// Could not create application image file. Go on since we can compile without it.
LOG(ERROR) << "installd could not create '" << image_path
<< "' for image file during dexopt";
- // If we have a valid image file path but no image fd, explicitly erase the image file.
- if (unlink(image_path.c_str()) < 0) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "Couldn't unlink image file " << image_path;
- }
- }
+ // If we have a valid image file path but cannot create tmp file, reset it.
+ image_file.reset();
} else if (!set_permissions_and_ownership(
image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str());
@@ -1096,9 +1084,9 @@
// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to
// out_vdex_wrapper. Returns true for success or false in case of errors.
bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed,
- const char* instruction_set, bool is_public, int uid, bool is_secondary_dex,
- bool profile_guided, UniqueFile* in_vdex_wrapper,
- UniqueFile* out_vdex_wrapper) {
+ const char* instruction_set, bool is_public, int uid,
+ bool is_secondary_dex, bool profile_guided,
+ UniqueFile* in_vdex_wrapper, RestorableFile* out_vdex_wrapper) {
CHECK(in_vdex_wrapper != nullptr);
CHECK(out_vdex_wrapper != nullptr);
// Open the existing VDEX. We do this before creating the new output VDEX, which will
@@ -1113,6 +1101,14 @@
return false;
}
+ // Create work file first. All files will be deleted when it fails.
+ *out_vdex_wrapper = RestorableFile::CreateWritableFile(out_vdex_path_str,
+ /*permissions*/ 0644);
+ if (out_vdex_wrapper->fd() < 0) {
+ ALOGE("installd cannot open vdex '%s' during dexopt\n", out_vdex_path_str.c_str());
+ return false;
+ }
+
bool update_vdex_in_place = false;
if (dexopt_action != DEX2OAT_FROM_SCRATCH) {
// Open the possibly existing vdex. If none exist, we pass -1 to dex2oat for input-vdex-fd.
@@ -1144,41 +1140,19 @@
(dexopt_action == DEX2OAT_FOR_BOOT_IMAGE) &&
!profile_guided;
if (update_vdex_in_place) {
+ // dex2oat marks it invalid anyway. So delete it and set work file fd.
+ unlink(in_vdex_path_str.c_str());
// Open the file read-write to be able to update it.
- in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0),
- in_vdex_path_str);
- if (in_vdex_wrapper->fd() == -1) {
- // If we failed to open the file, we cannot update it in place.
- update_vdex_in_place = false;
- }
+ in_vdex_wrapper->reset(out_vdex_wrapper->fd(), in_vdex_path_str);
+ // Disable auto close for the in wrapper fd (it will be done when destructing the out
+ // wrapper).
+ in_vdex_wrapper->DisableAutoClose();
} else {
in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0),
in_vdex_path_str);
}
}
- // If we are updating the vdex in place, we do not need to recreate a vdex,
- // and can use the same existing one.
- if (update_vdex_in_place) {
- // We unlink the file in case the invocation of dex2oat fails, to ensure we don't
- // have bogus stale vdex files.
- out_vdex_wrapper->reset(
- in_vdex_wrapper->fd(),
- out_vdex_path_str,
- UnlinkIgnoreResult);
- // Disable auto close for the in wrapper fd (it will be done when destructing the out
- // wrapper).
- in_vdex_wrapper->DisableAutoClose();
- } else {
- out_vdex_wrapper->reset(
- open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644),
- out_vdex_path_str,
- UnlinkIgnoreResult);
- if (out_vdex_wrapper->fd() < 0) {
- ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str());
- return false;
- }
- }
if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid,
out_vdex_path_str.c_str(), is_secondary_dex)) {
ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str());
@@ -1190,16 +1164,13 @@
}
// Opens the output oat file for the given apk.
-UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir,
- bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) {
+RestorableFile open_oat_out_file(const char* apk_path, const char* oat_dir, bool is_public, int uid,
+ const char* instruction_set, bool is_secondary_dex) {
char out_oat_path[PKG_PATH_MAX];
if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) {
- return UniqueFile();
+ return RestorableFile();
}
- UniqueFile oat(
- open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644),
- out_oat_path,
- UnlinkIgnoreResult);
+ RestorableFile oat = RestorableFile::CreateWritableFile(out_oat_path, /*permissions*/ 0644);
if (oat.fd() < 0) {
PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path;
} else if (!set_permissions_and_ownership(
@@ -1838,6 +1809,7 @@
if (sec_dex_result == kSecondaryDexOptProcessOk) {
oat_dir = oat_dir_str.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
+ *completed = true;
return 0; // Nothing to do, report success.
}
} else if (sec_dex_result == kSecondaryDexOptProcessCancelled) {
@@ -1873,8 +1845,8 @@
}
// Create the output OAT file.
- UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid,
- instruction_set, is_secondary_dex);
+ RestorableFile out_oat =
+ open_oat_out_file(dex_path, oat_dir, is_public, uid, instruction_set, is_secondary_dex);
if (out_oat.fd() < 0) {
*error_msg = "Could not open out oat file.";
return -1;
@@ -1882,7 +1854,7 @@
// Open vdex files.
UniqueFile in_vdex;
- UniqueFile out_vdex;
+ RestorableFile out_vdex;
if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed,
instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex,
&out_vdex)) {
@@ -1918,8 +1890,8 @@
}
// Create the app image file if needed.
- UniqueFile out_image = maybe_open_app_image(
- out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex);
+ RestorableFile out_image = maybe_open_app_image(out_oat.path(), generate_app_image, is_public,
+ uid, is_secondary_dex);
UniqueFile dex_metadata;
if (dex_metadata_path != nullptr) {
@@ -1952,30 +1924,18 @@
LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---";
RunDex2Oat runner(dex2oat_bin, execv_helper.get());
- runner.Initialize(out_oat,
- out_vdex,
- out_image,
- in_dex,
- in_vdex,
- dex_metadata,
- reference_profile,
- class_loader_context,
- join_fds(context_input_fds),
- swap_fd.get(),
- instruction_set,
- compiler_filter,
- debuggable,
- boot_complete,
- for_restore,
- target_sdk_version,
- enable_hidden_api_checks,
- generate_compact_dex,
- use_jitzygote_image,
+ runner.Initialize(out_oat.GetUniqueFile(), out_vdex.GetUniqueFile(), out_image.GetUniqueFile(),
+ in_dex, in_vdex, dex_metadata, reference_profile, class_loader_context,
+ join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter,
+ debuggable, boot_complete, for_restore, target_sdk_version,
+ enable_hidden_api_checks, generate_compact_dex, use_jitzygote_image,
compilation_reason);
bool cancelled = false;
pid_t pid = dexopt_status_->check_cancellation_and_fork(&cancelled);
if (cancelled) {
+ *completed = false;
+ reference_profile.DisableCleanup();
return 0;
}
if (pid == 0) {
@@ -2003,6 +1963,7 @@
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- cancelled";
// cancelled, not an error
*completed = false;
+ reference_profile.DisableCleanup();
return 0;
}
LOG(VERBOSE) << "DexInv: --- END '" << dex_path << "' --- status=0x"
@@ -2012,13 +1973,42 @@
}
}
- // TODO(b/156537504) Implement SWAP of completed files
- // We've been successful, don't delete output.
- out_oat.DisableCleanup();
- out_vdex.DisableCleanup();
- out_image.DisableCleanup();
+ // dex2oat ran successfully, so profile is safe to keep.
reference_profile.DisableCleanup();
+ // We've been successful, commit work files.
+ // If committing (=renaming tmp to regular) fails, try to restore backup files.
+ // If restoring fails as well, as a last resort, remove all files.
+ if (!out_oat.CreateBackupFile() || !out_vdex.CreateBackupFile() ||
+ !out_image.CreateBackupFile()) {
+ // Renaming failure can mean that the original file may not be accessible from installd.
+ LOG(ERROR) << "Cannot create backup file from existing file, file in wrong state?"
+ << ", out_oat:" << out_oat.path() << " ,out_vdex:" << out_vdex.path()
+ << " ,out_image:" << out_image.path();
+ out_oat.ResetAndRemoveAllFiles();
+ out_vdex.ResetAndRemoveAllFiles();
+ out_image.ResetAndRemoveAllFiles();
+ return -1;
+ }
+ if (!out_oat.CommitWorkFile() || !out_vdex.CommitWorkFile() || !out_image.CommitWorkFile()) {
+ LOG(ERROR) << "Cannot commit, out_oat:" << out_oat.path()
+ << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+ if (!out_oat.RestoreBackupFile() || !out_vdex.RestoreBackupFile() ||
+ !out_image.RestoreBackupFile()) {
+ LOG(ERROR) << "Cannot cancel commit, out_oat:" << out_oat.path()
+ << " ,out_vdex:" << out_vdex.path() << " ,out_image:" << out_image.path();
+ // Restoring failed.
+ out_oat.ResetAndRemoveAllFiles();
+ out_vdex.ResetAndRemoveAllFiles();
+ out_image.ResetAndRemoveAllFiles();
+ }
+ return -1;
+ }
+ // Now remove remaining backup files.
+ out_oat.RemoveBackupFile();
+ out_vdex.RemoveBackupFile();
+ out_image.RemoveBackupFile();
+
*completed = true;
return 0;
}
@@ -2562,7 +2552,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 +2626,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 +2728,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 +2758,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/restorable_file.cpp b/cmds/installd/restorable_file.cpp
new file mode 100644
index 0000000..fd54a23
--- /dev/null
+++ b/cmds/installd/restorable_file.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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.
+ */
+
+#include "restorable_file.h"
+
+#include <string>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace {
+
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+std::string GetTmpFilePath(const std::string& path) {
+ return android::base::StringPrintf("%s%s", path.c_str(), kTmpFileSuffix);
+}
+
+std::string GetBackupFilePath(const std::string& path) {
+ return android::base::StringPrintf("%s%s", path.c_str(), kBackupFileSuffix);
+}
+
+void UnlinkPossiblyNonExistingFile(const std::string& path) {
+ if (unlink(path.c_str()) < 0) {
+ if (errno != ENOENT && errno != EROFS) { // EROFS reported even if it does not exist.
+ PLOG(ERROR) << "Cannot unlink: " << path;
+ }
+ }
+}
+
+// Check if file for the given path exists
+bool FileExists(const std::string& path) {
+ struct stat st;
+ return ::stat(path.c_str(), &st) == 0;
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+RestorableFile::RestorableFile() : RestorableFile(-1, "") {}
+
+RestorableFile::RestorableFile(int value, const std::string& path) : unique_file_(value, path) {
+ // As cleanup is null, this does not make much difference but use unique_file_ only for closing
+ // tmp file.
+ unique_file_.DisableCleanup();
+}
+
+RestorableFile::~RestorableFile() {
+ reset();
+}
+
+void RestorableFile::reset() {
+ // need to copy before reset clears it.
+ std::string path(unique_file_.path());
+ unique_file_.reset();
+ if (!path.empty()) {
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ }
+}
+
+bool RestorableFile::CreateBackupFile() {
+ if (path().empty() || !FileExists(path())) {
+ return true;
+ }
+ std::string backup = GetBackupFilePath(path());
+ UnlinkPossiblyNonExistingFile(backup);
+ if (rename(path().c_str(), backup.c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << path() << " to " << backup;
+ return false;
+ }
+ return true;
+}
+
+bool RestorableFile::CommitWorkFile() {
+ std::string path(unique_file_.path());
+ // Keep the path with Commit for debugging purpose.
+ unique_file_.reset(-1, path);
+ if (!path.empty()) {
+ if (rename(GetTmpFilePath(path).c_str(), path.c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << GetTmpFilePath(path) << " to " << path;
+ // Remove both files as renaming can fail due to the original file as well.
+ UnlinkPossiblyNonExistingFile(path);
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool RestorableFile::RestoreBackupFile() {
+ std::string backup = GetBackupFilePath(path());
+ if (path().empty() || !FileExists(backup)) {
+ return true;
+ }
+ UnlinkPossiblyNonExistingFile(path());
+ if (rename(backup.c_str(), path().c_str()) < 0) {
+ PLOG(ERROR) << "Cannot rename " << backup << " to " << path();
+ return false;
+ }
+ return true;
+}
+
+void RestorableFile::RemoveBackupFile() {
+ UnlinkPossiblyNonExistingFile(GetBackupFilePath(path()));
+}
+
+const UniqueFile& RestorableFile::GetUniqueFile() const {
+ return unique_file_;
+}
+
+void RestorableFile::ResetAndRemoveAllFiles() {
+ std::string path(unique_file_.path());
+ reset();
+ RemoveAllFiles(path);
+}
+
+RestorableFile RestorableFile::CreateWritableFile(const std::string& path, int permissions) {
+ std::string tmp_file_path = GetTmpFilePath(path);
+ // If old tmp file exists, delete it.
+ UnlinkPossiblyNonExistingFile(tmp_file_path);
+ int fd = -1;
+ if (!path.empty()) {
+ fd = open(tmp_file_path.c_str(), O_RDWR | O_CREAT, permissions);
+ if (fd < 0) {
+ PLOG(ERROR) << "Cannot create file: " << tmp_file_path;
+ }
+ }
+ RestorableFile rf(fd, path);
+ return rf;
+}
+
+void RestorableFile::RemoveAllFiles(const std::string& path) {
+ UnlinkPossiblyNonExistingFile(GetTmpFilePath(path));
+ UnlinkPossiblyNonExistingFile(GetBackupFilePath(path));
+ UnlinkPossiblyNonExistingFile(path);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/restorable_file.h b/cmds/installd/restorable_file.h
new file mode 100644
index 0000000..eda2292
--- /dev/null
+++ b/cmds/installd/restorable_file.h
@@ -0,0 +1,107 @@
+/*
+ * 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_INSTALLD_RESTORABLE_FILE_H
+#define ANDROID_INSTALLD_RESTORABLE_FILE_H
+
+#include <functional>
+#include <string>
+
+#include "unique_file.h"
+
+namespace android {
+namespace installd {
+
+// This is a file abstraction which allows restoring to the original file while temporary work
+// file is updated.
+//
+// Typical flow for this API will be:
+// RestorableFile rf = RestorableFile::CreateWritableFile(...)
+// write to file using file descriptor acquired from: rf.fd()
+// Make work file into a regular file with: rf.CommitWorkFile()
+// Or throw away the work file by destroying the instance without calling CommitWorkFile().
+// The temporary work file is closed / removed when an instance is destroyed without calling
+// CommitWorkFile(). The original file, if CommitWorkFile() is not called, will be kept.
+//
+// For safer restoration of original file when commit fails, following 3 steps can be taken:
+// 1. CreateBackupFile(): This renames an existing regular file into a separate backup file.
+// 2. CommitWorkFile(): Rename the work file into the regular file.
+// 3. RemoveBackupFile(): Removes the backup file
+// If CommitWorkFile fails, client can call RestoreBackupFile() which will restore regular file from
+// the backup.
+class RestorableFile {
+public:
+ // Creates invalid instance with no fd (=-1) and empty path.
+ RestorableFile();
+ RestorableFile(RestorableFile&& other) = default;
+ ~RestorableFile();
+
+ // Passes all contents of other file into the current file.
+ // Files kept for the current file will be either deleted or committed depending on
+ // CommitWorkFile() and DisableCleanUp() calls made before this.
+ RestorableFile& operator=(RestorableFile&& other) = default;
+
+ // Gets file descriptor for backing work (=temporary) file. If work file does not exist, it will
+ // return -1.
+ int fd() const { return unique_file_.fd(); }
+
+ // Gets the path name for the regular file (not temporary file).
+ const std::string& path() const { return unique_file_.path(); }
+
+ // Closes work file, deletes it and resets all internal states into default states.
+ void reset();
+
+ // Closes work file and closes all files including work file, backup file and regular file.
+ void ResetAndRemoveAllFiles();
+
+ // Creates a backup file by renaming existing regular file. This will return false if renaming
+ // fails. If regular file for renaming does not exist, it will return true.
+ bool CreateBackupFile();
+
+ // Closes existing work file and makes it a regular file.
+ // Note that the work file is closed and fd() will return -1 after this. path() will still
+ // return the original path.
+ // This will return false when committing fails (=cannot rename). Both the regular file and tmp
+ // file will be deleted when it fails.
+ bool CommitWorkFile();
+
+ // Cancels the commit and restores the backup file into the regular one. If renaming fails,
+ // it will return false. This returns true if the backup file does not exist.
+ bool RestoreBackupFile();
+
+ // Removes the backup file.
+ void RemoveBackupFile();
+
+ // Gets UniqueFile with the same path and fd() pointing to the work file.
+ const UniqueFile& GetUniqueFile() const;
+
+ // Creates writable RestorableFile. This involves creating tmp file for writing.
+ static RestorableFile CreateWritableFile(const std::string& path, int permissions);
+
+ // Removes the specified file together with tmp file generated as RestorableFile.
+ static void RemoveAllFiles(const std::string& path);
+
+private:
+ RestorableFile(int value, const std::string& path);
+
+ // Used as a storage for work file fd and path string.
+ UniqueFile unique_file_;
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_RESTORABLE_FILE_H
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 7082017..51f7716 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,26 @@
],
static_libs: [
"liblog",
- "libotapreoptparameters"
+ "libotapreoptparameters",
+ ],
+}
+
+cc_test {
+ name: "installd_file_test",
+ test_suites: ["device-tests"],
+ clang: true,
+ srcs: ["installd_file_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libutils",
+ ],
+ static_libs: [
+ "libinstalld",
+ "liblog",
],
}
diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp
index 5cb83fd..4976646 100644
--- a/cmds/installd/tests/installd_cache_test.cpp
+++ b/cmds/installd/tests/installd_cache_test.cpp
@@ -123,6 +123,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 ea26955..bb36c39 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -287,6 +287,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_);
@@ -634,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()),
@@ -728,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(),
@@ -1257,6 +1259,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode_));
@@ -1320,6 +1323,7 @@
kTestUserId,
kAppDataFlags,
kTestAppUid,
+ 0 /* previousAppId */,
se_info_,
kOSdkVersion,
&ce_data_inode));
diff --git a/cmds/installd/tests/installd_file_test.cpp b/cmds/installd/tests/installd_file_test.cpp
new file mode 100644
index 0000000..00fb308
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.cpp
@@ -0,0 +1,521 @@
+/*
+ * 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.
+ */
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "restorable_file.h"
+#include "unique_file.h"
+#include "utils.h"
+
+#undef LOG_TAG
+#define LOG_TAG "installd_file_test"
+
+namespace {
+
+constexpr char kFileTestDir[] = "/data/local/tmp/installd_file_test_data";
+constexpr char kTmpFileSuffix[] = ".tmp";
+constexpr char kBackupFileSuffix[] = ".backup";
+
+void UnlinkWithAssert(const std::string& path) {
+ ASSERT_EQ(0, unlink(path.c_str()));
+}
+
+} // namespace
+
+namespace android {
+namespace installd {
+
+// Add these as macros as functions make it hard to tell where the failure has happened.
+#define ASSERT_FILE_NOT_EXISTING(path) \
+ { \
+ struct stat st; \
+ ASSERT_NE(0, ::stat(path.c_str(), &st)); \
+ }
+#define ASSERT_FILE_EXISTING(path) \
+ { \
+ struct stat st; \
+ ASSERT_EQ(0, ::stat(path.c_str(), &st)); \
+ }
+#define ASSERT_FILE_CONTENT(path, expectedContent) ASSERT_EQ(expectedContent, ReadTestFile(path))
+#define ASSERT_FILE_OPEN(path, fd) \
+ { \
+ fd = open(path.c_str(), O_RDWR); \
+ ASSERT_TRUE(fd >= 0); \
+ }
+#define ASSERT_WRITE_TO_FD(fd, content) \
+ ASSERT_TRUE(android::base::WriteStringToFd(content, android::base::borrowed_fd(fd)))
+
+class FileTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
+
+ ASSERT_EQ(0, create_dir_if_needed(kFileTestDir, 0777));
+ }
+
+ virtual void TearDown() {
+ system(android::base::StringPrintf("rm -rf %s", kFileTestDir).c_str());
+ }
+
+ std::string GetTestFilePath(const std::string& fileName) {
+ return android::base::StringPrintf("%s/%s", kFileTestDir, fileName.c_str());
+ }
+
+ void CreateTestFileWithContents(const std::string& path, const std::string& content) {
+ ALOGI("CreateTestFileWithContents:%s", path.c_str());
+ ASSERT_TRUE(android::base::WriteStringToFile(content, path));
+ }
+
+ std::string GetTestName() {
+ std::string name(testing::UnitTest::GetInstance()->current_test_info()->name());
+ return name;
+ }
+
+ std::string ReadTestFile(const std::string& path) {
+ std::string content;
+ bool r = android::base::ReadFileToString(path, &content);
+ if (!r) {
+ PLOG(ERROR) << "Cannot read file:" << path;
+ }
+ return content;
+ }
+};
+
+TEST_F(FileTest, TestUniqueFileMoveConstruction) {
+ const int fd = 101;
+ std::string testFile = GetTestFilePath(GetTestName());
+ UniqueFile uf1(fd, testFile);
+ uf1.DisableAutoClose();
+
+ UniqueFile uf2(std::move(uf1));
+
+ ASSERT_EQ(fd, uf2.fd());
+ ASSERT_EQ(testFile, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileAssignment) {
+ const int fd1 = 101;
+ const int fd2 = 102;
+ std::string testFile1 = GetTestFilePath(GetTestName());
+ std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+ UniqueFile uf1(fd1, testFile1);
+ uf1.DisableAutoClose();
+
+ UniqueFile uf2(fd2, testFile2);
+ uf2.DisableAutoClose();
+
+ ASSERT_EQ(fd2, uf2.fd());
+ ASSERT_EQ(testFile2, uf2.path());
+
+ uf2 = std::move(uf1);
+
+ ASSERT_EQ(fd1, uf2.fd());
+ ASSERT_EQ(testFile1, uf2.path());
+}
+
+TEST_F(FileTest, TestUniqueFileCleanup) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ { UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert); }
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestUniqueFileNoCleanup) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ {
+ UniqueFile uf = UniqueFile(fd, testFile, UnlinkWithAssert);
+ uf.DisableCleanup();
+ }
+
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestUniqueFileFd) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ int fd;
+ ASSERT_FILE_OPEN(testFile, fd);
+
+ UniqueFile uf(fd, testFile, UnlinkWithAssert);
+
+ ASSERT_EQ(fd, uf.fd());
+
+ uf.reset();
+
+ ASSERT_EQ(-1, uf.fd());
+}
+
+TEST_F(FileTest, TestRestorableFileMoveConstruction) {
+ std::string testFile = GetTestFilePath(GetTestName());
+
+ RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile, 0600);
+ int fd = rf1.fd();
+
+ RestorableFile rf2(std::move(rf1));
+
+ ASSERT_EQ(fd, rf2.fd());
+ ASSERT_EQ(testFile, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileAssignment) {
+ std::string testFile1 = GetTestFilePath(GetTestName());
+ std::string testFile2 = GetTestFilePath(GetTestName() + "2");
+
+ RestorableFile rf1 = RestorableFile::CreateWritableFile(testFile1, 0600);
+ int fd1 = rf1.fd();
+
+ RestorableFile rf2 = RestorableFile::CreateWritableFile(testFile2, 0600);
+ int fd2 = rf2.fd();
+
+ ASSERT_EQ(fd2, rf2.fd());
+ ASSERT_EQ(testFile2, rf2.path());
+
+ rf2 = std::move(rf1);
+
+ ASSERT_EQ(fd1, rf2.fd());
+ ASSERT_EQ(testFile1, rf2.path());
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithReset) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+
+ const UniqueFile& uf = rf.GetUniqueFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+
+ rf.reset();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_TRUE(rf.path().empty());
+ }
+}
+
+TEST_F(FileTest, TestRestorableFileVerifyUniqueFileWithCommit) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+
+ const UniqueFile& uf = rf.GetUniqueFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ rf.CommitWorkFile();
+
+ ASSERT_EQ(rf.fd(), uf.fd());
+ ASSERT_EQ(rf.path(), uf.path());
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ }
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileNotCommitted) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ ASSERT_FILE_EXISTING(testFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNotCommittedWithOriginalAndOldTmp) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+ ASSERT_FILE_EXISTING(testFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "OriginalContent");
+}
+
+TEST_F(FileTest, TestRestorableFileNewFileCommitted) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+
+ ASSERT_FILE_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+ rf.RemoveBackupFile();
+
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+
+ ASSERT_FILE_EXISTING(backupFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+
+ rf.RemoveBackupFile();
+
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommittedWithOriginalAndOldTmp) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "OldTmp");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+ ASSERT_FILE_CONTENT(tmpFile, "NewContent");
+
+ ASSERT_TRUE(rf.CommitWorkFile());
+
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_CONTENT(testFile, "NewContent");
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureNoOriginal) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+
+ // Now remove tmp file to force commit failure.
+ close(rf.fd());
+ ASSERT_EQ(0, unlink(tmpFile.c_str()));
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_FALSE(rf.CommitWorkFile());
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_TRUE(rf.RestoreBackupFile());
+ }
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileCommitFailureAndRollback) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ // Now remove tmp file to force commit failure.
+ close(rf.fd());
+ ASSERT_EQ(0, unlink(tmpFile.c_str()));
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+
+ ASSERT_FALSE(rf.CommitWorkFile());
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_EQ(testFile, rf.path());
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ ASSERT_TRUE(rf.RestoreBackupFile());
+ }
+
+ ASSERT_FILE_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileResetAndRemoveAllFiles) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ {
+ RestorableFile rf = RestorableFile::CreateWritableFile(testFile, 0600);
+ ASSERT_WRITE_TO_FD(rf.fd(), "NewContent");
+
+ ASSERT_TRUE(rf.CreateBackupFile());
+
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_EXISTING(backupFile);
+
+ rf.ResetAndRemoveAllFiles();
+
+ ASSERT_EQ(-1, rf.fd());
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+ }
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentFile) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+
+ RestorableFile::RemoveAllFiles(testFile);
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+TEST_F(FileTest, TestRestorableFileRemoveFileAndTmpFileWithContentAndTmpFile) {
+ std::string testFile = GetTestFilePath(GetTestName());
+ std::string tmpFile = testFile + kTmpFileSuffix;
+ std::string backupFile = testFile + kBackupFileSuffix;
+ CreateTestFileWithContents(testFile, "OriginalContent");
+ CreateTestFileWithContents(testFile + kTmpFileSuffix, "TmpContent");
+
+ RestorableFile::RemoveAllFiles(testFile);
+
+ ASSERT_FILE_NOT_EXISTING(tmpFile);
+ ASSERT_FILE_NOT_EXISTING(testFile);
+ ASSERT_FILE_NOT_EXISTING(backupFile);
+}
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/tests/installd_file_test.xml b/cmds/installd/tests/installd_file_test.xml
new file mode 100644
index 0000000..5ec6e3f
--- /dev/null
+++ b/cmds/installd/tests/installd_file_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_file_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push"
+ value="installd_file_test->/data/local/tmp/installd_file_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access (rollback. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_file_test" />
+ </test>
+</configuration>
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/service/service.cpp b/cmds/service/service.cpp
index fdbe85b..fe417a3 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -45,33 +45,6 @@
}
}
-// get the name of the generic interface we hold a reference to
-static String16 get_interface_name(sp<IBinder> service)
-{
- if (service != nullptr) {
- Parcel data, reply;
- data.markForBinder(service);
- status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
- if (err == NO_ERROR) {
- return reply.readString16();
- }
- }
- return String16();
-}
-
-static String8 good_old_string(const String16& src)
-{
- String8 name8;
- char ch8[2];
- ch8[1] = 0;
- for (unsigned j = 0; j < src.size(); j++) {
- char16_t ch = src[j];
- if (ch < 128) ch8[0] = (char)ch;
- name8.append(ch8);
- }
- return name8;
-}
-
int main(int argc, char* const argv[])
{
bool wantsUsage = false;
@@ -132,8 +105,8 @@
String16 name = services[i];
sp<IBinder> service = sm->checkService(name);
aout << i
- << "\t" << good_old_string(name)
- << ": [" << good_old_string(get_interface_name(service)) << "]"
+ << "\t" << name
+ << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]"
<< endl;
}
} else if (strcmp(argv[optind], "call") == 0) {
@@ -141,7 +114,7 @@
if (optind+1 < argc) {
int serviceArg = optind;
sp<IBinder> service = sm->checkService(String16(argv[optind++]));
- String16 ifName = get_interface_name(service);
+ String16 ifName = (service ? service->getInterfaceDescriptor() : String16());
int32_t code = atoi(argv[optind++]);
if (service != nullptr && ifName.size() > 0) {
Parcel data, reply;
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.rc b/cmds/servicemanager/servicemanager.rc
index 0dd29e0..e5d689f 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -6,8 +6,8 @@
onrestart restart apexd
onrestart restart audioserver
onrestart restart gatekeeperd
- onrestart class_restart main
- onrestart class_restart hal
- onrestart class_restart early_hal
+ onrestart class_restart --only-enabled main
+ onrestart class_restart --only-enabled hal
+ onrestart class_restart --only-enabled early_hal
task_profiles ServiceCapacityLow
shutdown critical
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/data/etc/Android.bp b/data/etc/Android.bp
index 5fe4ea1..931c5e3 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -89,6 +89,12 @@
}
prebuilt_etc {
+ name: "android.hardware.fingerprint.prebuilt.xml",
+ src: "android.hardware.fingerprint.xml",
+ defaults: ["frameworks_native_data_etc_defaults"],
+}
+
+prebuilt_etc {
name: "android.hardware.location.gps.prebuilt.xml",
src: "android.hardware.location.gps.xml",
defaults: ["frameworks_native_data_etc_defaults"],
diff --git a/data/etc/android.hardware.telephony.calling.xml b/data/etc/android.hardware.telephony.calling.xml
new file mode 100644
index 0000000..3e92a66
--- /dev/null
+++ b/data/etc/android.hardware.telephony.calling.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Calling API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.software.telecom" />
+ <feature name="android.hardware.telephony.radio" />
+ <feature name="android.hardware.telephony.subscription" />
+ <feature name="android.hardware.telephony.calling" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.cdma.xml b/data/etc/android.hardware.telephony.cdma.xml
index 082378d..c81cf06 100644
--- a/data/etc/android.hardware.telephony.cdma.xml
+++ b/data/etc/android.hardware.telephony.cdma.xml
@@ -4,9 +4,9 @@
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.
@@ -17,5 +17,11 @@
<!-- This is the standard set of telephony features for a CDMA phone. -->
<permissions>
<feature name="android.hardware.telephony" />
+ <feature name="android.software.telecom" />
+ <feature name="android.hardware.telephony.radio" />
+ <feature name="android.hardware.telephony.subscription" />
<feature name="android.hardware.telephony.cdma" />
+ <feature name="android.hardware.telephony.calling" />
+ <feature name="android.hardware.telephony.data" />
+ <feature name="android.hardware.telephony.messaging" />
</permissions>
diff --git a/data/etc/android.hardware.telephony.data.xml b/data/etc/android.hardware.telephony.data.xml
new file mode 100644
index 0000000..2716152
--- /dev/null
+++ b/data/etc/android.hardware.telephony.data.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Data API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.radio" />
+ <feature name="android.hardware.telephony.subscription" />
+ <feature name="android.hardware.telephony.data" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.gsm.xml b/data/etc/android.hardware.telephony.gsm.xml
index 7927fa8..e368a06 100644
--- a/data/etc/android.hardware.telephony.gsm.xml
+++ b/data/etc/android.hardware.telephony.gsm.xml
@@ -4,9 +4,9 @@
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.
@@ -17,5 +17,11 @@
<!-- This is the standard set of telephony features for a GSM phone. -->
<permissions>
<feature name="android.hardware.telephony" />
+ <feature name="android.software.telecom" />
+ <feature name="android.hardware.telephony.radio" />
+ <feature name="android.hardware.telephony.subscription" />
<feature name="android.hardware.telephony.gsm" />
+ <feature name="android.hardware.telephony.calling" />
+ <feature name="android.hardware.telephony.data" />
+ <feature name="android.hardware.telephony.messaging" />
</permissions>
diff --git a/data/etc/android.hardware.telephony.radio.xml b/data/etc/android.hardware.telephony.radio.xml
new file mode 100644
index 0000000..4377705
--- /dev/null
+++ b/data/etc/android.hardware.telephony.radio.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Radio Access API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.radio" />
+</permissions>
diff --git a/data/etc/android.hardware.telephony.subscription.xml b/data/etc/android.hardware.telephony.subscription.xml
new file mode 100644
index 0000000..449a2a6
--- /dev/null
+++ b/data/etc/android.hardware.telephony.subscription.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for devices to support Telephony Subscription API. -->
+<permissions>
+ <feature name="android.hardware.telephony" />
+ <feature name="android.hardware.telephony.subscription" />
+</permissions>
diff --git a/data/etc/android.software.telecom.xml b/data/etc/android.software.telecom.xml
new file mode 100644
index 0000000..db28ba6
--- /dev/null
+++ b/data/etc/android.software.telecom.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- This is the standard set of features for devices that support the Telecom API. -->
+<permissions>
+ <feature name="android.software.telecom" />
+</permissions>
diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index cc0ee82..95b8110 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -38,7 +38,7 @@
<feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
- <feature name="android.software.connectionservice" />
+ <feature name="android.software.telecom" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.home_screen" />
<feature name="android.software.companion_device_setup" />
diff --git a/data/etc/go_handheld_core_hardware.xml b/data/etc/go_handheld_core_hardware.xml
index e6db4ad..d601931 100644
--- a/data/etc/go_handheld_core_hardware.xml
+++ b/data/etc/go_handheld_core_hardware.xml
@@ -37,7 +37,7 @@
<feature name="android.hardware.security.model.compatible" />
<!-- basic system services -->
- <feature name="android.software.connectionservice" />
+ <feature name="android.software.telecom" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
<feature name="android.software.input_methods" />
diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml
index 41f1514..68b8def 100644
--- a/data/etc/handheld_core_hardware.xml
+++ b/data/etc/handheld_core_hardware.xml
@@ -42,7 +42,7 @@
<!-- basic system services -->
<feature name="android.software.app_widgets" />
- <feature name="android.software.connectionservice" />
+ <feature name="android.software.telecom" />
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
diff --git a/include/android/input.h b/include/android/input.h
index 6d2c1b3..fbd61b5 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -1385,6 +1385,17 @@
*/
void AInputQueue_finishEvent(AInputQueue* queue, AInputEvent* event, int handled);
+/**
+ * Returns the AInputQueue* object associated with the supplied Java InputQueue
+ * object. The returned native object holds a weak reference to the Java object,
+ * and is only valid as long as the Java object has not yet been disposed. You
+ * should ensure that there is a strong reference to the Java object and that it
+ * has not been disposed before using the returned object.
+ *
+ * Available since API level 33.
+ */
+AInputQueue* AInputQueue_fromJava(JNIEnv* env, jobject inputQueue) __INTRODUCED_IN(33);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 059bc41..3a13104 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -595,6 +595,15 @@
bool enableBackPressure)
__INTRODUCED_IN(31);
+/**
+ * Sets the frame timeline to use.
+ *
+ * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to
+ * the corresponding expected present time and deadline from the frame to be rendered.
+ */
+void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction,
+ int64_t vsyncId) __INTRODUCED_IN(33);
+
__END_DECLS
#endif // ANDROID_SURFACE_CONTROL_H
diff --git a/include/binder/Enum.h b/include/binder/Enum.h
deleted file mode 100644
index 4c25654..0000000
--- a/include/binder/Enum.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#error Do not rely on global include files. All Android cc_* programs are given access to \
- include_dirs for frameworks/native/include via global configuration, but this is legacy \
- configuration. Instead, you should have a direct dependency on libbinder OR one of your \
- dependencies should re-export libbinder headers with export_shared_lib_headers.
diff --git a/include/input/Input.h b/include/input/Input.h
index 54c7114..e1cacac 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; }
@@ -801,8 +812,11 @@
static std::string actionToString(int32_t action);
+ // MotionEvent will transform various axes in different ways, based on the source. For
+ // example, the x and y axes will not have any offsets/translations applied if it comes from a
+ // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods
+ // are used to apply these transformations for different axes.
static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy);
-
static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&,
const PointerCoords&);
@@ -837,15 +851,12 @@
inline bool getHasFocus() const { return mHasFocus; }
- inline bool getInTouchMode() const { return mInTouchMode; }
-
- void initialize(int32_t id, bool hasFocus, bool inTouchMode);
+ void initialize(int32_t id, bool hasFocus);
void initialize(const FocusEvent& from);
protected:
bool mHasFocus;
- bool mInTouchMode;
};
/*
@@ -934,8 +945,8 @@
*/
struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent {
int32_t action;
- nsecs_t downTimeNanos;
int32_t flags;
+ nsecs_t downTimeNanos;
int32_t keyCode;
int32_t scanCode;
int32_t metaState;
@@ -950,8 +961,8 @@
float rawX;
float rawY;
int32_t actionMasked;
- nsecs_t downTimeNanos;
int32_t flags;
+ nsecs_t downTimeNanos;
int32_t metaState;
int32_t buttonState;
};
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index d655b28..edcb615 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -178,10 +178,9 @@
struct Focus {
int32_t eventId;
- // The following 3 fields take up 4 bytes total
+ // The following 2 fields take up 4 bytes total
bool hasFocus;
- bool inTouchMode;
- uint8_t empty[2];
+ uint8_t empty[3];
inline size_t size() const { return sizeof(Focus); }
} focus;
@@ -381,7 +380,7 @@
* Returns DEAD_OBJECT if the channel's peer has been closed.
* Other errors probably indicate that the channel is broken.
*/
- status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode);
+ status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus);
/* Publishes a capture event to the input channel.
*
diff --git a/include/powermanager/PowerHalWrapper.h b/include/powermanager/PowerHalWrapper.h
index 2c6eacb..dfb0ff5 100644
--- a/include/powermanager/PowerHalWrapper.h
+++ b/include/powermanager/PowerHalWrapper.h
@@ -201,10 +201,8 @@
std::array<std::atomic<HalSupport>,
static_cast<int32_t>(hardware::power::Boost::DISPLAY_UPDATE_IMMINENT) + 1>
mBoostSupportedArray GUARDED_BY(mBoostMutex) = {HalSupport::UNKNOWN};
- // Android framework only sends mode upto DISPLAY_INACTIVE.
- // Need to increase the array if more mode supported.
std::array<std::atomic<HalSupport>,
- static_cast<int32_t>(hardware::power::Mode::DISPLAY_INACTIVE) + 1>
+ static_cast<int32_t>(*(android::enum_range<hardware::power::Mode>().end() - 1)) + 1>
mModeSupportedArray GUARDED_BY(mModeMutex) = {HalSupport::UNKNOWN};
};
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
index b74923c..ac3e5b8 100644
--- a/libs/android_runtime_lazy/Android.bp
+++ b/libs/android_runtime_lazy/Android.bp
@@ -42,12 +42,13 @@
cc_library {
name: "libandroid_runtime_lazy",
vendor_available: true,
+ recovery_available: true,
double_loadable: true,
host_supported: true,
target: {
darwin: {
enabled: false,
- }
+ },
},
cflags: [
diff --git a/libs/battery/MultiStateCounter.h b/libs/battery/MultiStateCounter.h
index 03e1f2a..0caf005 100644
--- a/libs/battery/MultiStateCounter.h
+++ b/libs/battery/MultiStateCounter.h
@@ -63,12 +63,24 @@
void setValue(state_t state, const T& value);
/**
- * Updates the value for the current state and returns the delta from the previously
- * set value.
+ * Updates the value by distributing the delta from the previously set value
+ * among states according to their respective time-in-state.
+ * Returns the delta from the previously set value.
*/
const T& updateValue(const T& value, time_t timestamp);
- void addValue(const T& value);
+ /**
+ * Updates the value by distributing the specified increment among states according
+ * to their respective time-in-state.
+ */
+ void incrementValue(const T& increment, time_t timestamp);
+
+ /**
+ * Adds the specified increment to the value for the current state, without affecting
+ * the last updated value or timestamp. Ignores partial time-in-state: the entirety of
+ * the increment is given to the current state.
+ */
+ void addValue(const T& increment);
void reset();
@@ -195,10 +207,18 @@
<< ", which is lower than the previous value " << valueToString(lastValue)
<< "\n";
ALOGE("%s", str.str().c_str());
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
}
} else if (timestamp < lastUpdateTimestamp) {
ALOGE("updateValue is called with an earlier timestamp: %lu, previous: %lu\n",
(unsigned long)timestamp, (unsigned long)lastUpdateTimestamp);
+
+ for (int i = 0; i < stateCount; i++) {
+ states[i].timeInStateSinceUpdate = 0;
+ }
}
}
}
@@ -208,11 +228,17 @@
}
template <class T>
+void MultiStateCounter<T>::incrementValue(const T& increment, time_t timestamp) {
+ T newValue = lastValue;
+ add(&newValue, increment, 1 /* numerator */, 1 /* denominator */);
+ updateValue(newValue, timestamp);
+}
+
+template <class T>
void MultiStateCounter<T>::addValue(const T& value) {
if (!isEnabled) {
return;
}
-
add(&states[currentState].counter, value, 1 /* numerator */, 1 /* denominator */);
}
diff --git a/libs/battery/MultiStateCounterTest.cpp b/libs/battery/MultiStateCounterTest.cpp
index 848fd10..cb11a54 100644
--- a/libs/battery/MultiStateCounterTest.cpp
+++ b/libs/battery/MultiStateCounterTest.cpp
@@ -176,7 +176,7 @@
testCounter.setState(0, 0);
testCounter.updateValue(6.0, 2000);
- // Time moves back. The negative delta from 2000 to 1000 is ignored
+ // Time moves back. The delta over the negative interval from 2000 to 1000 is ignored
testCounter.updateValue(8.0, 1000);
double delta = testCounter.updateValue(11.0, 3000);
@@ -189,6 +189,47 @@
EXPECT_DOUBLE_EQ(3.0, delta);
}
+TEST_F(MultiStateCounterTest, updateValue_nonmonotonic) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ // Value goes down. The negative delta from 6.0 to 4.0 is ignored
+ testCounter.updateValue(4.0, 3000);
+
+ // Value goes up again. The positive delta from 4.0 to 7.0 is accumulated.
+ double delta = testCounter.updateValue(7.0, 4000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(7.0-4.0) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(9.0, testCounter.getCount(0));
+
+ // 7.0-4.0
+ EXPECT_DOUBLE_EQ(3.0, delta);
+}
+
+TEST_F(MultiStateCounterTest, incrementValue) {
+ DoubleMultiStateCounter testCounter(2, 0);
+ testCounter.updateValue(0, 0);
+ testCounter.setState(0, 0);
+ testCounter.updateValue(6.0, 2000);
+
+ testCounter.setState(1, 3000);
+
+ testCounter.incrementValue(8.0, 6000);
+
+ // The total accumulated count is:
+ // 6.0 // For the period 0-2000
+ // +(8.0 * 0.25) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(8.0, testCounter.getCount(0));
+
+ // 0 // For the period 0-3000
+ // +(8.0 * 0.75) // For the period 3000-4000
+ EXPECT_DOUBLE_EQ(6.0, testCounter.getCount(1));
+}
+
TEST_F(MultiStateCounterTest, addValue) {
DoubleMultiStateCounter testCounter(1, 0);
testCounter.updateValue(0, 0);
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 56cf76f..f60b32e 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -25,6 +25,7 @@
name: "libbinder_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
@@ -75,6 +76,7 @@
vndk: {
enabled: true,
},
+ recovery_available: true,
double_loadable: true,
host_supported: true,
// TODO(b/153609531): remove when no longer needed.
@@ -147,6 +149,11 @@
"UtilsHost.cpp",
],
},
+ recovery: {
+ exclude_header_libs: [
+ "libandroid_runtime_vm_headers",
+ ],
+ },
},
aidl: {
@@ -195,7 +202,7 @@
sanitize: {
misc_undefined: ["integer"],
},
- min_sdk_version: "29",
+ min_sdk_version: "30",
tidy: true,
tidy_flags: [
@@ -218,10 +225,7 @@
"portability*",
],
- pgo: {
- sampling: true,
- profile_file: "libbinder/libbinder.profdata",
- },
+ afdo: true,
}
cc_defaults {
diff --git a/libs/binder/OWNERS b/libs/binder/OWNERS
index 1c8bdea..f954e74 100644
--- a/libs/binder/OWNERS
+++ b/libs/binder/OWNERS
@@ -1,5 +1,4 @@
# Bug component: 32456
-arve@google.com
ctate@google.com
hackbod@google.com
maco@google.com
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 181f405..f84f639 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -521,6 +521,25 @@
return memcmp(data(), other.data(), size);
}
+status_t Parcel::compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset,
+ size_t len, int* result) const {
+ if (len > INT32_MAX || thisOffset > INT32_MAX || otherOffset > INT32_MAX) {
+ // Don't accept size_t values which may have come from an inadvertent conversion from a
+ // negative int.
+ return BAD_VALUE;
+ }
+ size_t thisLimit;
+ if (__builtin_add_overflow(thisOffset, len, &thisLimit) || thisLimit > mDataSize) {
+ return BAD_VALUE;
+ }
+ size_t otherLimit;
+ if (__builtin_add_overflow(otherOffset, len, &otherLimit) || otherLimit > other.mDataSize) {
+ return BAD_VALUE;
+ }
+ *result = memcmp(data() + thisOffset, other.data() + otherOffset, len);
+ return NO_ERROR;
+}
+
bool Parcel::allowFds() const
{
return mAllowFds;
@@ -611,6 +630,8 @@
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
+#elif defined(__ANDROID_RECOVERY__)
+constexpr int32_t kHeader = B_PACK_CHARS('R', 'E', 'C', 'O');
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
@@ -718,6 +739,17 @@
}
}
+binder::Status Parcel::enforceNoDataAvail() const {
+ const auto n = dataAvail();
+ if (n == 0) {
+ return binder::Status::ok();
+ }
+ return binder::Status::
+ fromExceptionCode(binder::Status::Exception::EX_BAD_PARCELABLE,
+ String8::format("Parcel data not fully consumed, unread size: %zu",
+ n));
+}
+
size_t Parcel::objectsCount() const
{
return mObjectsSize;
@@ -803,7 +835,7 @@
const size_t padded = pad_size(len);
- // sanity check for integer overflow
+ // check for integer overflow
if (mDataPos+padded < mDataPos) {
return nullptr;
}
@@ -2181,12 +2213,14 @@
type == BINDER_TYPE_FD)) {
// We should never receive other types (eg BINDER_TYPE_FDA) as long as we don't support
// them in libbinder. If we do receive them, it probably means a kernel bug; try to
- // recover gracefully by clearing out the objects, and releasing the objects we do
- // know about.
+ // recover gracefully by clearing out the objects.
android_errorWriteLog(0x534e4554, "135930648");
+ android_errorWriteLog(0x534e4554, "203847542");
ALOGE("%s: unsupported type object (%" PRIu32 ") at offset %" PRIu64 "\n",
__func__, type, (uint64_t)offset);
- releaseObjects();
+
+ // WARNING: callers of ipcSetDataReference need to make sure they
+ // don't rely on mObjectsSize in their release_func.
mObjectsSize = 0;
break;
}
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 4f21cda..9abe4b5 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -176,6 +176,11 @@
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
+ if (mMaxThreads == 0) {
+ ALOGW("Extra binder thread started, but 0 threads requested. Do not use "
+ "*startThreadPool when zero threads are requested.");
+ }
+
mThreadPoolStarted = true;
spawnPooledThread(true);
}
diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp
index 53c9b78..a5a2bb1 100644
--- a/libs/binder/RpcSession.cpp
+++ b/libs/binder/RpcSession.cpp
@@ -29,13 +29,11 @@
#include <android-base/hex.h>
#include <android-base/macros.h>
#include <android-base/scopeguard.h>
-#include <android_runtime/vm.h>
#include <binder/BpBinder.h>
#include <binder/Parcel.h>
#include <binder/RpcServer.h>
#include <binder/RpcTransportRaw.h>
#include <binder/Stability.h>
-#include <jni.h>
#include <utils/String8.h>
#include "FdTrigger.h"
@@ -48,6 +46,11 @@
extern "C" pid_t gettid();
#endif
+#ifndef __ANDROID_RECOVERY__
+#include <android_runtime/vm.h>
+#include <jni.h>
+#endif
+
namespace android {
using base::unique_fd;
@@ -315,6 +318,9 @@
}
namespace {
+#ifdef __ANDROID_RECOVERY__
+class JavaThreadAttacher {};
+#else
// RAII object for attaching / detaching current thread to JVM if Android Runtime exists. If
// Android Runtime doesn't exist, no-op.
class JavaThreadAttacher {
@@ -367,6 +373,7 @@
return fn();
}
};
+#endif
} // namespace
void RpcSession::join(sp<RpcSession>&& session, PreJoinSetupResult&& setupResult) {
@@ -374,7 +381,7 @@
if (setupResult.status == OK) {
LOG_ALWAYS_FATAL_IF(!connection, "must have connection if setup succeeded");
- JavaThreadAttacher javaThreadAttacher;
+ [[maybe_unused]] JavaThreadAttacher javaThreadAttacher;
while (true) {
status_t status = session->state()->getAndExecuteCommand(connection, session,
RpcState::CommandType::ANY);
diff --git a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
index ece7989..bffab5e 100644
--- a/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
+++ b/libs/binder/aidl/android/content/pm/StagedApexInfo.aidl
@@ -27,4 +27,7 @@
@utf8InCpp String diskImagePath;
long versionCode;
@utf8InCpp String versionName;
+ boolean hasBootClassPathJars;
+ boolean hasDex2OatBootClassPathJars;
+ boolean hasSystemServerClassPathJars;
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index ff90b30..f5abb85 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -129,48 +129,50 @@
#endif
-#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
- const ::android::StaticString16 \
- I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
- const ::android::String16 I##INTERFACE::descriptor( \
- I##INTERFACE##_descriptor_static_str16); \
- const ::android::String16& \
- I##INTERFACE::getInterfaceDescriptor() const { \
- return I##INTERFACE::descriptor; \
- } \
- ::android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
- const ::android::sp<::android::IBinder>& obj) \
- { \
- ::android::sp<I##INTERFACE> intr; \
- if (obj != nullptr) { \
- intr = ::android::sp<I##INTERFACE>::cast( \
- obj->queryLocalInterface(I##INTERFACE::descriptor)); \
- if (intr == nullptr) { \
- intr = ::android::sp<Bp##INTERFACE>::make(obj); \
- } \
- } \
- return intr; \
- } \
- std::unique_ptr<I##INTERFACE> I##INTERFACE::default_impl; \
- bool I##INTERFACE::setDefaultImpl(std::unique_ptr<I##INTERFACE> impl)\
- { \
- /* Only one user of this interface can use this function */ \
- /* at a time. This is a heuristic to detect if two different */ \
- /* users in the same process use this function. */ \
- assert(!I##INTERFACE::default_impl); \
- if (impl) { \
- I##INTERFACE::default_impl = std::move(impl); \
- return true; \
- } \
- return false; \
- } \
- const std::unique_ptr<I##INTERFACE>& I##INTERFACE::getDefaultImpl() \
- { \
- return I##INTERFACE::default_impl; \
- } \
- I##INTERFACE::I##INTERFACE() { } \
- I##INTERFACE::~I##INTERFACE() { } \
+// Macro to be used by both IMPLEMENT_META_INTERFACE and IMPLEMENT_META_NESTED_INTERFACE
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(ITYPE, INAME, BPTYPE) \
+ const ::android::String16& ITYPE::getInterfaceDescriptor() const { return ITYPE::descriptor; } \
+ ::android::sp<ITYPE> ITYPE::asInterface(const ::android::sp<::android::IBinder>& obj) { \
+ ::android::sp<ITYPE> intr; \
+ if (obj != nullptr) { \
+ intr = ::android::sp<ITYPE>::cast(obj->queryLocalInterface(ITYPE::descriptor)); \
+ if (intr == nullptr) { \
+ intr = ::android::sp<BPTYPE>::make(obj); \
+ } \
+ } \
+ return intr; \
+ } \
+ std::unique_ptr<ITYPE> ITYPE::default_impl; \
+ bool ITYPE::setDefaultImpl(std::unique_ptr<ITYPE> impl) { \
+ /* Only one user of this interface can use this function */ \
+ /* at a time. This is a heuristic to detect if two different */ \
+ /* users in the same process use this function. */ \
+ assert(!ITYPE::default_impl); \
+ if (impl) { \
+ ITYPE::default_impl = std::move(impl); \
+ return true; \
+ } \
+ return false; \
+ } \
+ const std::unique_ptr<ITYPE>& ITYPE::getDefaultImpl() { return ITYPE::default_impl; } \
+ ITYPE::INAME() {} \
+ ITYPE::~INAME() {}
+// Macro for an interface type.
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ const ::android::StaticString16 I##INTERFACE##_descriptor_static_str16( \
+ __IINTF_CONCAT(u, NAME)); \
+ const ::android::String16 I##INTERFACE::descriptor(I##INTERFACE##_descriptor_static_str16); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(I##INTERFACE, I##INTERFACE, Bp##INTERFACE)
+
+// Macro for "nested" interface type.
+// For example,
+// class Parent .. { class INested .. { }; };
+// DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(Parent, Nested, "Parent.INested")
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_NESTED_INTERFACE(PARENT, INTERFACE, NAME) \
+ const ::android::String16 PARENT::I##INTERFACE::descriptor(NAME); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE0(PARENT::I##INTERFACE, I##INTERFACE, \
+ PARENT::Bp##INTERFACE)
#define CHECK_INTERFACE(interface, data, reply) \
do { \
@@ -226,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/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index d90e803..450e388 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -16,6 +16,7 @@
#pragma once
+#include <array>
#include <map> // for legacy reasons
#include <string>
#include <type_traits>
@@ -53,6 +54,9 @@
class RpcSession;
class String8;
class TextOutput;
+namespace binder {
+class Status;
+}
class Parcel {
friend class IPCThreadState;
@@ -81,6 +85,8 @@
size_t start, size_t len);
int compareData(const Parcel& other);
+ status_t compareDataInRange(size_t thisOffset, const Parcel& other, size_t otherOffset,
+ size_t length, int* result) const;
bool allowFds() const;
bool pushAllowFds(bool allowFds);
@@ -128,6 +134,10 @@
IPCThreadState* threadState = nullptr) const;
bool checkInterface(IBinder*) const;
+ // Verify there are no bytes left to be read on the Parcel.
+ // Returns Status(EX_BAD_PARCELABLE) when the Parcel is not consumed.
+ binder::Status enforceNoDataAvail() const;
+
void freeData();
size_t objectsCount() const;
@@ -205,6 +215,32 @@
status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) __attribute__((deprecated("use std::optional version instead")));
status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val);
+ // Write an IInterface or a vector of IInterface's
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinder(const sp<T>& val) {
+ return writeStrongBinder(T::asBinder(val));
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinderVector(const std::vector<sp<T>>& val) {
+ return writeData(val);
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t writeStrongBinderVector(const std::optional<std::vector<sp<T>>>& val) {
+ return writeData(val);
+ }
+
+ template <typename T, size_t N>
+ status_t writeFixedArray(const std::array<T, N>& val) {
+ return writeData(val);
+ }
+ template <typename T, size_t N>
+ status_t writeFixedArray(const std::optional<std::array<T, N>>& val) {
+ return writeData(val);
+ }
+
// Write an Enum vector with underlying type int8_t.
// Does not use padding; each byte is contiguous.
template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0>
@@ -419,6 +455,16 @@
status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const;
status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const;
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t readStrongBinderVector(std::vector<sp<T>>* val) const {
+ return readData(val);
+ }
+ template <typename T,
+ std::enable_if_t<std::is_base_of_v<::android::IInterface, T>, bool> = true>
+ status_t readStrongBinderVector(std::optional<std::vector<sp<T>>>* val) const {
+ return readData(val);
+ }
status_t readByteVector(std::optional<std::vector<int8_t>>* val) const;
status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const __attribute__((deprecated("use std::optional version instead")));
@@ -458,6 +504,15 @@
std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const __attribute__((deprecated("use std::optional version instead")));
status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const;
+ template <typename T, size_t N>
+ status_t readFixedArray(std::array<T, N>* val) const {
+ return readData(val);
+ }
+ template <typename T, size_t N>
+ status_t readFixedArray(std::optional<std::array<T, N>>* val) const {
+ return readData(val);
+ }
+
template<typename T>
status_t read(Flattenable<T>& val) const;
@@ -789,6 +844,16 @@
|| is_specialization_v<T, std::unique_ptr>
|| is_specialization_v<T, std::shared_ptr>;
+ // Tells if T is a fixed-size array.
+ template <typename T>
+ struct is_fixed_array : std::false_type {};
+
+ template <typename T, size_t N>
+ struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+ template <typename T>
+ static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
// special int32 value to indicate NonNull or Null parcelables
// This is fixed to be only 0 or 1 by contract, do not change.
static constexpr int32_t kNonNullParcelableFlag = 1;
@@ -893,7 +958,9 @@
if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
} else if constexpr (std::is_base_of_v<Parcelable, T>) {
if (!c) return writeData(static_cast<int32_t>(kNullParcelableFlag));
- } else /* constexpr */ { // could define this, but raise as error.
+ } else if constexpr (is_fixed_array_v<T>) {
+ if (!c) return writeData(static_cast<int32_t>(kNullVectorSize));
+ } else /* constexpr */ { // could define this, but raise as error.
static_assert(dependent_false_v<CT>);
}
return writeData(*c);
@@ -932,6 +999,23 @@
return OK;
}
+ template <typename T, size_t N>
+ status_t writeData(const std::array<T, N>& val) {
+ static_assert(N <= std::numeric_limits<int32_t>::max());
+ status_t status = writeData(static_cast<int32_t>(N));
+ if (status != OK) return status;
+ if constexpr (is_pointer_equivalent_array_v<T>) {
+ static_assert(N <= std::numeric_limits<size_t>::max() / sizeof(T));
+ return write(val.data(), val.size() * sizeof(T));
+ } else /* constexpr */ {
+ for (const auto& t : val) {
+ status = writeData(t);
+ if (status != OK) return status;
+ }
+ return OK;
+ }
+ }
+
// readData function overloads.
// Implementation detail: Function overloading improves code readability over
// template overloading, but prevents readData<T> from being used for those types.
@@ -1024,9 +1108,8 @@
int32_t peek;
status_t status = readData(&peek);
if (status != OK) return status;
- if constexpr (is_specialization_v<T, std::vector>
- || std::is_same_v<T, String16>
- || std::is_same_v<T, std::string>) {
+ if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T> ||
+ std::is_same_v<T, String16> || std::is_same_v<T, std::string>) {
if (peek == kNullVectorSize) {
c->reset();
return OK;
@@ -1036,12 +1119,15 @@
c->reset();
return OK;
}
- } else /* constexpr */ { // could define this, but raise as error.
+ } else /* constexpr */ { // could define this, but raise as error.
static_assert(dependent_false_v<CT>);
}
// create a new object.
if constexpr (is_specialization_v<CT, std::optional>) {
- c->emplace();
+ // Call default constructor explicitly
+ // - Clang bug: https://bugs.llvm.org/show_bug.cgi?id=35748
+ // std::optional::emplace() doesn't work with nested types.
+ c->emplace(T());
} else /* constexpr */ {
T* const t = new (std::nothrow) T; // contents read from Parcel below.
if (t == nullptr) return NO_MEMORY;
@@ -1050,7 +1136,7 @@
// rewind data ptr to reread (this is pretty quick), otherwise we could
// pass an optional argument to readData to indicate a peeked value.
setDataPosition(startPos);
- if constexpr (is_specialization_v<T, std::vector>) {
+ if constexpr (is_specialization_v<T, std::vector> || is_fixed_array_v<T>) {
return readData(&**c, READ_FLAG_SP_NULLABLE); // nullable sp<> allowed now
} else {
return readData(&**c);
@@ -1113,6 +1199,41 @@
return OK;
}
+ template <typename T, size_t N>
+ status_t readData(std::array<T, N>* val, ReadFlags readFlags = READ_FLAG_NONE) const {
+ static_assert(N <= std::numeric_limits<int32_t>::max());
+ int32_t size;
+ status_t status = readInt32(&size);
+ if (status != OK) return status;
+ if (size < 0) return UNEXPECTED_NULL;
+ if (size != static_cast<int32_t>(N)) return BAD_VALUE;
+ if constexpr (is_pointer_equivalent_array_v<T>) {
+ auto data = reinterpret_cast<const T*>(readInplace(N * sizeof(T)));
+ if (data == nullptr) return BAD_VALUE;
+ memcpy(val->data(), data, N * sizeof(T));
+ } else if constexpr (is_specialization_v<T, sp>) {
+ for (auto& t : *val) {
+ if (readFlags & READ_FLAG_SP_NULLABLE) {
+ status = readNullableStrongBinder(&t); // allow nullable
+ } else {
+ status = readStrongBinder(&t);
+ }
+ if (status != OK) return status;
+ }
+ } else if constexpr (is_fixed_array_v<T>) { // pass readFlags down to nested arrays
+ for (auto& t : *val) {
+ status = readData(&t, readFlags);
+ if (status != OK) return status;
+ }
+ } else /* constexpr */ {
+ for (auto& t : *val) {
+ status = readData(&t);
+ if (status != OK) return status;
+ }
+ }
+ return OK;
+ }
+
//-----------------------------------------------------------------------------
private:
diff --git a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
index 34f1cbf..5baa4d7 100644
--- a/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
+++ b/libs/binder/include_rpc_unstable/binder_rpc_unstable.hpp
@@ -32,9 +32,11 @@
bool RunRpcServerCallback(AIBinder* service, unsigned int port, void (*readyCallback)(void* param),
void* param);
-// Starts an RPC server on a given port and a given root IBinder object.
-// This function sets up the server, calls readyCallback with a given param, and
-// then joins before returning.
+// Starts an RPC server on a given port and a given root IBinder factory.
+// RunRpcServerWithFactory acts like RunRpcServerCallback, but instead of
+// assigning single root IBinder object to all connections, factory is called
+// whenever a client connects, making it possible to assign unique IBinder
+// object to each client.
bool RunRpcServerWithFactory(AIBinder* (*factory)(unsigned int cid, void* context),
void* factoryContext, unsigned int port);
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index 9c04e58..4289574 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -38,7 +38,7 @@
host: {
cflags: [
"-D__INTRODUCED_IN(n)=",
- "-D__assert(a,b,c)=",
+ "-D__assert(a,b,c)=do { syslog(LOG_ERR, a \": \" c); abort(); } while(false)",
// We want all the APIs to be available on the host.
"-D__ANDROID_API__=10000",
],
@@ -54,6 +54,7 @@
defaults: ["libbinder_ndk_host_user"],
host_supported: true,
+ recovery_available: true,
llndk: {
symbol_file: "libbinder_ndk.map.txt",
@@ -155,6 +156,7 @@
name: "libbinder_headers_platform_shared",
export_include_dirs: ["include_cpp"],
vendor_available: true,
+ recovery_available: true,
host_supported: true,
// TODO(b/153609531): remove when no longer needed.
native_bridge_supported: true,
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 81aa551..28e3ff4 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -104,6 +104,17 @@
return {};
}
+// b/175635923 libcxx causes "implicit-conversion" with a string with invalid char
+static std::string SanitizeString(const String16& str) {
+ std::string sanitized{String8(str)};
+ for (auto& c : sanitized) {
+ if (!isprint(c)) {
+ c = '?';
+ }
+ }
+ return sanitized;
+}
+
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
if (clazz == nullptr) return false;
@@ -118,7 +129,7 @@
if (descriptor != newDescriptor) {
if (getBinder()->isBinderAlive()) {
LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor
- << "' but descriptor is actually '" << descriptor << "'.";
+ << "' but descriptor is actually '" << SanitizeString(descriptor) << "'.";
} else {
// b/155793159
LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor
@@ -555,6 +566,10 @@
return ::android::IPCThreadState::self()->getCallingPid();
}
+bool AIBinder_isHandlingTransaction() {
+ return ::android::IPCThreadState::self()->getServingStackPointer() != nullptr;
+}
+
void AIBinder_incStrong(AIBinder* binder) {
if (binder == nullptr) {
return;
@@ -784,3 +799,12 @@
void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) {
binder->asABBinder()->setMinSchedulerPolicy(policy, priority);
}
+
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) {
+ ABBinder* localBinder = binder->asABBinder();
+ if (localBinder == nullptr) {
+ LOG(FATAL) << "AIBinder_setInheritRt must be called on a local binder";
+ }
+
+ localBinder->setInheritRt(inheritRt);
+}
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/ndk/include_cpp/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
index 2b18a0a..0bf1e3d 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h
@@ -27,15 +27,146 @@
#pragma once
#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
#include <android/binder_internal_logging.h>
#include <android/binder_parcel.h>
+#include <array>
#include <optional>
#include <string>
+#include <type_traits>
#include <vector>
namespace ndk {
+namespace {
+template <typename Test, template <typename...> class Ref>
+struct is_specialization : std::false_type {};
+
+template <template <typename...> class Ref, typename... Args>
+struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
+
+template <typename Test, template <typename...> class Ref>
+static inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
+
+// Get the first template type from a container, the T from MyClass<T, ...>.
+template <typename T>
+struct first_template_type {
+ using type = void;
+};
+
+template <template <typename...> class V, typename T, typename... Args>
+struct first_template_type<V<T, Args...>> {
+ using type = T;
+};
+
+template <typename T>
+using first_template_type_t = typename first_template_type<T>::type;
+
+// Tells if T represents NDK interface (shared_ptr<ICInterface-derived>)
+template <typename T>
+static inline constexpr bool is_interface_v = is_specialization_v<T, std::shared_ptr>&&
+ std::is_base_of_v<::ndk::ICInterface, first_template_type_t<T>>;
+
+// Tells if T represents NDK parcelable with readFromParcel/writeToParcel methods defined
+template <typename T, typename = void>
+struct is_parcelable : std::false_type {};
+
+template <typename T>
+struct is_parcelable<
+ T, std::void_t<decltype(std::declval<T>().readFromParcel(std::declval<const AParcel*>())),
+ decltype(std::declval<T>().writeToParcel(std::declval<AParcel*>()))>>
+ : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_parcelable_v = is_parcelable<T>::value;
+
+// Tells if T represents nullable NDK parcelable (optional<parcelable> or unique_ptr<parcelable>)
+template <typename T>
+static inline constexpr bool is_nullable_parcelable_v = is_parcelable_v<first_template_type_t<T>> &&
+ (is_specialization_v<T, std::optional> ||
+ is_specialization_v<T, std::unique_ptr>);
+
+// Tells if T is a fixed-size array.
+template <typename T>
+struct is_fixed_array : std::false_type {};
+
+template <typename T, size_t N>
+struct is_fixed_array<std::array<T, N>> : std::true_type {};
+
+template <typename T>
+static inline constexpr bool is_fixed_array_v = is_fixed_array<T>::value;
+
+template <typename T>
+static inline constexpr bool dependent_false_v = false;
+} // namespace
+
+/**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_stdArrayAllocator(void* arrayData, int32_t length, T** outBuffer) {
+ if (length < 0) return false;
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ *outBuffer = arr->data();
+ return true;
+}
+
+/**
+ * This checks the length against the array size and retrieves the buffer. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayAllocator(void* arrayData, int32_t length,
+ T** outBuffer) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+ if (length < 0) {
+ *arr = std::nullopt;
+ return true;
+ }
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ arr->emplace();
+ *outBuffer = (*arr)->data();
+ return true;
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayExternalAllocator(void* arrayData, int32_t length) {
+ (void)arrayData;
+ return length == static_cast<int32_t>(N);
+}
+
+/**
+ * This checks the length against the array size. No allocation required.
+ */
+template <typename T, size_t N>
+static inline bool AParcel_nullableStdArrayExternalAllocator(void* arrayData, int32_t length) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+
+ if (length < 0) {
+ *arr = std::nullopt;
+ return true;
+ }
+
+ if (length != static_cast<int32_t>(N)) {
+ return false;
+ }
+
+ arr->emplace();
+ return true;
+}
+
/**
* This retrieves and allocates a vector to size 'length' and returns the underlying buffer.
*/
@@ -345,6 +476,118 @@
}
/**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_stdArrayGetter(const void* arrData, size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrData);
+ return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_stdArraySetter(void* arrData, size_t index, T value) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrData);
+ (*arr)[index] = value;
+}
+
+/**
+ * This retrieves the underlying value in a std::array which may not be contiguous at index from a
+ * corresponding arrData.
+ */
+template <typename T, size_t N>
+static inline T AParcel_nullableStdArrayGetter(const void* arrData, size_t index) {
+ const std::optional<std::array<T, N>>* arr =
+ static_cast<const std::optional<std::array<T, N>>*>(arrData);
+ return (*arr)[index];
+}
+
+/**
+ * This sets the underlying value in a corresponding arrData which may not be contiguous at
+ * index.
+ */
+template <typename T, size_t N>
+static inline void AParcel_nullableStdArraySetter(void* arrData, size_t index, T value) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrData);
+ (*arr)->at(index) = value;
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::string, N> at index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::array<std::string, N>* arr = static_cast<std::array<std::string, N>*>(arrData);
+ std::string& element = arr->at(index);
+ return AParcel_stdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a std::array<std::string, N> at index
+ * 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayStringElementGetter(const void* arrData, size_t index,
+ int32_t* outLength) {
+ const std::array<std::string, N>* arr = static_cast<const std::array<std::string, N>*>(arrData);
+ const std::string& element = arr->at(index);
+
+ *outLength = static_cast<int32_t>(element.size());
+ return element.c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::array<std::optional<std::string>, N> at index 'index' to
+ * size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_stdArrayNullableStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::array<std::optional<std::string>, N>* arr =
+ static_cast<std::array<std::optional<std::string>, N>*>(arrData);
+ std::optional<std::string>& element = arr->at(index);
+ return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
+ * This gets the length and buffer of a std::string inside of a
+ * std::array<std::optional<std::string>, N> at index 'index'.
+ */
+template <size_t N>
+static const char* AParcel_stdArrayNullableStringElementGetter(const void* arrData, size_t index,
+ int32_t* outLength) {
+ const std::array<std::optional<std::string>, N>* arr =
+ static_cast<const std::array<std::optional<std::string>, N>*>(arrData);
+ const std::optional<std::string>& element = arr->at(index);
+
+ if (!element) {
+ *outLength = -1;
+ return nullptr;
+ }
+
+ *outLength = static_cast<int32_t>(element->size());
+ return element->c_str();
+}
+
+/**
+ * Allocates a std::string inside of std::optional<std::array<std::optional<std::string>, N>> at
+ * index 'index' to size 'length'.
+ */
+template <size_t N>
+static inline bool AParcel_nullableStdArrayStringElementAllocator(void* arrData, size_t index,
+ int32_t length, char** buffer) {
+ std::optional<std::array<std::optional<std::string>, N>>* arr =
+ static_cast<std::optional<std::array<std::optional<std::string>, N>>*>(arrData);
+ std::optional<std::string>& element = (*arr)->at(index);
+ return AParcel_nullableStdStringAllocator(static_cast<void*>(&element), length, buffer);
+}
+
+/**
* Convenience API for writing a std::string.
*/
static inline binder_status_t AParcel_writeString(AParcel* parcel, const std::string& str) {
@@ -429,11 +672,17 @@
*/
template <typename P>
static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
+ if constexpr (is_interface_v<P>) {
+ // Legacy behavior: allow null
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_parcelable_v<P>);
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p.writeToParcel(parcel);
}
- return p.writeToParcel(parcel);
}
/**
@@ -441,85 +690,134 @@
*/
template <typename P>
static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
+ if constexpr (is_interface_v<P>) {
+ // Legacy behavior: allow null
+ return first_template_type_t<P>::readFromParcel(parcel, p);
+ } else {
+ static_assert(is_parcelable_v<P>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return p->readFromParcel(parcel);
}
- if (null == 0) {
- return STATUS_UNEXPECTED_NULL;
- }
- return p->readFromParcel(parcel);
}
/**
* Convenience API for writing a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::optional<P>& p) {
- if (p == std::nullopt) {
- return AParcel_writeInt32(parcel, 0); // null
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel, const P& p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::writeToParcel(parcel, p);
+ } else {
+ static_assert(is_nullable_parcelable_v<P>);
+ if (!p) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
}
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
-}
-
-/**
- * Convenience API for writing a nullable parcelable.
- */
-template <typename P>
-static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
- const std::unique_ptr<P>& p) {
- if (!p) {
- return AParcel_writeInt32(parcel, 0); // null
- }
- binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
- if (status != STATUS_OK) {
- return status;
- }
- return p->writeToParcel(parcel);
}
/**
* Convenience API for reading a nullable parcelable.
*/
template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::optional<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel, P* p) {
+ if constexpr (is_interface_v<P>) {
+ return first_template_type_t<P>::readFromParcel(parcel, p);
+ } else if constexpr (is_specialization_v<P, std::optional>) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ *p = std::nullopt;
+ return STATUS_OK;
+ }
+ p->emplace(first_template_type_t<P>());
+ return (*p)->readFromParcel(parcel);
+ } else {
+ static_assert(is_specialization_v<P, std::unique_ptr>);
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ p->reset();
+ return STATUS_OK;
+ }
+ *p = std::make_unique<first_template_type_t<P>>();
+ return (*p)->readFromParcel(parcel);
}
- if (null == 0) {
- *p = std::nullopt;
- return STATUS_OK;
- }
- *p = std::optional<P>(P{});
- return (*p)->readFromParcel(parcel);
+}
+
+// Forward decls
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value);
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value);
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value);
+
+/**
+ * Reads an object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayData(const AParcel* parcel, void* arrayData, size_t index) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ return AParcel_readData(parcel, &arr->at(index));
}
/**
- * Convenience API for reading a nullable parcelable.
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
*/
-template <typename P>
-static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
- std::unique_ptr<P>* p) {
- int32_t null;
- binder_status_t status = AParcel_readInt32(parcel, &null);
- if (status != STATUS_OK) {
- return status;
- }
- if (null == 0) {
- p->reset();
- return STATUS_OK;
- }
- *p = std::make_unique<P>();
- return (*p)->readFromParcel(parcel);
+template <typename T, size_t N>
+binder_status_t AParcel_readStdArrayNullableData(const AParcel* parcel, void* arrayData,
+ size_t index) {
+ std::array<T, N>* arr = static_cast<std::array<T, N>*>(arrayData);
+ return AParcel_readNullableData(parcel, &arr->at(index));
+}
+
+/**
+ * Reads a nullable object of type T inside a std::array<T, N> at index 'index' from 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_readNullableStdArrayNullableData(const AParcel* parcel, void* arrayData,
+ size_t index) {
+ std::optional<std::array<T, N>>* arr = static_cast<std::optional<std::array<T, N>>*>(arrayData);
+ return AParcel_readNullableData(parcel, &(*arr)->at(index));
+}
+
+/**
+ * Writes an object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayData(AParcel* parcel, const void* arrayData, size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+ return AParcel_writeData(parcel, arr->at(index));
+}
+
+/**
+ * Writes a nullable object of type T inside a std::array<T, N> at index 'index' to 'parcel'.
+ */
+template <typename T, size_t N>
+binder_status_t AParcel_writeStdArrayNullableData(AParcel* parcel, const void* arrayData,
+ size_t index) {
+ const std::array<T, N>* arr = static_cast<const std::array<T, N>*>(arrayData);
+ return AParcel_writeNullableData(parcel, arr->at(index));
}
/**
@@ -665,9 +963,25 @@
*/
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<P>& vec) {
- const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
- AParcel_writeStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_writeInt32Array(parcel, reinterpret_cast<const int32_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_writeInt64Array(parcel, reinterpret_cast<const int64_t*>(vec.data()),
+ static_cast<int32_t>(vec.size()));
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+ const void* vectorData = static_cast<const void*>(&vec);
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec.size()),
+ AParcel_writeStdVectorParcelableElement<P>);
+ }
}
/**
@@ -675,9 +989,24 @@
*/
template <typename P>
static inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<P>* vec) {
- void* vectorData = static_cast<void*>(vec);
- return AParcel_readParcelableArray(parcel, vectorData, AParcel_stdVectorExternalAllocator<P>,
- AParcel_readStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ void* vectorData = static_cast<void*>(vec);
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_readInt32Array(parcel, vectorData, AParcel_stdVectorAllocator<int32_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_readInt64Array(parcel, vectorData, AParcel_stdVectorAllocator<int64_t>);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::string>, "specialization should be used");
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readParcelableArray(parcel, vectorData,
+ AParcel_stdVectorExternalAllocator<P>,
+ AParcel_readStdVectorParcelableElement<P>);
+ }
}
/**
@@ -686,10 +1015,30 @@
template <typename P>
static inline binder_status_t AParcel_writeVector(AParcel* parcel,
const std::optional<std::vector<P>>& vec) {
- if (!vec) return AParcel_writeInt32(parcel, -1);
- const void* vectorData = static_cast<const void*>(&vec);
- return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
- AParcel_writeNullableStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_writeByteArray(
+ parcel, vec ? reinterpret_cast<const int8_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_writeInt32Array(
+ parcel, vec ? reinterpret_cast<const int32_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_writeInt64Array(
+ parcel, vec ? reinterpret_cast<const int64_t*>(vec->data()) : nullptr,
+ vec ? static_cast<int32_t>(vec->size()) : -1);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::optional<std::string>>,
+ "specialization should be used");
+ if (!vec) return AParcel_writeInt32(parcel, -1);
+ const void* vectorData = static_cast<const void*>(&vec);
+ return AParcel_writeParcelableArray(parcel, vectorData, static_cast<int32_t>(vec->size()),
+ AParcel_writeNullableStdVectorParcelableElement<P>);
+ }
}
/**
@@ -698,10 +1047,28 @@
template <typename P>
static inline binder_status_t AParcel_readVector(const AParcel* parcel,
std::optional<std::vector<P>>* vec) {
- void* vectorData = static_cast<void*>(vec);
- return AParcel_readParcelableArray(parcel, vectorData,
- AParcel_nullableStdVectorExternalAllocator<P>,
- AParcel_readNullableStdVectorParcelableElement<P>);
+ if constexpr (std::is_enum_v<P>) {
+ void* vectorData = static_cast<void*>(vec);
+ if constexpr (std::is_same_v<std::underlying_type_t<P>, int8_t>) {
+ return AParcel_readByteArray(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int8_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int32_t>) {
+ return AParcel_readInt32Array(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int32_t>);
+ } else if constexpr (std::is_same_v<std::underlying_type_t<P>, int64_t>) {
+ return AParcel_readInt64Array(parcel, vectorData,
+ AParcel_nullableStdVectorAllocator<int64_t>);
+ } else {
+ static_assert(dependent_false_v<P>, "unrecognized type");
+ }
+ } else {
+ static_assert(!std::is_same_v<P, std::optional<std::string>>,
+ "specialization should be used");
+ void* vectorData = static_cast<void*>(vec);
+ return AParcel_readParcelableArray(parcel, vectorData,
+ AParcel_nullableStdVectorExternalAllocator<P>,
+ AParcel_readNullableStdVectorParcelableElement<P>);
+ }
}
// @START
@@ -1083,6 +1450,294 @@
return STATUS_OK;
}
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArray(AParcel* parcel,
+ const std::array<T, N>& arr) {
+ if constexpr (std::is_same_v<T, bool>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeBoolArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayGetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(arr.data()),
+ static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_writeCharArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_writeInt32Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_writeInt64Array(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_writeFloatArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_writeDoubleArray(parcel, arr.data(), static_cast<int32_t>(arr.size()));
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayStringElementGetter<N>);
+ } else {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_writeStdArrayData<T, N>);
+ }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeFixedArrayWithNullableData(AParcel* parcel,
+ const std::array<T, N>& arr) {
+ if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+ std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+ std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+ return AParcel_writeFixedArray(parcel, arr);
+ } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeStringArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_stdArrayNullableStringElementGetter<N>);
+ } else {
+ const void* arrayData = static_cast<const void*>(&arr);
+ return AParcel_writeParcelableArray(parcel, arrayData, static_cast<int32_t>(N),
+ &AParcel_writeStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Writes a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_writeNullableFixedArrayWithNullableData(
+ AParcel* parcel, const std::optional<std::array<T, N>>& arr) {
+ if (!arr) return AParcel_writeInt32(parcel, -1);
+ return AParcel_writeFixedArrayWithNullableData(parcel, arr.value());
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArray(const AParcel* parcel, std::array<T, N>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool>) {
+ return AParcel_readBoolArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArraySetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_readByteArray(parcel, arrayData, &AParcel_stdArrayAllocator<int8_t, N>);
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_readCharArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_readInt32Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_readInt64Array(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_readFloatArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_readDoubleArray(parcel, arrayData, &AParcel_stdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArrayStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_readStdArrayData<T, N>);
+ }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readFixedArrayWithNullableData(const AParcel* parcel,
+ std::array<T, N>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, uint8_t> ||
+ std::is_same_v<T, char16_t> || std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, int64_t> || std::is_same_v<T, float> ||
+ std::is_same_v<T, double> || std::is_same_v<T, std::string>) {
+ return AParcel_readFixedArray(parcel, arr);
+ } else if constexpr (std::is_same_v<T, std::optional<std::string>>) {
+ return AParcel_readStringArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_stdArrayNullableStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData, &AParcel_stdArrayExternalAllocator<N>,
+ &AParcel_readStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Reads a fixed-size array of T.
+ */
+template <typename T, size_t N>
+static inline binder_status_t AParcel_readNullableFixedArrayWithNullableData(
+ const AParcel* parcel, std::optional<std::array<T, N>>* arr) {
+ void* arrayData = static_cast<void*>(arr);
+ if constexpr (std::is_same_v<T, bool>) {
+ return AParcel_readBoolArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<T, N>,
+ &AParcel_nullableStdArraySetter<T, N>);
+ } else if constexpr (std::is_same_v<T, uint8_t>) {
+ return AParcel_readByteArray(parcel, arrayData,
+ &AParcel_nullableStdArrayAllocator<int8_t, N>);
+ } else if constexpr (std::is_same_v<T, char16_t>) {
+ return AParcel_readCharArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ return AParcel_readInt32Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ return AParcel_readInt64Array(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, float>) {
+ return AParcel_readFloatArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, double>) {
+ return AParcel_readDoubleArray(parcel, arrayData, &AParcel_nullableStdArrayAllocator<T, N>);
+ } else if constexpr (std::is_same_v<T, std::string>) {
+ return AParcel_readStringArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<N>,
+ &AParcel_nullableStdArrayStringElementAllocator<N>);
+ } else {
+ return AParcel_readParcelableArray(parcel, arrayData,
+ &AParcel_nullableStdArrayExternalAllocator<T, N>,
+ &AParcel_readStdArrayNullableData<T, N>);
+ }
+}
+
+/**
+ * Convenience API for writing a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeData(AParcel* parcel, const T& value) {
+ if constexpr (is_specialization_v<T, std::vector>) {
+ return AParcel_writeVector(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) {
+ return AParcel_writeFixedArray(parcel, value);
+ } else if constexpr (std::is_same_v<std::string, T>) {
+ return AParcel_writeString(parcel, value);
+ } else if constexpr (std::is_same_v<bool, T>) {
+ return AParcel_writeBool(parcel, value);
+ } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+ return AParcel_writeByte(parcel, value);
+ } else if constexpr (std::is_same_v<char16_t, T>) {
+ return AParcel_writeChar(parcel, value);
+ } else if constexpr (std::is_same_v<int32_t, T>) {
+ return AParcel_writeInt32(parcel, value);
+ } else if constexpr (std::is_same_v<int64_t, T>) {
+ return AParcel_writeInt64(parcel, value);
+ } else if constexpr (std::is_same_v<float, T>) {
+ return AParcel_writeFloat(parcel, value);
+ } else if constexpr (std::is_same_v<double, T>) {
+ return AParcel_writeDouble(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_writeRequiredParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_writeRequiredStrongBinder(parcel, value);
+ } else if constexpr (std::is_enum_v<T>) {
+ return AParcel_writeData(parcel, static_cast<std::underlying_type_t<T>>(value));
+ } else if constexpr (is_interface_v<T>) {
+ return AParcel_writeParcelable(parcel, value);
+ } else if constexpr (is_parcelable_v<T>) {
+ return AParcel_writeParcelable(parcel, value);
+ } else {
+ static_assert(dependent_false_v<T>, "unrecognized type");
+ return STATUS_OK;
+ }
+}
+
+/**
+ * Convenience API for writing a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_writeNullableData(AParcel* parcel, const T& value) {
+ if constexpr (is_specialization_v<T, std::optional> &&
+ is_specialization_v<first_template_type_t<T>, std::vector>) {
+ return AParcel_writeVector(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ is_fixed_array_v<first_template_type_t<T>>) {
+ return AParcel_writeNullableFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array.
+ return AParcel_writeFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ std::is_same_v<first_template_type_t<T>, std::string>) {
+ return AParcel_writeString(parcel, value);
+ } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+ return AParcel_writeNullableParcelable(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_writeNullableParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_writeNullableStrongBinder(parcel, value);
+ } else {
+ return AParcel_writeData(parcel, value);
+ }
+}
+
+/**
+ * Convenience API for reading a value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readData(const AParcel* parcel, T* value) {
+ if constexpr (is_specialization_v<T, std::vector>) {
+ return AParcel_readVector(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) {
+ return AParcel_readFixedArray(parcel, value);
+ } else if constexpr (std::is_same_v<std::string, T>) {
+ return AParcel_readString(parcel, value);
+ } else if constexpr (std::is_same_v<bool, T>) {
+ return AParcel_readBool(parcel, value);
+ } else if constexpr (std::is_same_v<int8_t, T> || std::is_same_v<uint8_t, T>) {
+ return AParcel_readByte(parcel, value);
+ } else if constexpr (std::is_same_v<char16_t, T>) {
+ return AParcel_readChar(parcel, value);
+ } else if constexpr (std::is_same_v<int32_t, T>) {
+ return AParcel_readInt32(parcel, value);
+ } else if constexpr (std::is_same_v<int64_t, T>) {
+ return AParcel_readInt64(parcel, value);
+ } else if constexpr (std::is_same_v<float, T>) {
+ return AParcel_readFloat(parcel, value);
+ } else if constexpr (std::is_same_v<double, T>) {
+ return AParcel_readDouble(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_readRequiredParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_readRequiredStrongBinder(parcel, value);
+ } else if constexpr (std::is_enum_v<T>) {
+ return AParcel_readData(parcel, reinterpret_cast<std::underlying_type_t<T>*>(value));
+ } else if constexpr (is_interface_v<T>) {
+ return AParcel_readParcelable(parcel, value);
+ } else if constexpr (is_parcelable_v<T>) {
+ return AParcel_readParcelable(parcel, value);
+ } else {
+ static_assert(dependent_false_v<T>, "unrecognized type");
+ return STATUS_OK;
+ }
+}
+
+/**
+ * Convenience API for reading a nullable value of any type.
+ */
+template <typename T>
+static inline binder_status_t AParcel_readNullableData(const AParcel* parcel, T* value) {
+ if constexpr (is_specialization_v<T, std::optional> &&
+ is_specialization_v<first_template_type_t<T>, std::vector>) {
+ return AParcel_readVector(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ is_fixed_array_v<first_template_type_t<T>>) {
+ return AParcel_readNullableFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_fixed_array_v<T>) { // happens with a nullable multi-dimensional array.
+ return AParcel_readFixedArrayWithNullableData(parcel, value);
+ } else if constexpr (is_specialization_v<T, std::optional> &&
+ std::is_same_v<first_template_type_t<T>, std::string>) {
+ return AParcel_readString(parcel, value);
+ } else if constexpr (is_nullable_parcelable_v<T> || is_interface_v<T>) {
+ return AParcel_readNullableParcelable(parcel, value);
+ } else if constexpr (std::is_same_v<ScopedFileDescriptor, T>) {
+ return AParcel_readNullableParcelFileDescriptor(parcel, value);
+ } else if constexpr (std::is_same_v<SpAIBinder, T>) {
+ return AParcel_readNullableStrongBinder(parcel, value);
+ } else {
+ return AParcel_readData(parcel, value);
+ }
+}
+
} // namespace ndk
/** @} */
diff --git a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
index aa3b978..972eca7 100644
--- a/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
+++ b/libs/binder/ndk/include_cpp/android/binder_parcelable_utils.h
@@ -46,6 +46,18 @@
AParcelableHolder() = delete;
explicit AParcelableHolder(parcelable_stability_t stability)
: mParcel(AParcel_create()), mStability(stability) {}
+
+#if __ANDROID_API__ >= 31
+ AParcelableHolder(const AParcelableHolder& other)
+ : mParcel(AParcel_create()), mStability(other.mStability) {
+ // AParcelableHolder has been introduced in 31.
+ if (__builtin_available(android 31, *)) {
+ AParcel_appendFrom(other.mParcel.get(), this->mParcel.get(), 0,
+ AParcel_getDataSize(other.mParcel.get()));
+ }
+ }
+#endif
+
AParcelableHolder(AParcelableHolder&& other) = default;
virtual ~AParcelableHolder() = default;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 782328d..4163897 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -393,6 +393,14 @@
pid_t AIBinder_getCallingPid() __INTRODUCED_IN(29);
/**
+ * Determine whether the current thread is currently executing an incoming transaction.
+ *
+ * \return true if the current thread is currently executing an incoming transaction, and false
+ * otherwise.
+ */
+bool AIBinder_isHandlingTransaction() __INTRODUCED_IN(33);
+
+/**
* This can only be called if a strong reference to this object already exists in process.
*
* Available since API level 29.
diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
index b0217c4..89f21dd 100644
--- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
+++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h
@@ -68,4 +68,16 @@
*/
void AIBinder_setMinSchedulerPolicy(AIBinder* binder, int policy, int priority) __INTRODUCED_IN(33);
+/**
+ * Allow the binder to inherit realtime scheduling policies from its caller.
+ *
+ * This must be called before the object is sent to another process. Not thread
+ * safe.
+ *
+ * \param binder local server binder to set the policy for
+ * \param inheritRt whether to inherit realtime scheduling policies (default is
+ * false).
+ */
+void AIBinder_setInheritRt(AIBinder* binder, bool inheritRt) __INTRODUCED_IN(33);
+
__END_DECLS
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 64170af..3824a1b 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -119,11 +119,11 @@
AIBinder_setRequestingSid; # apex
AParcel_markSensitive; # systemapi llndk
AServiceManager_forEachDeclaredInstance; # apex llndk
- AServiceManager_forceLazyServicesPersist; # llndk
+ AServiceManager_forceLazyServicesPersist; # apex llndk
AServiceManager_isDeclared; # apex llndk
AServiceManager_isUpdatableViaApex; # apex
AServiceManager_reRegister; # llndk
- AServiceManager_registerLazyService; # llndk
+ AServiceManager_registerLazyService; # apex llndk
AServiceManager_setActiveServicesCallback; # llndk
AServiceManager_tryUnregister; # llndk
AServiceManager_waitForService; # apex llndk
@@ -145,6 +145,8 @@
global:
AIBinder_Class_disableInterfaceTokenHeader;
AIBinder_DeathRecipient_setOnUnlinked;
+ AIBinder_isHandlingTransaction;
+ AIBinder_setInheritRt; # llndk
AIBinder_setMinSchedulerPolicy; # llndk
AParcel_marshal;
AParcel_unmarshal;
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index 499f88e..58fa13a 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -505,6 +505,31 @@
}
};
+TEST(NdkBinder, SetInheritRt) {
+ // functional test in binderLibTest
+ sp<IFoo> foo = sp<MyTestFoo>::make();
+ AIBinder* binder = foo->getBinder();
+
+ // does not abort
+ AIBinder_setInheritRt(binder, true);
+ AIBinder_setInheritRt(binder, false);
+ AIBinder_setInheritRt(binder, true);
+
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, SetInheritRtNonLocal) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(binder, nullptr);
+
+ ASSERT_TRUE(AIBinder_isRemote(binder));
+
+ EXPECT_DEATH(AIBinder_setInheritRt(binder, true), "");
+ EXPECT_DEATH(AIBinder_setInheritRt(binder, false), "");
+
+ AIBinder_decStrong(binder);
+}
+
TEST(NdkBinder, AddNullService) {
EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name"));
}
diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp
index ecb044e..e2fc18d 100644
--- a/libs/binder/rust/Android.bp
+++ b/libs/binder/rust/Android.bp
@@ -20,10 +20,34 @@
"libdowncast_rs",
],
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
- }
+ },
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ "com.android.uwb",
+ "com.android.virt",
+ ],
+ min_sdk_version: "current",
+}
+
+rust_library {
+ name: "libbinder_tokio_rs",
+ crate_name: "binder_tokio",
+ srcs: ["binder_tokio/lib.rs"],
+ rustlibs: [
+ "libbinder_rs",
+ "libtokio",
+ ],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
},
apex_available: [
"//apex_available:platform",
@@ -43,16 +67,19 @@
"libbinder_ndk",
],
host_supported: true,
+ vendor_available: true,
target: {
darwin: {
enabled: false,
- }
+ },
},
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.uwb",
"com.android.virt",
],
+ min_sdk_version: "current",
lints: "none",
clippy_lints: "none",
}
@@ -65,25 +92,37 @@
bindgen_flags: [
// Unfortunately the only way to specify the rust_non_exhaustive enum
// style for a type is to make it the default
- "--default-enum-style", "rust_non_exhaustive",
+ "--default-enum-style",
+ "rust_non_exhaustive",
// and then specify constified enums for the enums we don't want
// rustified
- "--constified-enum", "android::c_interface::consts::.*",
+ "--constified-enum",
+ "android::c_interface::consts::.*",
- "--allowlist-type", "android::c_interface::.*",
- "--allowlist-type", "AStatus",
- "--allowlist-type", "AIBinder_Class",
- "--allowlist-type", "AIBinder",
- "--allowlist-type", "AIBinder_Weak",
- "--allowlist-type", "AIBinder_DeathRecipient",
- "--allowlist-type", "AParcel",
- "--allowlist-type", "binder_status_t",
- "--allowlist-function", ".*",
+ "--allowlist-type",
+ "android::c_interface::.*",
+ "--allowlist-type",
+ "AStatus",
+ "--allowlist-type",
+ "AIBinder_Class",
+ "--allowlist-type",
+ "AIBinder",
+ "--allowlist-type",
+ "AIBinder_Weak",
+ "--allowlist-type",
+ "AIBinder_DeathRecipient",
+ "--allowlist-type",
+ "AParcel",
+ "--allowlist-type",
+ "binder_status_t",
+ "--allowlist-function",
+ ".*",
],
shared_libs: [
"libbinder_ndk",
],
host_supported: true,
+ vendor_available: true,
// Currently necessary for host builds
// TODO(b/31559095): bionic on host should define this
@@ -103,8 +142,10 @@
apex_available: [
"//apex_available:platform",
"com.android.compos",
+ "com.android.uwb",
"com.android.virt",
],
+ min_sdk_version: "current",
}
// TODO(b/184872979): remove once the Rust API is created.
@@ -118,8 +159,10 @@
],
apex_available: [
"com.android.compos",
+ "com.android.uwb",
"com.android.virt",
],
+ min_sdk_version: "current",
}
rust_test {
diff --git a/libs/binder/rust/binder_tokio/lib.rs b/libs/binder/rust/binder_tokio/lib.rs
new file mode 100644
index 0000000..91047be
--- /dev/null
+++ b/libs/binder/rust/binder_tokio/lib.rs
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+//! This crate lets you use the Tokio `spawn_blocking` pool with AIDL in async
+//! Rust code.
+//!
+//! This crate works by defining a type [`Tokio`], which you can use as the
+//! generic parameter in the async version of the trait generated by the AIDL
+//! compiler.
+//! ```text
+//! use binder_tokio::Tokio;
+//!
+//! binder::get_interface::<dyn SomeAsyncInterface<Tokio>>("...").
+//! ```
+//!
+//! [`Tokio`]: crate::Tokio
+
+use binder::public_api::{BinderAsyncPool, BoxFuture, Strong};
+use binder::{FromIBinder, StatusCode};
+use std::future::Future;
+
+/// 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)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// 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)
+ }).await;
+
+ // The `is_panic` branch is not actually reachable in Android as we compile
+ // with `panic = abort`.
+ match res {
+ Ok(Ok(service)) => Ok(service),
+ Ok(Err(err)) => Err(err),
+ Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
+ Err(e) if e.is_cancelled() => Err(StatusCode::FAILED_TRANSACTION),
+ Err(_) => Err(StatusCode::UNKNOWN_ERROR),
+ }
+}
+
+/// Use the Tokio `spawn_blocking` pool with AIDL.
+pub enum Tokio {}
+
+impl BinderAsyncPool for Tokio {
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>,
+ {
+ 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 cc5dd06..4d6b294 100644
--- a/libs/binder/rust/src/binder.rs
+++ b/libs/binder/rust/src/binder.rs
@@ -17,7 +17,7 @@
//! Trait definitions for binder objects
use crate::error::{status_t, Result, StatusCode};
-use crate::parcel::{OwnedParcel, Parcel};
+use crate::parcel::{Parcel, BorrowedParcel};
use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder};
use crate::sys;
@@ -66,6 +66,35 @@
}
}
+/// Implemented by sync interfaces to specify what the associated async interface is.
+/// Generic to handle the fact that async interfaces are generic over a thread pool.
+///
+/// The binder in any object implementing this trait should be compatible with the
+/// `Target` associated type, and using `FromIBinder` to convert it to the target
+/// should not fail.
+pub trait ToAsyncInterface<P>
+where
+ Self: Interface,
+ Self::Target: FromIBinder,
+{
+ /// The async interface associated with this sync interface.
+ type Target: ?Sized;
+}
+
+/// Implemented by async interfaces to specify what the associated sync interface is.
+///
+/// The binder in any object implementing this trait should be compatible with the
+/// `Target` associated type, and using `FromIBinder` to convert it to the target
+/// should not fail.
+pub trait ToSyncInterface
+where
+ Self: Interface,
+ Self::Target: FromIBinder,
+{
+ /// The sync interface associated with this async interface.
+ type Target: ?Sized;
+}
+
/// Interface stability promise
///
/// An interface can promise to be a stable vendor interface ([`Vintf`]), or
@@ -129,7 +158,7 @@
/// Handle and reply to a request to invoke a transaction on this object.
///
/// `reply` may be [`None`] if the sender does not expect a reply.
- fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>;
+ fn on_transact(&self, code: TransactionCode, data: &BorrowedParcel<'_>, reply: &mut BorrowedParcel<'_>) -> Result<()>;
/// Handle a request to invoke the dump transaction on this
/// object.
@@ -167,6 +196,7 @@
fn ping_binder(&mut self) -> Result<()>;
/// Indicate that the service intends to receive caller security contexts.
+ #[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool);
/// Dump this object to the given file handle
@@ -177,25 +207,25 @@
fn get_extension(&mut self) -> Result<Option<SpIBinder>>;
/// Create a Parcel that can be used with `submit_transact`.
- fn prepare_transact(&self) -> Result<OwnedParcel>;
+ fn prepare_transact(&self) -> Result<Parcel>;
/// Perform a generic operation with the object.
///
- /// The provided [`OwnedParcel`] must have been created by a call to
+ /// The provided [`Parcel`] must have been created by a call to
/// `prepare_transact` on the same binder.
///
/// # Arguments
///
/// * `code` - Transaction code for the operation.
- /// * `data` - [`OwnedParcel`] with input data.
+ /// * `data` - [`Parcel`] with input data.
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY)).
fn submit_transact(
&self,
code: TransactionCode,
- data: OwnedParcel,
+ data: Parcel,
flags: TransactionFlags,
- ) -> Result<OwnedParcel>;
+ ) -> Result<Parcel>;
/// Perform a generic operation with the object. This is a convenience
/// method that internally calls `prepare_transact` followed by
@@ -206,15 +236,15 @@
/// * `flags` - Transaction flags, e.g. marking the transaction as
/// asynchronous ([`FLAG_ONEWAY`](FLAG_ONEWAY))
/// * `input_callback` A callback for building the `Parcel`.
- fn transact<F: FnOnce(&mut Parcel) -> Result<()>>(
+ fn transact<F: FnOnce(BorrowedParcel<'_>) -> Result<()>>(
&self,
code: TransactionCode,
flags: TransactionFlags,
input_callback: F,
) -> Result<Parcel> {
let mut parcel = self.prepare_transact()?;
- input_callback(&mut parcel.borrowed())?;
- self.submit_transact(code, parcel, flags).map(OwnedParcel::into_parcel)
+ input_callback(parcel.borrowed())?;
+ self.submit_transact(code, parcel, flags)
}
}
@@ -336,6 +366,26 @@
pub fn downgrade(this: &Strong<I>) -> Weak<I> {
Weak::new(this)
}
+
+ /// Convert this synchronous binder handle into an asynchronous one.
+ pub fn into_async<P>(self) -> Strong<<I as ToAsyncInterface<P>>::Target>
+ where
+ I: ToAsyncInterface<P>,
+ {
+ // By implementing the ToAsyncInterface trait, it is guaranteed that the binder
+ // object is also valid for the target type.
+ FromIBinder::try_from(self.0.as_binder()).unwrap()
+ }
+
+ /// Convert this asynchronous binder handle into a synchronous one.
+ pub fn into_sync(self) -> Strong<<I as ToSyncInterface>::Target>
+ where
+ I: ToSyncInterface,
+ {
+ // By implementing the ToSyncInterface trait, it is guaranteed that the binder
+ // object is also valid for the target type.
+ FromIBinder::try_from(self.0.as_binder()).unwrap()
+ }
}
impl<I: FromIBinder + ?Sized> Clone for Strong<I> {
@@ -475,8 +525,8 @@
/// fn on_transact(
/// &self,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> Result<()> {
/// // ...
/// }
@@ -635,6 +685,7 @@
pub struct BinderFeatures {
/// Indicates that the service intends to receive caller security contexts. This must be true
/// for `ThreadState::with_calling_sid` to work.
+ #[cfg(not(android_vndk))]
pub set_requesting_sid: bool,
// Ensure that clients include a ..BinderFeatures::default() to preserve backwards compatibility
// when new fields are added. #[non_exhaustive] doesn't work because it prevents struct
@@ -655,13 +706,13 @@
/// have the following type:
///
/// ```
-/// # use binder::{Interface, TransactionCode, Parcel};
+/// # use binder::{Interface, TransactionCode, BorrowedParcel};
/// # trait Placeholder {
/// fn on_transact(
/// service: &dyn Interface,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> binder::Result<()>;
/// # }
/// ```
@@ -676,7 +727,7 @@
/// using the provided function, `on_transact`.
///
/// ```
-/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel};
+/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, BorrowedParcel};
///
/// pub trait IServiceManager: Interface {
/// // remote methods...
@@ -692,8 +743,8 @@
/// fn on_transact(
/// service: &dyn IServiceManager,
/// code: TransactionCode,
-/// data: &Parcel,
-/// reply: &mut Parcel,
+/// data: &BorrowedParcel,
+/// reply: &mut BorrowedParcel,
/// ) -> binder::Result<()> {
/// // ...
/// Ok(())
@@ -713,12 +764,14 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -728,6 +781,7 @@
$interface:path[$descriptor:expr] {
native: $native:ident($on_transact:path),
proxy: $proxy:ident,
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -735,6 +789,7 @@
$interface[$descriptor] {
native: $native($on_transact),
proxy: $proxy {},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -746,6 +801,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
}
} => {
$crate::declare_binder_interface! {
@@ -754,6 +810,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $crate::Stability::default(),
}
}
@@ -765,6 +822,7 @@
proxy: $proxy:ident {
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $(async: $async_interface:ident,)?
stability: $stability:expr,
}
} => {
@@ -776,6 +834,7 @@
proxy: $proxy {
$($fname: $fty = $finit),*
},
+ $(async: $async_interface,)?
stability: $stability,
}
}
@@ -791,6 +850,8 @@
$($fname:ident: $fty:ty = $finit:expr),*
},
+ $( async: $async_interface:ident, )?
+
stability: $stability:expr,
}
} => {
@@ -827,6 +888,7 @@
/// Create a new binder service.
pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T, features: $crate::BinderFeatures) -> $crate::Strong<dyn $interface> {
let mut binder = $crate::Binder::new_with_stability($native(Box::new(inner)), $stability);
+ #[cfg(not(android_vndk))]
$crate::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid);
$crate::Strong::new(Box::new(binder))
}
@@ -837,7 +899,7 @@
$descriptor
}
- fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> {
+ fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::BorrowedParcel<'_>, reply: &mut $crate::BorrowedParcel<'_>) -> $crate::Result<()> {
match $on_transact(&*self.0, code, data, reply) {
// The C++ backend converts UNEXPECTED_NULL into an exception
Err($crate::StatusCode::UNEXPECTED_NULL) => {
@@ -912,19 +974,19 @@
where
dyn $interface: $crate::Interface
{
- fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
let binder = $crate::Interface::as_binder(self);
parcel.write(&binder)
}
}
impl $crate::parcel::SerializeOption for dyn $interface + '_ {
- fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
parcel.write(&this.map($crate::Interface::as_binder))
}
}
- impl std::fmt::Debug for dyn $interface {
+ impl std::fmt::Debug for dyn $interface + '_ {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.pad(stringify!($interface))
}
@@ -938,6 +1000,81 @@
.expect(concat!("Error cloning interface ", stringify!($interface)))
}
}
+
+ $(
+ // Async interface trait implementations.
+ impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> {
+ fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<$crate::Strong<dyn $async_interface<P>>> {
+ use $crate::AssociateClass;
+
+ let existing_class = ibinder.get_class();
+ if let Some(class) = existing_class {
+ if class != <$native as $crate::Remotable>::get_class() &&
+ class.get_descriptor() == <$native as $crate::Remotable>::get_descriptor()
+ {
+ // The binder object's descriptor string matches what we
+ // expect. We still need to treat this local or already
+ // associated object as remote, because we can't cast it
+ // into a Rust service object without a matching class
+ // pointer.
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ if ibinder.associate_class(<$native as $crate::Remotable>::get_class()) {
+ let service: $crate::Result<$crate::Binder<$native>> =
+ std::convert::TryFrom::try_from(ibinder.clone());
+ if let Ok(service) = service {
+ // We were able to associate with our expected class and
+ // the service is local.
+ todo!()
+ //return Ok($crate::Strong::new(Box::new(service)));
+ } else {
+ // Service is remote
+ return Ok($crate::Strong::new(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)));
+ }
+ }
+
+ Err($crate::StatusCode::BAD_TYPE.into())
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::Serialize for dyn $async_interface<P> + '_ {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ let binder = $crate::Interface::as_binder(self);
+ parcel.write(&binder)
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::parcel::SerializeOption for dyn $async_interface<P> + '_ {
+ fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
+ parcel.write(&this.map($crate::Interface::as_binder))
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.pad(stringify!($async_interface))
+ }
+ }
+
+ /// Convert a &dyn $async_interface to Strong<dyn $async_interface>
+ impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> {
+ type Owned = $crate::Strong<dyn $async_interface<P>>;
+ fn to_owned(&self) -> Self::Owned {
+ self.as_binder().into_interface()
+ .expect(concat!("Error cloning interface ", stringify!($async_interface)))
+ }
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::ToAsyncInterface<P> for dyn $interface {
+ type Target = dyn $async_interface<P>;
+ }
+
+ impl<P: $crate::BinderAsyncPool> $crate::ToSyncInterface for dyn $async_interface<P> {
+ type Target = dyn $interface;
+ }
+ )?
};
}
@@ -947,42 +1084,46 @@
#[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),*]
}
}
impl $crate::parcel::Serialize for $enum {
- fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize(&self, parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
parcel.write(&self.0)
}
}
impl $crate::parcel::SerializeArray for $enum {
- fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut $crate::parcel::BorrowedParcel<'_>) -> $crate::Result<()> {
let v: Vec<$backing> = slice.iter().map(|x| x.0).collect();
<$backing as binder::parcel::SerializeArray>::serialize_array(&v[..], parcel)
}
}
impl $crate::parcel::Deserialize for $enum {
- fn deserialize(parcel: &$crate::parcel::Parcel) -> $crate::Result<Self> {
+ fn deserialize(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Self> {
parcel.read().map(Self)
}
}
impl $crate::parcel::DeserializeArray for $enum {
- fn deserialize_array(parcel: &$crate::parcel::Parcel) -> $crate::Result<Option<Vec<Self>>> {
+ fn deserialize_array(parcel: &$crate::parcel::BorrowedParcel<'_>) -> $crate::Result<Option<Vec<Self>>> {
let v: Option<Vec<$backing>> =
<$backing as binder::parcel::DeserializeArray>::deserialize_array(parcel)?;
Ok(v.map(|v| v.into_iter().map(Self).collect()))
diff --git a/libs/binder/rust/src/binder_async.rs b/libs/binder/rust/src/binder_async.rs
new file mode 100644
index 0000000..214c0b5
--- /dev/null
+++ b/libs/binder/rust/src/binder_async.rs
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+use std::future::Future;
+use std::pin::Pin;
+
+/// A type alias for a pinned, boxed future that lets you write shorter code without littering it
+/// with Pin and Send bounds.
+pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
+
+/// A thread pool for running binder transactions.
+pub trait BinderAsyncPool {
+ /// This function should conceptually behave like this:
+ ///
+ /// ```text
+ /// let result = spawn_thread(|| spawn_me()).await;
+ /// return after_spawn(result).await;
+ /// ```
+ ///
+ /// If the spawning fails for some reason, the method may also skip the `after_spawn` closure
+ /// and immediately return an error.
+ ///
+ /// The only difference between different implementations should be which
+ /// `spawn_thread` method is used. For Tokio, it would be `tokio::task::spawn_blocking`.
+ ///
+ /// This method has the design it has because the only way to define a trait that
+ /// allows the return type of the spawn to be chosen by the caller is to return a
+ /// boxed `Future` trait object, and including `after_spawn` in the trait function
+ /// allows the caller to avoid double-boxing if they want to do anything to the value
+ /// returned from the spawned thread.
+ fn spawn<'a, F1, F2, Fut, A, B, E>(spawn_me: F1, after_spawn: F2) -> BoxFuture<'a, Result<B, E>>
+ where
+ F1: FnOnce() -> A,
+ F2: FnOnce(A) -> Fut,
+ Fut: Future<Output = Result<B, E>>,
+ F1: Send + 'static,
+ F2: Send + 'a,
+ Fut: Send + 'a,
+ A: Send + 'static,
+ B: Send + 'a,
+ E: From<crate::StatusCode>;
+}
diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs
index 81b620e..7c04a72 100644
--- a/libs/binder/rust/src/lib.rs
+++ b/libs/binder/rust/src/lib.rs
@@ -50,8 +50,8 @@
//! fn on_transact(
//! service: &dyn ITest,
//! code: TransactionCode,
-//! _data: &Parcel,
-//! reply: &mut Parcel,
+//! _data: &BorrowedParcel,
+//! reply: &mut BorrowedParcel,
//! ) -> binder::Result<()> {
//! match code {
//! SpIBinder::FIRST_CALL_TRANSACTION => {
@@ -98,6 +98,7 @@
#[macro_use]
mod binder;
+mod binder_async;
mod error;
mod native;
mod state;
@@ -108,12 +109,13 @@
pub use crate::binder::{
BinderFeatures, FromIBinder, IBinder, IBinderInternal, Interface, InterfaceClass, Remotable,
- Stability, Strong, TransactionCode, TransactionFlags, Weak, FIRST_CALL_TRANSACTION,
- FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
+ Stability, Strong, ToAsyncInterface, ToSyncInterface, TransactionCode, TransactionFlags, Weak,
+ FIRST_CALL_TRANSACTION, FLAG_CLEAR_BUF, FLAG_ONEWAY, FLAG_PRIVATE_LOCAL, LAST_CALL_TRANSACTION,
};
+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 parcel::{OwnedParcel, Parcel};
+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};
pub use state::{ProcessState, ThreadState};
@@ -133,8 +135,9 @@
wait_for_interface,
};
pub use super::{
- BinderFeatures, DeathRecipient, ExceptionCode, IBinder, Interface, ProcessState, SpIBinder,
- Status, StatusCode, Strong, ThreadState, Weak, WpIBinder,
+ BinderAsyncPool, BinderFeatures, BoxFuture, DeathRecipient, ExceptionCode, IBinder,
+ Interface, ProcessState, SpIBinder, Status, StatusCode, Strong, ThreadState, Weak,
+ WpIBinder,
};
/// Binder result containing a [`Status`] on error.
diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs
index a91092e..b7c7ae4 100644
--- a/libs/binder/rust/src/native.rs
+++ b/libs/binder/rust/src/native.rs
@@ -18,7 +18,7 @@
AsNative, Interface, InterfaceClassMethods, Remotable, Stability, TransactionCode,
};
use crate::error::{status_result, status_t, Result, StatusCode};
-use crate::parcel::{Parcel, Serialize};
+use crate::parcel::{BorrowedParcel, Serialize};
use crate::proxy::SpIBinder;
use crate::sys;
@@ -161,8 +161,8 @@
/// # fn on_transact(
/// # service: &dyn IBar,
/// # code: TransactionCode,
- /// # data: &Parcel,
- /// # reply: &mut Parcel,
+ /// # data: &BorrowedParcel,
+ /// # reply: &mut BorrowedParcel,
/// # ) -> binder::Result<()> {
/// # Ok(())
/// # }
@@ -212,7 +212,7 @@
/// Mark this binder object with local stability, which is vendor if we are
/// building for the VNDK and system otherwise.
- #[cfg(vendor_ndk)]
+ #[cfg(any(vendor_ndk, android_vndk))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -223,7 +223,7 @@
/// Mark this binder object with local stability, which is vendor if we are
/// building for the VNDK and system otherwise.
- #[cfg(not(vendor_ndk))]
+ #[cfg(not(any(vendor_ndk, android_vndk)))]
fn mark_local_stability(&mut self) {
unsafe {
// Safety: Self always contains a valid `AIBinder` pointer, so
@@ -277,8 +277,8 @@
reply: *mut sys::AParcel,
) -> status_t {
let res = {
- let mut reply = Parcel::borrowed(reply).unwrap();
- let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap();
+ let mut reply = BorrowedParcel::from_raw(reply).unwrap();
+ let data = BorrowedParcel::from_raw(data as *mut sys::AParcel).unwrap();
let object = sys::AIBinder_getUserData(binder);
let binder: &T = &*(object as *const T);
binder.on_transact(code, &data, &mut reply)
@@ -384,7 +384,7 @@
}
impl<B: Remotable> Serialize for Binder<B> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(Some(&self.as_binder()))
}
}
@@ -503,8 +503,8 @@
fn on_transact(
&self,
_code: TransactionCode,
- _data: &Parcel,
- _reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
Ok(())
}
@@ -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/src/parcel.rs b/libs/binder/rust/src/parcel.rs
index 9dba950..206b90c 100644
--- a/libs/binder/rust/src/parcel.rs
+++ b/libs/binder/rust/src/parcel.rs
@@ -21,11 +21,10 @@
use crate::proxy::SpIBinder;
use crate::sys;
-use std::cell::RefCell;
use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
-use std::ptr;
+use std::ptr::{self, NonNull};
use std::fmt;
mod file_descriptor;
@@ -46,41 +45,42 @@
/// other side of the IPC, and references to live Binder objects that will
/// result in the other side receiving a proxy Binder connected with the
/// original Binder in the Parcel.
-pub enum Parcel {
- /// Owned parcel pointer
- Owned(*mut sys::AParcel),
- /// Borrowed parcel pointer (will not be destroyed on drop)
- Borrowed(*mut sys::AParcel),
-}
-
-/// A variant of Parcel that is known to be owned.
-pub struct OwnedParcel {
- ptr: *mut sys::AParcel,
+///
+/// This type represents a parcel that is owned by Rust code.
+#[repr(transparent)]
+pub struct Parcel {
+ ptr: NonNull<sys::AParcel>,
}
/// # Safety
///
/// This type guarantees that it owns the AParcel and that all access to
-/// the AParcel happens through the OwnedParcel, so it is ok to send across
+/// the AParcel happens through the Parcel, so it is ok to send across
/// threads.
-unsafe impl Send for OwnedParcel {}
+unsafe impl Send for Parcel {}
-/// A variant of Parcel that is known to be borrowed.
+/// Container for a message (data and object references) that can be sent
+/// through Binder.
+///
+/// This object is a borrowed variant of [`Parcel`]. It is a separate type from
+/// `&mut Parcel` because it is not valid to `mem::swap` two parcels.
+#[repr(transparent)]
pub struct BorrowedParcel<'a> {
- inner: Parcel,
+ ptr: NonNull<sys::AParcel>,
_lifetime: PhantomData<&'a mut Parcel>,
}
-impl OwnedParcel {
- /// Create a new empty `OwnedParcel`.
- pub fn new() -> OwnedParcel {
+impl Parcel {
+ /// Create a new empty `Parcel`.
+ pub fn new() -> Parcel {
let ptr = unsafe {
// Safety: If `AParcel_create` succeeds, it always returns
// a valid pointer. If it fails, the process will crash.
sys::AParcel_create()
};
- assert!(!ptr.is_null());
- Self { ptr }
+ Self {
+ ptr: NonNull::new(ptr).expect("AParcel_create returned null pointer")
+ }
}
/// Create an owned reference to a parcel object from a raw pointer.
@@ -94,108 +94,43 @@
///
/// Additionally, the caller must guarantee that it is valid to take
/// ownership of the AParcel object. All future access to the AParcel
- /// must happen through this `OwnedParcel`.
+ /// must happen through this `Parcel`.
///
- /// Because `OwnedParcel` implements `Send`, the pointer must never point
- /// to any thread-local data, e.g., a variable on the stack, either directly
- /// or indirectly.
- pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<OwnedParcel> {
- ptr.as_mut().map(|ptr| Self { ptr })
+ /// Because `Parcel` implements `Send`, the pointer must never point to any
+ /// thread-local data, e.g., a variable on the stack, either directly or
+ /// indirectly.
+ pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<Parcel> {
+ NonNull::new(ptr).map(|ptr| Self { ptr })
}
/// Consume the parcel, transferring ownership to the caller.
pub(crate) fn into_raw(self) -> *mut sys::AParcel {
- let ptr = self.ptr;
+ let ptr = self.ptr.as_ptr();
let _ = ManuallyDrop::new(self);
ptr
}
- /// Convert this `OwnedParcel` into an owned `Parcel`.
- pub fn into_parcel(self) -> Parcel {
- Parcel::Owned(self.into_raw())
- }
-
/// Get a borrowed view into the contents of this `Parcel`.
pub fn borrowed(&mut self) -> BorrowedParcel<'_> {
+ // Safety: The raw pointer is a valid pointer to an AParcel, and the
+ // lifetime of the returned `BorrowedParcel` is tied to `self`, so the
+ // borrow checker will ensure that the `AParcel` can only be accessed
+ // via the `BorrowParcel` until it goes out of scope.
BorrowedParcel {
- inner: Parcel::Borrowed(self.ptr),
+ ptr: self.ptr,
_lifetime: PhantomData,
}
}
-}
-impl Default for OwnedParcel {
- fn default() -> Self {
- Self::new()
- }
-}
-
-impl Clone for OwnedParcel {
- fn clone(&self) -> Self {
- let mut new_parcel = Self::new();
- new_parcel
- .borrowed()
- .append_all_from(&Parcel::Borrowed(self.ptr))
- .expect("Failed to append from Parcel");
- new_parcel
- }
-}
-
-impl<'a> std::ops::Deref for BorrowedParcel<'a> {
- type Target = Parcel;
- fn deref(&self) -> &Parcel {
- &self.inner
- }
-}
-impl<'a> std::ops::DerefMut for BorrowedParcel<'a> {
- fn deref_mut(&mut self) -> &mut Parcel {
- &mut self.inner
- }
-}
-
-/// # Safety
-///
-/// The `Parcel` constructors guarantee that a `Parcel` object will always
-/// contain a valid pointer to an `AParcel`.
-unsafe impl AsNative<sys::AParcel> for Parcel {
- fn as_native(&self) -> *const sys::AParcel {
- match *self {
- Self::Owned(x) | Self::Borrowed(x) => x,
+ /// Get an immutable borrowed view into the contents of this `Parcel`.
+ pub fn borrowed_ref(&self) -> &BorrowedParcel<'_> {
+ // Safety: Parcel and BorrowedParcel are both represented in the same
+ // way as a NonNull<sys::AParcel> due to their use of repr(transparent),
+ // so casting references as done here is valid.
+ unsafe {
+ &*(self as *const Parcel as *const BorrowedParcel<'_>)
}
}
-
- fn as_native_mut(&mut self) -> *mut sys::AParcel {
- match *self {
- Self::Owned(x) | Self::Borrowed(x) => x,
- }
- }
-}
-
-impl Parcel {
- /// Create a new empty `Parcel`.
- ///
- /// Creates a new owned empty parcel that can be written to
- /// using the serialization methods and appended to and
- /// from using `append_from` and `append_from_all`.
- pub fn new() -> Parcel {
- let parcel = unsafe {
- // Safety: If `AParcel_create` succeeds, it always returns
- // a valid pointer. If it fails, the process will crash.
- sys::AParcel_create()
- };
- assert!(!parcel.is_null());
- Self::Owned(parcel)
- }
-
- /// Create a borrowed reference to a parcel object from a raw pointer.
- ///
- /// # Safety
- ///
- /// This constructor is safe if the raw pointer parameter is either null
- /// (resulting in `None`), or a valid pointer to an `AParcel` object.
- pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> {
- ptr.as_mut().map(|ptr| Self::Borrowed(ptr))
- }
}
impl Default for Parcel {
@@ -208,14 +143,77 @@
fn clone(&self) -> Self {
let mut new_parcel = Self::new();
new_parcel
- .append_all_from(self)
+ .borrowed()
+ .append_all_from(self.borrowed_ref())
.expect("Failed to append from Parcel");
new_parcel
}
}
+impl<'a> BorrowedParcel<'a> {
+ /// Create a borrowed reference to a parcel object from a raw pointer.
+ ///
+ /// # Safety
+ ///
+ /// This constructor is safe if the raw pointer parameter is either null
+ /// (resulting in `None`), or a valid pointer to an `AParcel` object.
+ ///
+ /// Since the raw pointer is not restricted by any lifetime, the lifetime on
+ /// the returned `BorrowedParcel` object can be chosen arbitrarily by the
+ /// caller. The caller must ensure it is valid to mutably borrow the AParcel
+ /// for the duration of the lifetime that the caller chooses. Note that
+ /// since this is a mutable borrow, it must have exclusive access to the
+ /// AParcel for the duration of the borrow.
+ pub unsafe fn from_raw(ptr: *mut sys::AParcel) -> Option<BorrowedParcel<'a>> {
+ Some(Self {
+ ptr: NonNull::new(ptr)?,
+ _lifetime: PhantomData,
+ })
+ }
+
+ /// Get a sub-reference to this reference to the parcel.
+ pub fn reborrow(&mut self) -> BorrowedParcel<'_> {
+ // Safety: The raw pointer is a valid pointer to an AParcel, and the
+ // lifetime of the returned `BorrowedParcel` is tied to `self`, so the
+ // borrow checker will ensure that the `AParcel` can only be accessed
+ // via the `BorrowParcel` until it goes out of scope.
+ BorrowedParcel {
+ ptr: self.ptr,
+ _lifetime: PhantomData,
+ }
+ }
+}
+
+/// # Safety
+///
+/// The `Parcel` constructors guarantee that a `Parcel` object will always
+/// contain a valid pointer to an `AParcel`.
+unsafe impl AsNative<sys::AParcel> for Parcel {
+ fn as_native(&self) -> *const sys::AParcel {
+ self.ptr.as_ptr()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ self.ptr.as_ptr()
+ }
+}
+
+/// # Safety
+///
+/// The `BorrowedParcel` constructors guarantee that a `BorrowedParcel` object
+/// will always contain a valid pointer to an `AParcel`.
+unsafe impl<'a> AsNative<sys::AParcel> for BorrowedParcel<'a> {
+ fn as_native(&self) -> *const sys::AParcel {
+ self.ptr.as_ptr()
+ }
+
+ fn as_native_mut(&mut self) -> *mut sys::AParcel {
+ self.ptr.as_ptr()
+ }
+}
+
// Data serialization methods
-impl Parcel {
+impl<'a> BorrowedParcel<'a> {
/// Data written to parcelable is zero'd before being deleted or reallocated.
pub fn mark_sensitive(&mut self) {
unsafe {
@@ -224,12 +222,12 @@
}
}
- /// Write a type that implements [`Serialize`] to the `Parcel`.
+ /// Write a type that implements [`Serialize`] to the parcel.
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(self)
}
- /// Writes the length of a slice to the `Parcel`.
+ /// Writes the length of a slice to the parcel.
///
/// This is used in AIDL-generated client side code to indicate the
/// allocated space for an output array parameter.
@@ -242,7 +240,7 @@
}
}
- /// Perform a series of writes to the `Parcel`, prepended with the length
+ /// Perform a series of writes to the parcel, prepended with the length
/// (in bytes) of the written data.
///
/// The length `0i32` will be written to the parcel first, followed by the
@@ -256,7 +254,7 @@
///
/// ```
/// # use binder::{Binder, Interface, Parcel};
- /// # let mut parcel = Parcel::Owned(std::ptr::null_mut());
+ /// # let mut parcel = Parcel::new();
/// parcel.sized_write(|subparcel| {
/// subparcel.write(&1u32)?;
/// subparcel.write(&2u32)?;
@@ -270,14 +268,14 @@
/// [16i32, 1u32, 2u32, 3u32]
/// ```
pub fn sized_write<F>(&mut self, f: F) -> Result<()>
- where for<'a>
- F: Fn(&'a WritableSubParcel<'a>) -> Result<()>
+ where
+ for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>
{
let start = self.get_data_position();
self.write(&0i32)?;
{
- let subparcel = WritableSubParcel(RefCell::new(self));
- f(&subparcel)?;
+ let mut subparcel = WritableSubParcel(self.reborrow());
+ f(&mut subparcel)?;
}
let end = self.get_data_position();
unsafe {
@@ -294,8 +292,8 @@
/// Returns the current position in the parcel data.
pub fn get_data_position(&self) -> i32 {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
- // and this call is otherwise safe.
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
sys::AParcel_getDataPosition(self.as_native())
}
}
@@ -303,8 +301,8 @@
/// Returns the total size of the parcel.
pub fn get_data_size(&self) -> i32 {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an `AParcel`,
- // and this call is otherwise safe.
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
sys::AParcel_getDataSize(self.as_native())
}
}
@@ -322,11 +320,11 @@
status_result(sys::AParcel_setDataPosition(self.as_native(), pos))
}
- /// Append a subset of another `Parcel`.
+ /// Append a subset of another parcel.
///
/// This appends `size` bytes of data from `other` starting at offset
- /// `start` to the current `Parcel`, or returns an error if not possible.
- pub fn append_from(&mut self, other: &Self, start: i32, size: i32) -> Result<()> {
+ /// `start` to the current parcel, or returns an error if not possible.
+ pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> {
let status = unsafe {
// Safety: `Parcel::appendFrom` from C++ checks that `start`
// and `size` are in bounds, and returns an error otherwise.
@@ -341,33 +339,125 @@
status_result(status)
}
- /// Append the contents of another `Parcel`.
- pub fn append_all_from(&mut self, other: &Self) -> Result<()> {
- self.append_from(other, 0, other.get_data_size())
+ /// Append the contents of another parcel.
+ pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> {
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
+ // `AParcel`, and this call is otherwise safe.
+ let size = unsafe { sys::AParcel_getDataSize(other.as_native()) };
+ self.append_from(other, 0, size)
}
}
-/// A segment of a writable parcel, used for [`Parcel::sized_write`].
-pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>);
+/// A segment of a writable parcel, used for [`BorrowedParcel::sized_write`].
+pub struct WritableSubParcel<'a>(BorrowedParcel<'a>);
impl<'a> WritableSubParcel<'a> {
/// Write a type that implements [`Serialize`] to the sub-parcel.
- pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> {
- parcelable.serialize(&mut *self.0.borrow_mut())
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ parcelable.serialize(&mut self.0)
+ }
+}
+
+impl Parcel {
+ /// Data written to parcelable is zero'd before being deleted or reallocated.
+ pub fn mark_sensitive(&mut self) {
+ self.borrowed().mark_sensitive()
+ }
+
+ /// Write a type that implements [`Serialize`] to the parcel.
+ pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
+ self.borrowed().write(parcelable)
+ }
+
+ /// Writes the length of a slice to the parcel.
+ ///
+ /// This is used in AIDL-generated client side code to indicate the
+ /// allocated space for an output array parameter.
+ pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
+ self.borrowed().write_slice_size(slice)
+ }
+
+ /// Perform a series of writes to the parcel, prepended with the length
+ /// (in bytes) of the written data.
+ ///
+ /// The length `0i32` will be written to the parcel first, followed by the
+ /// writes performed by the callback. The initial length will then be
+ /// updated to the length of all data written by the callback, plus the
+ /// size of the length elemement itself (4 bytes).
+ ///
+ /// # Examples
+ ///
+ /// After the following call:
+ ///
+ /// ```
+ /// # use binder::{Binder, Interface, Parcel};
+ /// # let mut parcel = Parcel::new();
+ /// parcel.sized_write(|subparcel| {
+ /// subparcel.write(&1u32)?;
+ /// subparcel.write(&2u32)?;
+ /// subparcel.write(&3u32)
+ /// });
+ /// ```
+ ///
+ /// `parcel` will contain the following:
+ ///
+ /// ```ignore
+ /// [16i32, 1u32, 2u32, 3u32]
+ /// ```
+ pub fn sized_write<F>(&mut self, f: F) -> Result<()>
+ where
+ for<'b> F: FnOnce(&'b mut WritableSubParcel<'b>) -> Result<()>
+ {
+ self.borrowed().sized_write(f)
+ }
+
+ /// Returns the current position in the parcel data.
+ pub fn get_data_position(&self) -> i32 {
+ self.borrowed_ref().get_data_position()
+ }
+
+ /// Returns the total size of the parcel.
+ pub fn get_data_size(&self) -> i32 {
+ self.borrowed_ref().get_data_size()
+ }
+
+ /// Move the current read/write position in the parcel.
+ ///
+ /// # Safety
+ ///
+ /// This method is safe if `pos` is less than the current size of the parcel
+ /// data buffer. Otherwise, we are relying on correct bounds checking in the
+ /// Parcel C++ code on every subsequent read or write to this parcel. If all
+ /// accesses are bounds checked, this call is still safe, but we can't rely
+ /// on that.
+ pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> {
+ self.borrowed_ref().set_data_position(pos)
+ }
+
+ /// Append a subset of another parcel.
+ ///
+ /// This appends `size` bytes of data from `other` starting at offset
+ /// `start` to the current parcel, or returns an error if not possible.
+ pub fn append_from(&mut self, other: &impl AsNative<sys::AParcel>, start: i32, size: i32) -> Result<()> {
+ self.borrowed().append_from(other, start, size)
+ }
+
+ /// Append the contents of another parcel.
+ pub fn append_all_from(&mut self, other: &impl AsNative<sys::AParcel>) -> Result<()> {
+ self.borrowed().append_all_from(other)
}
}
// Data deserialization methods
-impl Parcel {
- /// Attempt to read a type that implements [`Deserialize`] from this
- /// `Parcel`.
+impl<'a> BorrowedParcel<'a> {
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel.
pub fn read<D: Deserialize>(&self) -> Result<D> {
D::deserialize(self)
}
- /// Attempt to read a type that implements [`Deserialize`] from this
- /// `Parcel` onto an existing value. This operation will overwrite the old
- /// value partially or completely, depending on how much data is available.
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel
+ /// onto an existing value. This operation will overwrite the old value
+ /// partially or completely, depending on how much data is available.
pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> {
x.deserialize_from(self)
}
@@ -400,9 +490,9 @@
/// });
/// ```
///
- pub fn sized_read<F>(&self, mut f: F) -> Result<()>
+ pub fn sized_read<F>(&self, f: F) -> Result<()>
where
- for<'a> F: FnMut(ReadableSubParcel<'a>) -> Result<()>
+ for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>
{
let start = self.get_data_position();
let parcelable_size: i32 = self.read()?;
@@ -417,7 +507,10 @@
}
let subparcel = ReadableSubParcel {
- parcel: self,
+ parcel: BorrowedParcel {
+ ptr: self.ptr,
+ _lifetime: PhantomData,
+ },
end_position: end,
};
f(subparcel)?;
@@ -431,8 +524,8 @@
Ok(())
}
- /// Read a vector size from the `Parcel` and resize the given output vector
- /// to be correctly sized for that amount of data.
+ /// Read a vector size from the parcel and resize the given output vector to
+ /// be correctly sized for that amount of data.
///
/// This method is used in AIDL-generated server side code for methods that
/// take a mutable slice reference parameter.
@@ -450,7 +543,7 @@
Ok(())
}
- /// Read a vector size from the `Parcel` and either create a correctly sized
+ /// Read a vector size from the parcel and either create a correctly sized
/// vector for that amount of data or set the output parameter to None if
/// the vector should be null.
///
@@ -478,7 +571,7 @@
/// A segment of a readable parcel, used for [`Parcel::sized_read`].
pub struct ReadableSubParcel<'a> {
- parcel: &'a Parcel,
+ parcel: BorrowedParcel<'a>,
end_position: i32,
}
@@ -488,7 +581,7 @@
// The caller should have checked this,
// but it can't hurt to double-check
assert!(self.has_more_data());
- D::deserialize(self.parcel)
+ D::deserialize(&self.parcel)
}
/// Check if the sub-parcel has more data to read
@@ -497,11 +590,82 @@
}
}
-// Internal APIs
impl Parcel {
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel.
+ pub fn read<D: Deserialize>(&self) -> Result<D> {
+ self.borrowed_ref().read()
+ }
+
+ /// Attempt to read a type that implements [`Deserialize`] from this parcel
+ /// onto an existing value. This operation will overwrite the old value
+ /// partially or completely, depending on how much data is available.
+ pub fn read_onto<D: Deserialize>(&self, x: &mut D) -> Result<()> {
+ self.borrowed_ref().read_onto(x)
+ }
+
+ /// Safely read a sized parcelable.
+ ///
+ /// Read the size of a parcelable, compute the end position
+ /// of that parcelable, then build a sized readable sub-parcel
+ /// and call a closure with the sub-parcel as its parameter.
+ /// The closure can keep reading data from the sub-parcel
+ /// until it runs out of input data. The closure is responsible
+ /// for calling [`ReadableSubParcel::has_more_data`] to check for
+ /// more data before every read, at least until Rust generators
+ /// are stabilized.
+ /// After the closure returns, skip to the end of the current
+ /// parcelable regardless of how much the closure has read.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut parcelable = Default::default();
+ /// parcel.sized_read(|subparcel| {
+ /// if subparcel.has_more_data() {
+ /// parcelable.a = subparcel.read()?;
+ /// }
+ /// if subparcel.has_more_data() {
+ /// parcelable.b = subparcel.read()?;
+ /// }
+ /// Ok(())
+ /// });
+ /// ```
+ ///
+ pub fn sized_read<F>(&self, f: F) -> Result<()>
+ where
+ for<'b> F: FnOnce(ReadableSubParcel<'b>) -> Result<()>
+ {
+ self.borrowed_ref().sized_read(f)
+ }
+
+ /// Read a vector size from the parcel and resize the given output vector to
+ /// be correctly sized for that amount of data.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> {
+ self.borrowed_ref().resize_out_vec(out_vec)
+ }
+
+ /// Read a vector size from the parcel and either create a correctly sized
+ /// vector for that amount of data or set the output parameter to None if
+ /// the vector should be null.
+ ///
+ /// This method is used in AIDL-generated server side code for methods that
+ /// take a mutable slice reference parameter.
+ pub fn resize_nullable_out_vec<D: Default + Deserialize>(
+ &self,
+ out_vec: &mut Option<Vec<D>>,
+ ) -> Result<()> {
+ self.borrowed_ref().resize_nullable_out_vec(out_vec)
+ }
+}
+
+// Internal APIs
+impl<'a> BorrowedParcel<'a> {
pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> {
unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
// `AParcel`. `AsNative` for `Option<SpIBinder`> will either return
// null or a valid pointer to an `AIBinder`, both of which are
// valid, safe inputs to `AParcel_writeStrongBinder`.
@@ -521,7 +685,7 @@
pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> {
let mut binder = ptr::null_mut();
let status = unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
+ // Safety: `BorrowedParcel` always contains a valid pointer to an
// `AParcel`. We pass a valid, mutable out pointer to the `binder`
// parameter. After this call, `binder` will be either null or a
// valid pointer to an `AIBinder` owned by the caller.
@@ -541,25 +705,11 @@
impl Drop for Parcel {
fn drop(&mut self) {
// Run the C++ Parcel complete object destructor
- if let Self::Owned(ptr) = *self {
- unsafe {
- // Safety: `Parcel` always contains a valid pointer to an
- // `AParcel`. If we own the parcel, we can safely delete it
- // here.
- sys::AParcel_delete(ptr)
- }
- }
- }
-}
-
-impl Drop for OwnedParcel {
- fn drop(&mut self) {
- // Run the C++ Parcel complete object destructor
unsafe {
- // Safety: `OwnedParcel` always contains a valid pointer to an
+ // Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. Since we own the parcel, we can safely delete it
// here.
- sys::AParcel_delete(self.ptr)
+ sys::AParcel_delete(self.ptr.as_ptr())
}
}
}
@@ -571,9 +721,9 @@
}
}
-impl fmt::Debug for OwnedParcel {
+impl<'a> fmt::Debug for BorrowedParcel<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("OwnedParcel")
+ f.debug_struct("BorrowedParcel")
.finish()
}
}
@@ -595,7 +745,7 @@
assert_eq!(parcel.read::<Option<String>>(), Ok(None));
assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL));
- assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE));
+ assert_eq!(parcel.borrowed_ref().read_binder().err(), Some(StatusCode::BAD_TYPE));
parcel.write(&1i32).unwrap();
diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs
index 8bcc5d0..4d3d59a 100644
--- a/libs/binder/rust/src/parcel/file_descriptor.rs
+++ b/libs/binder/rust/src/parcel/file_descriptor.rs
@@ -15,7 +15,7 @@
*/
use super::{
- Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray,
+ Deserialize, DeserializeArray, DeserializeOption, BorrowedParcel, Serialize, SerializeArray,
SerializeOption,
};
use crate::binder::AsNative;
@@ -60,8 +60,18 @@
}
}
+impl PartialEq for ParcelFileDescriptor {
+ // Since ParcelFileDescriptors own the FD, if this function ever returns true (and it is used to
+ // compare two different objects), then it would imply that an FD is double-owned.
+ fn eq(&self, other: &Self) -> bool {
+ self.as_raw_fd() == other.as_raw_fd()
+ }
+}
+
+impl Eq for ParcelFileDescriptor {}
+
impl Serialize for ParcelFileDescriptor {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let fd = self.0.as_raw_fd();
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -78,7 +88,7 @@
impl SerializeArray for ParcelFileDescriptor {}
impl SerializeOption for ParcelFileDescriptor {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(f) = this {
f.serialize(parcel)
} else {
@@ -95,7 +105,7 @@
}
impl DeserializeOption for ParcelFileDescriptor {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let mut fd = -1i32;
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -125,7 +135,7 @@
}
impl Deserialize for ParcelFileDescriptor {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs
index ec00e1d..61f88b6 100644
--- a/libs/binder/rust/src/parcel/parcelable.rs
+++ b/libs/binder/rust/src/parcel/parcelable.rs
@@ -16,14 +16,14 @@
use crate::binder::{AsNative, FromIBinder, Stability, Strong};
use crate::error::{status_result, status_t, Result, Status, StatusCode};
-use crate::parcel::Parcel;
+use crate::parcel::BorrowedParcel;
use crate::proxy::SpIBinder;
use crate::sys;
use std::convert::{TryFrom, TryInto};
use std::ffi::c_void;
use std::os::raw::{c_char, c_ulong};
-use std::mem::{self, MaybeUninit};
+use std::mem::{self, MaybeUninit, ManuallyDrop};
use std::ptr;
use std::slice;
@@ -39,7 +39,7 @@
/// `Serialize::serialize` and its variants are generally
/// preferred over this function, since the former also
/// prepend a header.
- fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()>;
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
/// Internal deserialization function for parcelables.
///
@@ -47,26 +47,26 @@
/// `Deserialize::deserialize` and its variants are generally
/// preferred over this function, since the former also
/// parse the additional header.
- fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()>;
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()>;
}
/// A struct whose instances can be written to a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Serialize {
/// Serialize this instance into the given [`Parcel`].
- fn serialize(&self, parcel: &mut Parcel) -> Result<()>;
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()>;
}
/// A struct whose instances can be restored from a [`Parcel`].
// Might be able to hook this up as a serde backend in the future?
pub trait Deserialize: Sized {
/// Deserialize an instance from the given [`Parcel`].
- fn deserialize(parcel: &Parcel) -> Result<Self>;
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self>;
/// Deserialize an instance from the given [`Parcel`] onto the
/// current object. This operation will overwrite the old value
/// partially or completely, depending on how much data is available.
- fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> {
+ fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
*self = Self::deserialize(parcel)?;
Ok(())
}
@@ -80,8 +80,8 @@
// We want the default implementation for most types, but an override for
// a few special ones like `readByteArray` for `u8`.
pub trait SerializeArray: Serialize + Sized {
- /// Serialize an array of this type into the given [`Parcel`].
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ /// Serialize an array of this type into the given parcel.
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let res = unsafe {
// Safety: Safe FFI, slice will always be a safe pointer to pass.
sys::AParcel_writeParcelableArray(
@@ -111,7 +111,7 @@
let slice: &[T] = slice::from_raw_parts(array.cast(), index+1);
- let mut parcel = match Parcel::borrowed(parcel) {
+ let mut parcel = match BorrowedParcel::from_raw(parcel) {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -126,8 +126,8 @@
/// Defaults to calling Deserialize::deserialize() manually for every element,
/// but can be overridden for custom implementations like `readByteArray`.
pub trait DeserializeArray: Deserialize {
- /// Deserialize an array of type from the given [`Parcel`].
- fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ /// Deserialize an array of type from the given parcel.
+ fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
let res = unsafe {
// Safety: Safe FFI, vec is the correct opaque type expected by
@@ -173,7 +173,7 @@
None => return StatusCode::BAD_INDEX as status_t,
};
- let parcel = match Parcel::borrowed(parcel as *mut _) {
+ let parcel = match BorrowedParcel::from_raw(parcel as *mut _) {
None => return StatusCode::UNEXPECTED_NULL as status_t,
Some(p) => p,
};
@@ -205,8 +205,8 @@
// We also use it to provide a default implementation for AIDL-generated
// parcelables.
pub trait SerializeOption: Serialize {
- /// Serialize an Option of this type into the given [`Parcel`].
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ /// Serialize an Option of this type into the given parcel.
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(inner) = this {
parcel.write(&NON_NULL_PARCELABLE_FLAG)?;
parcel.write(inner)
@@ -218,8 +218,8 @@
/// Helper trait for types that can be nullable when deserialized.
pub trait DeserializeOption: Deserialize {
- /// Deserialize an Option of this type from the given [`Parcel`].
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ /// Deserialize an Option of this type from the given parcel.
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let null: i32 = parcel.read()?;
if null == NULL_PARCELABLE_FLAG {
Ok(None)
@@ -228,10 +228,10 @@
}
}
- /// Deserialize an Option of this type from the given [`Parcel`] onto the
+ /// Deserialize an Option of this type from the given parcel onto the
/// current object. This operation will overwrite the current value
/// partially or completely, depending on how much data is available.
- fn deserialize_option_from(this: &mut Option<Self>, parcel: &Parcel) -> Result<()> {
+ fn deserialize_option_from(this: &mut Option<Self>, parcel: &BorrowedParcel<'_>) -> Result<()> {
*this = Self::deserialize_option(parcel)?;
Ok(())
}
@@ -297,10 +297,23 @@
};
}
+/// Safety: All elements in the vector must be properly initialized.
+unsafe fn vec_assume_init<T>(vec: Vec<MaybeUninit<T>>) -> Vec<T> {
+ // We can convert from Vec<MaybeUninit<T>> to Vec<T> because MaybeUninit<T>
+ // has the same alignment and size as T, so the pointer to the vector
+ // allocation will be compatible.
+ let mut vec = ManuallyDrop::new(vec);
+ Vec::from_raw_parts(
+ vec.as_mut_ptr().cast(),
+ vec.len(),
+ vec.capacity(),
+ )
+}
+
macro_rules! impl_parcelable {
{Serialize, $ty:ty, $write_fn:path} => {
impl Serialize for $ty {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`, and any `$ty` literal value is safe to pass to
@@ -313,7 +326,7 @@
{Deserialize, $ty:ty, $read_fn:path} => {
impl Deserialize for $ty {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut val = Self::default();
unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -329,7 +342,7 @@
{SerializeArray, $ty:ty, $write_array_fn:path} => {
impl SerializeArray for $ty {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()`
@@ -353,7 +366,7 @@
{DeserializeArray, $ty:ty, $read_array_fn:path} => {
impl DeserializeArray for $ty {
- fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> {
+ fn deserialize_array(parcel: &BorrowedParcel<'_>) -> Result<Option<Vec<Self>>> {
let mut vec: Option<Vec<MaybeUninit<Self>>> = None;
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -371,11 +384,8 @@
// Safety: We are assuming that the NDK correctly
// initialized every element of the vector by now, so we
// know that all the MaybeUninits are now properly
- // initialized. We can transmute from Vec<MaybeUninit<T>> to
- // Vec<T> because MaybeUninit<T> has the same alignment and
- // size as T, so the pointer to the vector allocation will
- // be compatible.
- mem::transmute(vec)
+ // initialized.
+ vec.map(|vec| vec_assume_init(vec))
};
Ok(vec)
}
@@ -443,19 +453,19 @@
impl DeserializeArray for bool {}
impl Serialize for u8 {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
(*self as i8).serialize(parcel)
}
}
impl Deserialize for u8 {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
i8::deserialize(parcel).map(|v| v as u8)
}
}
impl SerializeArray for u8 {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
@@ -474,19 +484,19 @@
}
impl Serialize for i16 {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
(*self as u16).serialize(parcel)
}
}
impl Deserialize for i16 {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
u16::deserialize(parcel).map(|v| v as i16)
}
}
impl SerializeArray for i16 {
- fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> {
+ fn serialize_array(slice: &[Self], parcel: &mut BorrowedParcel<'_>) -> Result<()> {
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
// `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a
@@ -505,7 +515,7 @@
}
impl SerializeOption for str {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
match this {
None => unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -541,7 +551,7 @@
}
impl Serialize for str {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Some(self).serialize(parcel)
}
}
@@ -549,7 +559,7 @@
impl SerializeArray for &str {}
impl Serialize for String {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Some(self.as_str()).serialize(parcel)
}
}
@@ -557,13 +567,13 @@
impl SerializeArray for String {}
impl SerializeOption for String {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(String::as_str), parcel)
}
}
impl Deserialize for Option<String> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut vec: Option<Vec<u8>> = None;
let status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an `AParcel`.
@@ -591,7 +601,7 @@
impl DeserializeArray for Option<String> {}
impl Deserialize for String {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
@@ -601,19 +611,19 @@
impl DeserializeArray for String {}
impl<T: SerializeArray> Serialize for [T] {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeArray::serialize_array(self, parcel)
}
}
impl<T: SerializeArray> Serialize for Vec<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeArray::serialize_array(&self[..], parcel)
}
}
impl<T: SerializeArray> SerializeOption for [T] {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
if let Some(v) = this {
SerializeArray::serialize_array(v, parcel)
} else {
@@ -623,13 +633,13 @@
}
impl<T: SerializeArray> SerializeOption for Vec<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(Vec::as_slice), parcel)
}
}
impl<T: DeserializeArray> Deserialize for Vec<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
DeserializeArray::deserialize_array(parcel)
.transpose()
.unwrap_or(Err(StatusCode::UNEXPECTED_NULL))
@@ -637,25 +647,58 @@
}
impl<T: DeserializeArray> DeserializeOption for Vec<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
DeserializeArray::deserialize_array(parcel)
}
}
+impl<T: SerializeArray, const N: usize> Serialize for [T; N] {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ // forwards to T::serialize_array.
+ SerializeArray::serialize_array(self, parcel)
+ }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeOption for [T; N] {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ SerializeOption::serialize_option(this.map(|arr| &arr[..]), parcel)
+ }
+}
+
+impl<T: SerializeArray, const N: usize> SerializeArray for [T; N] {}
+
+impl<T: DeserializeArray, const N: usize> Deserialize for [T; N] {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
+ let vec = DeserializeArray::deserialize_array(parcel)
+ .transpose()
+ .unwrap_or(Err(StatusCode::UNEXPECTED_NULL))?;
+ vec.try_into().or(Err(StatusCode::BAD_VALUE))
+ }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeOption for [T; N] {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
+ let vec = DeserializeArray::deserialize_array(parcel)?;
+ vec.map(|v| v.try_into().or(Err(StatusCode::BAD_VALUE))).transpose()
+ }
+}
+
+impl<T: DeserializeArray, const N: usize> DeserializeArray for [T; N] {}
+
impl Serialize for Stability {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
i32::from(*self).serialize(parcel)
}
}
impl Deserialize for Stability {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
i32::deserialize(parcel).and_then(Stability::try_from)
}
}
impl Serialize for Status {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
unsafe {
// Safety: `Parcel` always contains a valid pointer to an `AParcel`
// and `Status` always contains a valid pointer to an `AStatus`, so
@@ -670,7 +713,7 @@
}
impl Deserialize for Status {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let mut status_ptr = ptr::null_mut();
let ret_status = unsafe {
// Safety: `Parcel` always contains a valid pointer to an
@@ -691,56 +734,60 @@
}
impl<T: Serialize + FromIBinder + ?Sized> Serialize for Strong<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(&**self, parcel)
}
}
impl<T: SerializeOption + FromIBinder + ?Sized> SerializeOption for Strong<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(|b| &**b), parcel)
}
}
+impl<T: Serialize + FromIBinder + ?Sized> SerializeArray for Strong<T> {}
+
impl<T: FromIBinder + ?Sized> Deserialize for Strong<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
let ibinder: SpIBinder = parcel.read()?;
FromIBinder::try_from(ibinder)
}
}
impl<T: FromIBinder + ?Sized> DeserializeOption for Strong<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
let ibinder: Option<SpIBinder> = parcel.read()?;
ibinder.map(FromIBinder::try_from).transpose()
}
}
+impl<T: FromIBinder + ?Sized> DeserializeArray for Strong<T> {}
+
// We need these to support Option<&T> for all T
impl<T: Serialize + ?Sized> Serialize for &T {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(*self, parcel)
}
}
impl<T: SerializeOption + ?Sized> SerializeOption for &T {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.copied(), parcel)
}
}
impl<T: SerializeOption> Serialize for Option<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(self.as_ref(), parcel)
}
}
impl<T: DeserializeOption> Deserialize for Option<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
DeserializeOption::deserialize_option(parcel)
}
- fn deserialize_from(&mut self, parcel: &Parcel) -> Result<()> {
+ fn deserialize_from(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
DeserializeOption::deserialize_option_from(self, parcel)
}
}
@@ -758,7 +805,7 @@
impl $crate::parcel::Serialize for $parcelable {
fn serialize(
&self,
- parcel: &mut $crate::parcel::Parcel,
+ parcel: &mut $crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
<Self as $crate::parcel::SerializeOption>::serialize_option(
Some(self),
@@ -772,7 +819,7 @@
impl $crate::parcel::SerializeOption for $parcelable {
fn serialize_option(
this: Option<&Self>,
- parcel: &mut $crate::parcel::Parcel,
+ parcel: &mut $crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
if let Some(this) = this {
use $crate::parcel::Parcelable;
@@ -792,13 +839,12 @@
/// `Deserialize`, `DeserializeArray` and `DeserializeOption` for
/// structured parcelables. The target type must implement the
/// `Parcelable` trait.
-/// ```
#[macro_export]
macro_rules! impl_deserialize_for_parcelable {
($parcelable:ident) => {
impl $crate::parcel::Deserialize for $parcelable {
fn deserialize(
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<Self> {
$crate::parcel::DeserializeOption::deserialize_option(parcel)
.transpose()
@@ -806,7 +852,7 @@
}
fn deserialize_from(
&mut self,
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
if status == $crate::parcel::NULL_PARCELABLE_FLAG {
@@ -822,7 +868,7 @@
impl $crate::parcel::DeserializeOption for $parcelable {
fn deserialize_option(
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<Option<Self>> {
let mut result = None;
Self::deserialize_option_from(&mut result, parcel)?;
@@ -830,7 +876,7 @@
}
fn deserialize_option_from(
this: &mut Option<Self>,
- parcel: &$crate::parcel::Parcel,
+ parcel: &$crate::parcel::BorrowedParcel<'_>,
) -> $crate::Result<()> {
let status: i32 = parcel.read()?;
if status == $crate::parcel::NULL_PARCELABLE_FLAG {
@@ -847,326 +893,332 @@
}
impl<T: Serialize> Serialize for Box<T> {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
Serialize::serialize(&**self, parcel)
}
}
impl<T: Deserialize> Deserialize for Box<T> {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
Deserialize::deserialize(parcel).map(Box::new)
}
}
impl<T: SerializeOption> SerializeOption for Box<T> {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
SerializeOption::serialize_option(this.map(|inner| &**inner), parcel)
}
}
impl<T: DeserializeOption> DeserializeOption for Box<T> {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<Self>> {
DeserializeOption::deserialize_option(parcel).map(|t| t.map(Box::new))
}
}
-#[test]
-fn test_custom_parcelable() {
- struct Custom(u32, bool, String, Vec<String>);
+#[cfg(test)]
+mod tests {
+ use crate::Parcel;
+ use super::*;
- impl Serialize for Custom {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
- self.0.serialize(parcel)?;
- self.1.serialize(parcel)?;
- self.2.serialize(parcel)?;
- self.3.serialize(parcel)
+ #[test]
+ fn test_custom_parcelable() {
+ struct Custom(u32, bool, String, Vec<String>);
+
+ impl Serialize for Custom {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
+ self.0.serialize(parcel)?;
+ self.1.serialize(parcel)?;
+ self.2.serialize(parcel)?;
+ self.3.serialize(parcel)
+ }
}
- }
- impl Deserialize for Custom {
- fn deserialize(parcel: &Parcel) -> Result<Self> {
- Ok(Custom(
- parcel.read()?,
- parcel.read()?,
- parcel.read()?,
- parcel.read::<Option<Vec<String>>>()?.unwrap(),
- ))
+ impl Deserialize for Custom {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<Self> {
+ Ok(Custom(
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read()?,
+ parcel.read::<Option<Vec<String>>>()?.unwrap(),
+ ))
+ }
}
+
+ let string8 = "Custom Parcelable".to_string();
+
+ let s1 = "str1".to_string();
+ let s2 = "str2".to_string();
+ let s3 = "str3".to_string();
+
+ let strs = vec![s1, s2, s3];
+
+ let custom = Custom(123_456_789, true, string8, strs);
+
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
+
+ assert!(custom.serialize(&mut parcel.borrowed()).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let custom2 = Custom::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(custom2.0, 123_456_789);
+ assert!(custom2.1);
+ assert_eq!(custom2.2, custom.2);
+ assert_eq!(custom2.3, custom.3);
}
- let string8 = "Custom Parcelable".to_string();
+ #[test]
+ #[allow(clippy::excessive_precision)]
+ fn test_slice_parcelables() {
+ let bools = [true, false, false, true];
- let s1 = "str1".to_string();
- let s2 = "str2".to_string();
- let s3 = "str3".to_string();
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
- let strs = vec![s1, s2, s3];
+ assert!(bools.serialize(&mut parcel.borrowed()).is_ok());
- let custom = Custom(123_456_789, true, string8, strs);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
+ assert_eq!(parcel.read::<u32>().unwrap(), 4);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 0);
+ assert_eq!(parcel.read::<u32>().unwrap(), 1);
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
- assert!(custom.serialize(&mut parcel).is_ok());
+ let vec = Vec::<bool>::deserialize(parcel.borrowed_ref()).unwrap();
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
+ assert_eq!(vec, [true, false, false, true]);
+
+ let u8s = [101u8, 255, 42, 117];
+
+ let mut parcel = Parcel::new();
+ let start = parcel.get_data_position();
+
+ assert!(parcel.write(&u8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
+ assert_eq!(vec, [101, 255, 42, 117]);
+
+ let i8s = [-128i8, 127, 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert!(parcel.write(&i8s[..]).is_ok());
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u8>::deserialize(parcel.borrowed_ref()).unwrap();
+ assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
+
+ let u16s = [u16::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u16s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u16>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
+
+ let i16s = [i16::max_value(), i16::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i16s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i16>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
+
+ let u32s = [u32::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
+
+ let i32s = [i32::max_value(), i32::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
+ assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
+ assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
+
+ let u64s = [u64::max_value(), 12_345, 42, 117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(u64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<u64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
+
+ let i64s = [i64::max_value(), i64::min_value(), 42, -117];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(i64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<i64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
+
+ let f32s = [
+ std::f32::NAN,
+ std::f32::INFINITY,
+ 1.23456789,
+ std::f32::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f32s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f32>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f32s[1..]);
+
+ let f64s = [
+ std::f64::NAN,
+ std::f64::INFINITY,
+ 1.234567890123456789,
+ std::f64::EPSILON,
+ ];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(f64s.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<f64>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ // NAN != NAN so we can't use it in the assert_eq:
+ assert!(vec[0].is_nan());
+ assert_eq!(vec[1..], f64s[1..]);
+
+ let s1 = "Hello, Binder!";
+ let s2 = "This is a utf8 string.";
+ let s3 = "Some more text here.";
+ let s4 = "Embedded nulls \0 \0";
+
+ let strs = [s1, s2, s3, s4];
+
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+ assert!(strs.serialize(&mut parcel.borrowed()).is_ok());
+ unsafe {
+ assert!(parcel.set_data_position(start).is_ok());
+ }
+
+ let vec = Vec::<String>::deserialize(parcel.borrowed_ref()).unwrap();
+
+ assert_eq!(vec, strs);
}
-
- let custom2 = Custom::deserialize(&parcel).unwrap();
-
- assert_eq!(custom2.0, 123_456_789);
- assert!(custom2.1);
- assert_eq!(custom2.2, custom.2);
- assert_eq!(custom2.3, custom.3);
-}
-
-#[test]
-#[allow(clippy::excessive_precision)]
-fn test_slice_parcelables() {
- let bools = [true, false, false, true];
-
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
-
- assert!(bools.serialize(&mut parcel).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4);
- assert_eq!(parcel.read::<u32>().unwrap(), 1);
- assert_eq!(parcel.read::<u32>().unwrap(), 0);
- assert_eq!(parcel.read::<u32>().unwrap(), 0);
- assert_eq!(parcel.read::<u32>().unwrap(), 1);
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<bool>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [true, false, false, true]);
-
- let u8s = [101u8, 255, 42, 117];
-
- let mut parcel = Parcel::new();
- let start = parcel.get_data_position();
-
- assert!(parcel.write(&u8s[..]).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u8>::deserialize(&parcel).unwrap();
- assert_eq!(vec, [101, 255, 42, 117]);
-
- let i8s = [-128i8, 127, 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert!(parcel.write(&i8s[..]).is_ok());
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u8>::deserialize(&parcel).unwrap();
- assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]);
-
- let u16s = [u16::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u16s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u16>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]);
-
- let i16s = [i16::max_value(), i16::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i16s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i16>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]);
-
- let u32s = [u32::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u32>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]);
-
- let i32s = [i32::max_value(), i32::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items
- assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value()
- assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42
- assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i32>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]);
-
- let u64s = [u64::max_value(), 12_345, 42, 117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(u64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<u64>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]);
-
- let i64s = [i64::max_value(), i64::min_value(), 42, -117];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(i64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<i64>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]);
-
- let f32s = [
- std::f32::NAN,
- std::f32::INFINITY,
- 1.23456789,
- std::f32::EPSILON,
- ];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(f32s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<f32>::deserialize(&parcel).unwrap();
-
- // NAN != NAN so we can't use it in the assert_eq:
- assert!(vec[0].is_nan());
- assert_eq!(vec[1..], f32s[1..]);
-
- let f64s = [
- std::f64::NAN,
- std::f64::INFINITY,
- 1.234567890123456789,
- std::f64::EPSILON,
- ];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(f64s.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<f64>::deserialize(&parcel).unwrap();
-
- // NAN != NAN so we can't use it in the assert_eq:
- assert!(vec[0].is_nan());
- assert_eq!(vec[1..], f64s[1..]);
-
- let s1 = "Hello, Binder!";
- let s2 = "This is a utf8 string.";
- let s3 = "Some more text here.";
- let s4 = "Embedded nulls \0 \0";
-
- let strs = [s1, s2, s3, s4];
-
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
- assert!(strs.serialize(&mut parcel).is_ok());
- unsafe {
- assert!(parcel.set_data_position(start).is_ok());
- }
-
- let vec = Vec::<String>::deserialize(&parcel).unwrap();
-
- assert_eq!(vec, strs);
}
diff --git a/libs/binder/rust/src/parcel/parcelable_holder.rs b/libs/binder/rust/src/parcel/parcelable_holder.rs
index bccfd2d..b4282b2 100644
--- a/libs/binder/rust/src/parcel/parcelable_holder.rs
+++ b/libs/binder/rust/src/parcel/parcelable_holder.rs
@@ -16,7 +16,7 @@
use crate::binder::Stability;
use crate::error::{Result, StatusCode};
-use crate::parcel::{OwnedParcel, Parcel, Parcelable};
+use crate::parcel::{Parcel, BorrowedParcel, Parcelable};
use crate::{impl_deserialize_for_parcelable, impl_serialize_for_parcelable};
use downcast_rs::{impl_downcast, DowncastSync};
@@ -50,7 +50,7 @@
parcelable: Arc<dyn AnyParcelable>,
name: String,
},
- Parcel(OwnedParcel),
+ Parcel(Parcel),
}
impl Default for ParcelableHolderData {
@@ -148,7 +148,6 @@
}
}
ParcelableHolderData::Parcel(ref mut parcel) => {
- let parcel = parcel.borrowed();
unsafe {
// Safety: 0 should always be a valid position.
parcel.set_data_position(0)?;
@@ -160,7 +159,7 @@
}
let mut parcelable = T::default();
- parcelable.read_from_parcel(&parcel)?;
+ parcelable.read_from_parcel(parcel.borrowed_ref())?;
let parcelable = Arc::new(parcelable);
let result = Arc::clone(&parcelable);
@@ -181,7 +180,7 @@
impl_deserialize_for_parcelable!(ParcelableHolder);
impl Parcelable for ParcelableHolder {
- fn write_to_parcel(&self, parcel: &mut Parcel) -> Result<()> {
+ fn write_to_parcel(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write(&self.stability)?;
let mut data = self.data.lock().unwrap();
@@ -214,14 +213,13 @@
Ok(())
}
ParcelableHolderData::Parcel(ref mut p) => {
- let p = p.borrowed();
parcel.write(&p.get_data_size())?;
- parcel.append_all_from(&p)
+ parcel.append_all_from(&*p)
}
}
}
- fn read_from_parcel(&mut self, parcel: &Parcel) -> Result<()> {
+ fn read_from_parcel(&mut self, parcel: &BorrowedParcel<'_>) -> Result<()> {
self.stability = parcel.read()?;
let data_size: i32 = parcel.read()?;
@@ -242,10 +240,8 @@
.checked_add(data_size)
.ok_or(StatusCode::BAD_VALUE)?;
- let mut new_parcel = OwnedParcel::new();
- new_parcel
- .borrowed()
- .append_from(parcel, data_start, data_size)?;
+ let mut new_parcel = Parcel::new();
+ new_parcel.append_from(parcel, data_start, data_size)?;
*self.data.get_mut().unwrap() = ParcelableHolderData::Parcel(new_parcel);
unsafe {
diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs
index a8d0c33..760d862 100644
--- a/libs/binder/rust/src/proxy.rs
+++ b/libs/binder/rust/src/proxy.rs
@@ -22,8 +22,7 @@
};
use crate::error::{status_result, Result, StatusCode};
use crate::parcel::{
- Deserialize, DeserializeArray, DeserializeOption, OwnedParcel, Parcel, Serialize, SerializeArray,
- SerializeOption,
+ Parcel, BorrowedParcel, Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption,
};
use crate::sys;
@@ -235,7 +234,7 @@
}
impl<T: AsNative<sys::AIBinder>> IBinderInternal for T {
- fn prepare_transact(&self) -> Result<OwnedParcel> {
+ fn prepare_transact(&self) -> Result<Parcel> {
let mut input = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -255,16 +254,16 @@
// Safety: At this point, `input` is either a valid, owned `AParcel`
// pointer, or null. `OwnedParcel::from_raw` safely handles both cases,
// taking ownership of the parcel.
- OwnedParcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
+ Parcel::from_raw(input).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
fn submit_transact(
&self,
code: TransactionCode,
- data: OwnedParcel,
+ data: Parcel,
flags: TransactionFlags,
- ) -> Result<OwnedParcel> {
+ ) -> Result<Parcel> {
let mut reply = ptr::null_mut();
let status = unsafe {
// Safety: `SpIBinder` guarantees that `self` always contains a
@@ -299,7 +298,7 @@
// construct a `Parcel` out of it. `AIBinder_transact` passes
// ownership of the `reply` parcel to Rust, so we need to
// construct an owned variant.
- OwnedParcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
+ Parcel::from_raw(reply).ok_or(StatusCode::UNEXPECTED_NULL)
}
}
@@ -324,6 +323,7 @@
status_result(status)
}
+ #[cfg(not(android_vndk))]
fn set_requesting_sid(&mut self, enable: bool) {
unsafe { sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) };
}
@@ -415,13 +415,13 @@
}
impl Serialize for SpIBinder {
- fn serialize(&self, parcel: &mut Parcel) -> Result<()> {
+ fn serialize(&self, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(Some(self))
}
}
impl SerializeOption for SpIBinder {
- fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> {
+ fn serialize_option(this: Option<&Self>, parcel: &mut BorrowedParcel<'_>) -> Result<()> {
parcel.write_binder(this)
}
}
@@ -429,7 +429,7 @@
impl SerializeArray for SpIBinder {}
impl Deserialize for SpIBinder {
- fn deserialize(parcel: &Parcel) -> Result<SpIBinder> {
+ fn deserialize(parcel: &BorrowedParcel<'_>) -> Result<SpIBinder> {
parcel
.read_binder()
.transpose()
@@ -438,7 +438,7 @@
}
impl DeserializeOption for SpIBinder {
- fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> {
+ fn deserialize_option(parcel: &BorrowedParcel<'_>) -> Result<Option<SpIBinder>> {
parcel.read_binder()
}
}
diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs
index 0e05f10..0aef744 100644
--- a/libs/binder/rust/src/state.rs
+++ b/libs/binder/rust/src/state.rs
@@ -99,6 +99,17 @@
}
}
+ /// Determine whether the current thread is currently executing an incoming transaction.
+ ///
+ /// \return true if the current thread is currently executing an incoming transaction, and false
+ /// otherwise.
+ pub fn is_handling_transaction() -> bool {
+ unsafe {
+ // Safety: Safe FFI
+ sys::AIBinder_isHandlingTransaction()
+ }
+ }
+
/// This function makes the client's security context available to the
/// service calling this function. This can be used for access control.
/// It does not suffer from the TOCTOU issues of get_calling_pid.
diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp
index ecc61f4..2d1175b 100644
--- a/libs/binder/rust/tests/Android.bp
+++ b/libs/binder/rust/tests/Android.bp
@@ -13,6 +13,8 @@
rustlibs: [
"libbinder_rs",
"libselinux_bindgen",
+ "libbinder_tokio_rs",
+ "libtokio",
],
shared_libs: [
"libselinux",
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index 335e8d8..80dc476 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -16,8 +16,8 @@
//! Rust Binder crate integration tests
-use binder::declare_binder_interface;
-use binder::parcel::Parcel;
+use binder::{declare_binder_enum, declare_binder_interface};
+use binder::parcel::BorrowedParcel;
use binder::{
Binder, BinderFeatures, IBinderInternal, Interface, StatusCode, ThreadState, TransactionCode,
FIRST_CALL_TRANSACTION,
@@ -100,6 +100,7 @@
Test = FIRST_CALL_TRANSACTION,
GetDumpArgs,
GetSelinuxContext,
+ GetIsHandlingTransaction,
}
impl TryFrom<u32> for TestTransactionCode {
@@ -112,6 +113,7 @@
_ if c == TestTransactionCode::GetSelinuxContext as u32 => {
Ok(TestTransactionCode::GetSelinuxContext)
}
+ _ if c == TestTransactionCode::GetIsHandlingTransaction as u32 => Ok(TestTransactionCode::GetIsHandlingTransaction),
_ => Err(StatusCode::UNKNOWN_TRANSACTION),
}
}
@@ -140,6 +142,10 @@
ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
sid.ok_or(StatusCode::UNEXPECTED_NULL)
}
+
+ fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ Ok(binder::is_handling_transaction())
+ }
}
/// Trivial testing binder interface
@@ -152,6 +158,24 @@
/// Returns the caller's SELinux context
fn get_selinux_context(&self) -> binder::Result<String>;
+
+ /// Returns the value of calling `is_handling_transaction`.
+ fn get_is_handling_transaction(&self) -> binder::Result<bool>;
+}
+
+/// Async trivial testing binder interface
+pub trait IATest<P>: Interface {
+ /// Returns a test string
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+
+ /// Return the arguments sent via dump
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>>;
+
+ /// Returns the value of calling `is_handling_transaction`.
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>>;
}
declare_binder_interface! {
@@ -160,19 +184,21 @@
proxy: BpTest {
x: i32 = 100
},
+ async: IATest,
}
}
fn on_transact(
service: &dyn ITest,
code: TransactionCode,
- _data: &Parcel,
- reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ reply: &mut BorrowedParcel<'_>,
) -> binder::Result<()> {
match code.try_into()? {
TestTransactionCode::Test => reply.write(&service.test()?),
TestTransactionCode::GetDumpArgs => reply.write(&service.get_dump_args()?),
TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+ TestTransactionCode::GetIsHandlingTransaction => reply.write(&service.get_is_handling_transaction()?),
}
}
@@ -199,6 +225,49 @@
)?;
reply.read()
}
+
+ fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ let reply = self.binder.transact(
+ TestTransactionCode::GetIsHandlingTransaction as TransactionCode,
+ 0,
+ |_| Ok(()),
+ )?;
+ reply.read()
+ }
+}
+
+impl<P: binder::BinderAsyncPool> IATest<P> for BpTest {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetDumpArgs as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetSelinuxContext as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
+
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ let binder = self.binder.clone();
+ P::spawn(
+ move || binder.transact(TestTransactionCode::GetIsHandlingTransaction as TransactionCode, 0, |_| Ok(())),
+ |reply| async move { reply?.read() }
+ )
+ }
}
impl ITest for Binder<BnTest> {
@@ -213,6 +282,32 @@
fn get_selinux_context(&self) -> binder::Result<String> {
self.0.get_selinux_context()
}
+
+ fn get_is_handling_transaction(&self) -> binder::Result<bool> {
+ self.0.get_is_handling_transaction()
+ }
+}
+
+impl<P: binder::BinderAsyncPool> IATest<P> for Binder<BnTest> {
+ fn test(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.test();
+ Box::pin(async move { res })
+ }
+
+ fn get_dump_args(&self) -> binder::BoxFuture<'static, binder::Result<Vec<String>>> {
+ let res = self.0.get_dump_args();
+ Box::pin(async move { res })
+ }
+
+ fn get_selinux_context(&self) -> binder::BoxFuture<'static, binder::Result<String>> {
+ let res = self.0.get_selinux_context();
+ Box::pin(async move { res })
+ }
+
+ fn get_is_handling_transaction(&self) -> binder::BoxFuture<'static, binder::Result<bool>> {
+ let res = self.0.get_is_handling_transaction();
+ Box::pin(async move { res })
+ }
}
/// Trivial testing binder interface
@@ -228,8 +323,8 @@
fn on_transact_same_descriptor(
_service: &dyn ITestSameDescriptor,
_code: TransactionCode,
- _data: &Parcel,
- _reply: &mut Parcel,
+ _data: &BorrowedParcel<'_>,
+ _reply: &mut BorrowedParcel<'_>,
) -> binder::Result<()> {
Ok(())
}
@@ -238,6 +333,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;
@@ -255,7 +367,9 @@
SpIBinder, StatusCode, Strong,
};
- use super::{BnTest, ITest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
+ use binder_tokio::Tokio;
+
+ use super::{BnTest, ITest, IATest, ITestSameDescriptor, TestService, RUST_SERVICE_BINARY};
pub struct ScopedServiceProcess(Child);
@@ -303,12 +417,47 @@
binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(),
Some(StatusCode::NAME_NOT_FOUND)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
// The service manager service isn't an ITest, so this must fail.
assert_eq!(
binder::get_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::get_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ }
+
+ #[tokio::test]
+ async fn check_services_async() {
+ let mut sm = binder::get_service("manager").expect("Did not get manager binder service");
+ assert!(sm.is_binder_alive());
+ assert!(sm.ping_binder().is_ok());
+
+ assert!(binder::get_service("this_service_does_not_exist").is_none());
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("this_service_does_not_exist").await.err(),
+ Some(StatusCode::NAME_NOT_FOUND)
+ );
+
+ // The service manager service isn't an ITest, so this must fail.
+ assert_eq!(
+ binder_tokio::get_interface::<dyn ITest>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
+ assert_eq!(
+ binder_tokio::get_interface::<dyn IATest<Tokio>>("manager").await.err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -323,6 +472,10 @@
binder::wait_for_interface::<dyn ITest>("manager").err(),
Some(StatusCode::BAD_TYPE)
);
+ assert_eq!(
+ binder::wait_for_interface::<dyn IATest<Tokio>>("manager").err(),
+ Some(StatusCode::BAD_TYPE)
+ );
}
#[test]
@@ -334,6 +487,15 @@
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
+ #[tokio::test]
+ async fn trivial_client_async() {
+ let service_name = "trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "trivial_client_test");
+ }
+
#[test]
fn wait_for_trivial_client() {
let service_name = "wait_for_trivial_client_test";
@@ -343,23 +505,73 @@
assert_eq!(test_client.test().unwrap(), "wait_for_trivial_client_test");
}
+ #[tokio::test]
+ async fn wait_for_trivial_client_async() {
+ let service_name = "wait_for_trivial_client_test";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::wait_for_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(test_client.test().await.unwrap(), "wait_for_trivial_client_test");
+ }
+
+ fn get_expected_selinux_context() -> &'static str {
+ unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ .to_str()
+ .expect("context was invalid UTF-8")
+ }
+ }
+
#[test]
fn get_selinux_context() {
let service_name = "get_selinux_context";
let _process = ScopedServiceProcess::new(service_name);
let test_client: Strong<dyn ITest> =
binder::get_interface(service_name).expect("Did not get manager binder service");
- let expected_context = unsafe {
- let mut out_ptr = ptr::null_mut();
- assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
- assert!(!out_ptr.is_null());
- CStr::from_ptr(out_ptr)
- };
assert_eq!(
test_client.get_selinux_context().unwrap(),
- expected_context
- .to_str()
- .expect("context was invalid UTF-8"),
+ get_expected_selinux_context()
+ );
+ }
+
+ #[tokio::test]
+ async fn get_selinux_context_async() {
+ let service_name = "get_selinux_context_async";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ assert_eq!(
+ test_client.get_selinux_context().await.unwrap(),
+ get_expected_selinux_context()
+ );
+ }
+
+ #[tokio::test]
+ async fn get_selinux_context_sync_to_async() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ let test_client = test_client.into_async::<Tokio>();
+ assert_eq!(
+ test_client.get_selinux_context().await.unwrap(),
+ get_expected_selinux_context()
+ );
+ }
+
+ #[tokio::test]
+ async fn get_selinux_context_async_to_sync() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ let test_client = test_client.into_sync();
+ assert_eq!(
+ test_client.get_selinux_context().unwrap(),
+ get_expected_selinux_context()
);
}
@@ -720,4 +932,45 @@
Err(err) => assert_eq!(err, binder::StatusCode::BAD_VALUE),
}
}
+
+ #[test]
+ fn get_is_handling_transaction() {
+ let service_name = "get_is_handling_transaction";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ // Should be true externally.
+ assert!(test_client.get_is_handling_transaction().unwrap());
+
+ // Should be false locally.
+ assert!(!binder::is_handling_transaction());
+
+ // Should also be false in spawned thread.
+ std::thread::spawn(|| {
+ assert!(!binder::is_handling_transaction());
+ }).join().unwrap();
+ }
+
+ #[tokio::test]
+ async fn get_is_handling_transaction_async() {
+ let service_name = "get_is_handling_transaction_async";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Strong<dyn IATest<Tokio>> =
+ binder_tokio::get_interface(service_name).await.expect("Did not get manager binder service");
+ // Should be true externally.
+ assert!(test_client.get_is_handling_transaction().await.unwrap());
+
+ // Should be false locally.
+ assert!(!binder::is_handling_transaction());
+
+ // Should also be false in spawned task.
+ tokio::spawn(async {
+ assert!(!binder::is_handling_transaction());
+ }).await.unwrap();
+
+ // And in spawn_blocking task.
+ tokio::task::spawn_blocking(|| {
+ assert!(!binder::is_handling_transaction());
+ }).await.unwrap();
+ }
}
diff --git a/libs/binder/rust/tests/serialization.rs b/libs/binder/rust/tests/serialization.rs
index 66ba846..1fc761e 100644
--- a/libs/binder/rust/tests/serialization.rs
+++ b/libs/binder/rust/tests/serialization.rs
@@ -20,7 +20,7 @@
use binder::declare_binder_interface;
use binder::parcel::ParcelFileDescriptor;
use binder::{
- Binder, BinderFeatures, ExceptionCode, Interface, Parcel, Result, SpIBinder, Status,
+ Binder, BinderFeatures, BorrowedParcel, ExceptionCode, Interface, Result, SpIBinder, Status,
StatusCode, TransactionCode,
};
@@ -111,8 +111,8 @@
fn on_transact(
_service: &dyn ReadParcelTest,
code: TransactionCode,
- parcel: &Parcel,
- reply: &mut Parcel,
+ parcel: &BorrowedParcel<'_>,
+ reply: &mut BorrowedParcel<'_>,
) -> Result<()> {
match code {
bindings::Transaction_TEST_BOOL => {
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index c893899..63a4b2c 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -112,7 +112,7 @@
BINDER_LIB_TEST_NOP_TRANSACTION_WAIT,
BINDER_LIB_TEST_GETPID,
BINDER_LIB_TEST_ECHO_VECTOR,
- BINDER_LIB_TEST_REJECT_BUF,
+ BINDER_LIB_TEST_REJECT_OBJECTS,
BINDER_LIB_TEST_CAN_GET_SID,
};
@@ -1166,13 +1166,53 @@
memcpy(parcelData, &obj, sizeof(obj));
data.setDataSize(sizeof(obj));
+ EXPECT_EQ(data.objectsCount(), 1);
+
// Either the kernel should reject this transaction (if it's correct), but
// if it's not, the server implementation should return an error if it
// finds an object in the received Parcel.
- EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_BUF, data, &reply),
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
Not(StatusEq(NO_ERROR)));
}
+TEST_F(BinderLibTest, WeakRejected) {
+ Parcel data, reply;
+ sp<IBinder> server = addServer();
+ ASSERT_TRUE(server != nullptr);
+
+ auto binder = sp<BBinder>::make();
+ wp<BBinder> wpBinder(binder);
+ flat_binder_object obj{
+ .hdr = {.type = BINDER_TYPE_WEAK_BINDER},
+ .flags = 0,
+ .binder = reinterpret_cast<uintptr_t>(wpBinder.get_refs()),
+ .cookie = reinterpret_cast<uintptr_t>(wpBinder.unsafe_get()),
+ };
+ data.setDataCapacity(1024);
+ // Write a bogus object at offset 0 to get an entry in the offset table
+ data.writeFileDescriptor(0);
+ EXPECT_EQ(data.objectsCount(), 1);
+ uint8_t *parcelData = const_cast<uint8_t *>(data.data());
+ // And now, overwrite it with the weak binder
+ memcpy(parcelData, &obj, sizeof(obj));
+ data.setDataSize(sizeof(obj));
+
+ // a previous bug caused other objects to be released an extra time, so we
+ // test with an object that libbinder will actually try to release
+ EXPECT_EQ(OK, data.writeStrongBinder(sp<BBinder>::make()));
+
+ EXPECT_EQ(data.objectsCount(), 2);
+
+ // send it many times, since previous error was memory corruption, make it
+ // more likely that the server crashes
+ for (size_t i = 0; i < 100; i++) {
+ EXPECT_THAT(server->transact(BINDER_LIB_TEST_REJECT_OBJECTS, data, &reply),
+ StatusEq(BAD_VALUE));
+ }
+
+ EXPECT_THAT(server->pingBinder(), StatusEq(NO_ERROR));
+}
+
TEST_F(BinderLibTest, GotSid) {
sp<IBinder> server = addServer();
@@ -1566,7 +1606,7 @@
reply->writeUint64Vector(vector);
return NO_ERROR;
}
- case BINDER_LIB_TEST_REJECT_BUF: {
+ case BINDER_LIB_TEST_REJECT_OBJECTS: {
return data.objectsCount() == 0 ? BAD_VALUE : NO_ERROR;
}
case BINDER_LIB_TEST_CAN_GET_SID: {
diff --git a/libs/binder/tests/binderParcelUnitTest.cpp b/libs/binder/tests/binderParcelUnitTest.cpp
index 4950b23..aee15d8 100644
--- a/libs/binder/tests/binderParcelUnitTest.cpp
+++ b/libs/binder/tests/binderParcelUnitTest.cpp
@@ -16,15 +16,17 @@
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
+#include <binder/Status.h>
#include <cutils/ashmem.h>
#include <gtest/gtest.h>
using android::IPCThreadState;
using android::OK;
using android::Parcel;
+using android::status_t;
using android::String16;
using android::String8;
-using android::status_t;
+using android::binder::Status;
TEST(Parcel, NonNullTerminatedString8) {
String8 kTestString = String8("test-is-good");
@@ -60,6 +62,19 @@
EXPECT_EQ(output.size(), 0);
}
+TEST(Parcel, EnforceNoDataAvail) {
+ const int32_t kTestInt = 42;
+ const String8 kTestString = String8("test-is-good");
+ Parcel p;
+ p.writeInt32(kTestInt);
+ p.writeString8(kTestString);
+ p.setDataPosition(0);
+ EXPECT_EQ(kTestInt, p.readInt32());
+ EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_BAD_PARCELABLE);
+ EXPECT_EQ(kTestString, p.readString8());
+ EXPECT_EQ(p.enforceNoDataAvail().exceptionCode(), Status::Exception::EX_NONE);
+}
+
// Tests a second operation results in a parcel at the same location as it
// started.
void parcelOpSameLength(const std::function<void(Parcel*)>& a, const std::function<void(Parcel*)>& b) {
diff --git a/libs/binder/tests/binderRpcTest.cpp b/libs/binder/tests/binderRpcTest.cpp
index 7bbe3d7..5a96b78 100644
--- a/libs/binder/tests/binderRpcTest.cpp
+++ b/libs/binder/tests/binderRpcTest.cpp
@@ -22,6 +22,7 @@
#include <aidl/IBinderRpcTest.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android/binder_auto_utils.h>
#include <android/binder_libbinder.h>
#include <binder/Binder.h>
@@ -1303,11 +1304,20 @@
}
TEST_P(BinderRpc, UseKernelBinderCallingId) {
+ bool okToFork = ProcessState::selfOrNull() == nullptr;
+
auto proc = createRpcTestSocketServerProcess({});
- // we can't allocate IPCThreadState so actually the first time should
- // succeed :(
- EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ // If this process has used ProcessState already, then the forked process
+ // cannot use it at all. If this process hasn't used it (depending on the
+ // order tests are run), then the forked process can use it, and we'll only
+ // catch the invalid usage the second time. Such is the burden of global
+ // state!
+ if (okToFork) {
+ // we can't allocate IPCThreadState so actually the first time should
+ // succeed :(
+ EXPECT_OK(proc.rootIface->useKernelBinderCallingId());
+ }
// second time! we catch the error :)
EXPECT_EQ(DEAD_OBJECT, proc.rootIface->useKernelBinderCallingId().transactionError());
@@ -1505,7 +1515,17 @@
auto socket = rpcServer->releaseServer();
auto keepAlive = sp<BBinder>::make();
- ASSERT_EQ(OK, binder->setRpcClientDebug(std::move(socket), keepAlive));
+ auto setRpcClientDebugStatus = binder->setRpcClientDebug(std::move(socket), keepAlive);
+
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ ASSERT_EQ(INVALID_OPERATION, setRpcClientDebugStatus)
+ << "setRpcClientDebug should return INVALID_OPERATION on non-debuggable builds, "
+ "but get "
+ << statusToString(setRpcClientDebugStatus);
+ GTEST_SKIP();
+ }
+
+ ASSERT_EQ(OK, setRpcClientDebugStatus);
auto rpcSession = RpcSession::make();
ASSERT_EQ(OK, rpcSession->setupInetClient("127.0.0.1", port));
@@ -1999,5 +2019,6 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
+
return RUN_ALL_TESTS();
}
diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp
index e4f57b0..13f7195 100644
--- a/libs/binder/tests/parcel_fuzzer/binder.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder.cpp
@@ -22,6 +22,7 @@
#include <android/os/IServiceManager.h>
#include <binder/ParcelableHolder.h>
#include <binder/PersistableBundle.h>
+#include <binder/Status.h>
using ::android::status_t;
using ::android::base::HexString;
@@ -100,6 +101,7 @@
PARCEL_READ_NO_STATUS(size_t, dataAvail),
PARCEL_READ_NO_STATUS(size_t, dataPosition),
PARCEL_READ_NO_STATUS(size_t, dataCapacity),
+ PARCEL_READ_NO_STATUS(::android::binder::Status, enforceNoDataAvail),
[] (const ::android::Parcel& p, uint8_t pos) {
FUZZ_LOG() << "about to setDataPosition: " << pos;
p.setDataPosition(pos);
@@ -192,6 +194,8 @@
// only reading one binder type for now
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readStrongBinder),
PARCEL_READ_WITH_STATUS(android::sp<android::os::IServiceManager>, readNullableStrongBinder),
+ PARCEL_READ_WITH_STATUS(std::vector<android::sp<android::os::IServiceManager>>, readStrongBinderVector),
+ PARCEL_READ_WITH_STATUS(std::optional<std::vector<android::sp<android::os::IServiceManager>>>, readStrongBinderVector),
PARCEL_READ_WITH_STATUS(::std::unique_ptr<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
PARCEL_READ_WITH_STATUS(::std::optional<std::vector<android::sp<android::IBinder>>>, readStrongBinderVector),
@@ -231,6 +235,32 @@
PARCEL_READ_WITH_STATUS(std::optional<std::vector<std::optional<std::string>>>, readUtf8VectorFromUtf16Vector),
PARCEL_READ_WITH_STATUS(std::vector<std::string>, readUtf8VectorFromUtf16Vector),
+#define COMMA ,
+ PARCEL_READ_WITH_STATUS(std::array<uint8_t COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<uint8_t COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<char16_t COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<char16_t COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<std::string COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<std::string> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<android::String16 COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<android::String16> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<android::sp<android::IBinder> COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<android::sp<android::IBinder> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::optional<ExampleParcelable> COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ByteEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<ByteEnum COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<IntEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<IntEnum COMMA 3>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<LongEnum COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<LongEnum COMMA 3>>, readFixedArray),
+ // nested arrays
+ PARCEL_READ_WITH_STATUS(std::array<std::array<uint8_t COMMA 3> COMMA 4>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<uint8_t COMMA 3> COMMA 4>>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::array<ExampleParcelable COMMA 3>, readFixedArray),
+ PARCEL_READ_WITH_STATUS(std::optional<std::array<std::array<std::optional<ExampleParcelable> COMMA 3> COMMA 4>>, readFixedArray),
+#undef COMMA
+
[] (const android::Parcel& p, uint8_t /*len*/) {
FUZZ_LOG() << "about to read flattenable";
ExampleFlattenable f;
@@ -308,6 +338,15 @@
status_t status = p.hasFileDescriptorsInRange(offset, length, &result);
FUZZ_LOG() << " status: " << status << " result: " << result;
},
+ [] (const ::android::Parcel& p, uint8_t /* data */) {
+ FUZZ_LOG() << "about to call compareDataInRange() with status";
+ size_t thisOffset = p.readUint32();
+ size_t otherOffset = p.readUint32();
+ size_t length = p.readUint32();
+ int result;
+ status_t status = p.compareDataInRange(thisOffset, p, otherOffset, length, &result);
+ FUZZ_LOG() << " status: " << status << " result: " << result;
+ },
};
// clang-format on
#pragma clang diagnostic pop
diff --git a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
index c0a762d..5aeb5cc 100644
--- a/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
+++ b/libs/binder/tests/parcel_fuzzer/binder_ndk.cpp
@@ -25,6 +25,7 @@
// TODO(b/142061461): parent class
class SomeParcelable {
public:
+ binder_status_t writeToParcel(AParcel* /*parcel*/) { return STATUS_OK; }
binder_status_t readFromParcel(const AParcel* parcel) {
return AParcel_readInt32(parcel, &mValue);
}
@@ -33,6 +34,41 @@
int32_t mValue = 0;
};
+class ISomeInterface : public ::ndk::ICInterface {
+public:
+ ISomeInterface() = default;
+ virtual ~ISomeInterface() = default;
+ static binder_status_t readFromParcel(const AParcel* parcel,
+ std::shared_ptr<ISomeInterface>* instance);
+};
+
+static binder_status_t onTransact(AIBinder*, transaction_code_t, const AParcel*, AParcel*) {
+ return STATUS_UNKNOWN_TRANSACTION;
+}
+
+static AIBinder_Class* g_class = ::ndk::ICInterface::defineClass("ISomeInterface", onTransact);
+
+class BpSomeInterface : public ::ndk::BpCInterface<ISomeInterface> {
+public:
+ explicit BpSomeInterface(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}
+ virtual ~BpSomeInterface() = default;
+};
+
+binder_status_t ISomeInterface::readFromParcel(const AParcel* parcel,
+ std::shared_ptr<ISomeInterface>* instance) {
+ ::ndk::SpAIBinder binder;
+ binder_status_t status = AParcel_readStrongBinder(parcel, binder.getR());
+ if (status == STATUS_OK) {
+ if (AIBinder_associateClass(binder.get(), g_class)) {
+ *instance = std::static_pointer_cast<ISomeInterface>(
+ ::ndk::ICInterface::asInterface(binder.get()));
+ } else {
+ *instance = ::ndk::SharedRefBase::make<BpSomeInterface>(binder);
+ }
+ }
+ return status;
+}
+
#define PARCEL_READ(T, FUN) \
[](const NdkParcelAdapter& p, uint8_t /*data*/) { \
FUZZ_LOG() << "about to read " #T " using " #FUN " with status"; \
@@ -100,6 +136,8 @@
PARCEL_READ(std::optional<std::vector<ndk::SpAIBinder>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<ndk::ScopedFileDescriptor>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<ndk::ScopedFileDescriptor>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::vector<std::shared_ptr<ISomeInterface>>, ndk::AParcel_readVector),
+ PARCEL_READ(std::optional<std::vector<std::shared_ptr<ISomeInterface>>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<int32_t>, ndk::AParcel_readVector),
PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<uint32_t>, ndk::AParcel_readVector),
@@ -118,5 +156,21 @@
PARCEL_READ(std::optional<std::vector<char16_t>>, ndk::AParcel_readVector),
PARCEL_READ(std::vector<int32_t>, ndk::AParcel_resizeVector),
PARCEL_READ(std::optional<std::vector<int32_t>>, ndk::AParcel_resizeVector),
+
+ // methods for std::array<T,N>
+#define COMMA ,
+ PARCEL_READ(std::array<bool COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<uint8_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<char16_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<int32_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<int64_t COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<float COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<double COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<std::string COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<SomeParcelable COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<ndk::SpAIBinder COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<ndk::ScopedFileDescriptor COMMA 3>, ndk::AParcel_readData),
+ PARCEL_READ(std::array<std::shared_ptr<ISomeInterface> COMMA 3>, ndk::AParcel_readData),
+#undef COMMA
};
// clang-format on
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 570af71..1fd2c62 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -12,7 +12,7 @@
srcs: ["cputimeinstate.cpp"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"liblog",
"libnetdutils"
@@ -31,7 +31,7 @@
srcs: ["testtimeinstate.cpp"],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libtimeinstate",
"libnetdutils",
diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp
index f2d0943..858739c 100644
--- a/libs/graphicsenv/GpuStatsInfo.cpp
+++ b/libs/graphicsenv/GpuStatsInfo.cpp
@@ -88,6 +88,7 @@
if ((status = parcel->writeBool(cpuVulkanInUse)) != OK) return status;
if ((status = parcel->writeBool(falsePrerotation)) != OK) return status;
if ((status = parcel->writeBool(gles1InUse)) != OK) return status;
+ if ((status = parcel->writeBool(angleInUse)) != OK) return status;
return OK;
}
@@ -101,6 +102,7 @@
if ((status = parcel->readBool(&cpuVulkanInUse)) != OK) return status;
if ((status = parcel->readBool(&falsePrerotation)) != OK) return status;
if ((status = parcel->readBool(&gles1InUse)) != OK) return status;
+ if ((status = parcel->readBool(&angleInUse)) != OK) return status;
return OK;
}
@@ -111,6 +113,7 @@
StringAppendF(&result, "cpuVulkanInUse = %d\n", cpuVulkanInUse);
StringAppendF(&result, "falsePrerotation = %d\n", falsePrerotation);
StringAppendF(&result, "gles1InUse = %d\n", gles1InUse);
+ StringAppendF(&result, "angleInUse = %d\n", angleInUse);
result.append("glDriverLoadingTime:");
for (int32_t loadingTime : glDriverLoadingTime) {
StringAppendF(&result, " %d", loadingTime);
diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
index 9aba69f..5b513d2 100644
--- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
+++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <string>
#include <vector>
@@ -52,7 +53,7 @@
};
/*
- * class for transporting gpu app stats from GpuService to authorized recipents.
+ * class for transporting gpu app stats from GpuService to authorized recipients.
* This class is intended to be a data container.
*/
class GpuStatsAppInfo : public Parcelable {
@@ -72,6 +73,9 @@
bool cpuVulkanInUse = false;
bool falsePrerotation = false;
bool gles1InUse = false;
+ bool angleInUse = false;
+
+ std::chrono::time_point<std::chrono::system_clock> lastAccessTime;
};
/*
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 19a29c1..e5dcdea 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",
@@ -250,10 +248,7 @@
"libpdx_headers",
],
- pgo: {
- sampling: true,
- profile_file: "libgui/libgui.profdata",
- },
+ afdo: true,
lto: {
thin: true,
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 1ae90f3..85a4b67 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -132,13 +132,12 @@
}
}
-BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
- int width, int height, int32_t format)
- : mSurfaceControl(surface),
- mSize(width, height),
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
+ : mSurfaceControl(nullptr),
+ mSize(1, 1),
mRequestedSize(mSize),
- mFormat(format),
- mNextTransaction(nullptr) {
+ mFormat(PIXEL_FORMAT_RGBA_8888),
+ mSyncTransaction(nullptr) {
createBufferQueue(&mProducer, &mConsumer);
// since the adapter is in the client process, set dequeue timeout
// explicitly so that dequeueBuffer will block
@@ -158,25 +157,21 @@
mBufferItemConsumer->setName(String8(consumerName.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
- mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
- mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
mBufferItemConsumer->setBlastBufferQueue(this);
ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
-
- mTransformHint = mSurfaceControl->getTransformHint();
- mBufferItemConsumer->setTransformHint(mTransformHint);
- SurfaceComposerClient::Transaction()
- .setFlags(surface, layer_state_t::eEnableBackpressure,
- layer_state_t::eEnableBackpressure)
- .setApplyToken(mApplyToken)
- .apply();
mNumAcquired = 0;
mNumFrameAvailable = 0;
- BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", width,
- height, format, mTransformHint);
+ BQA_LOGV("BLASTBufferQueue created width=%d height=%d format=%d mTransformHint=%d", mSize.width,
+ mSize.height, mFormat, mTransformHint);
+}
+
+BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface,
+ int width, int height, int32_t format)
+ : BLASTBufferQueue(name) {
+ update(surface, width, height, format);
}
BLASTBufferQueue::~BLASTBufferQueue() {
@@ -228,12 +223,9 @@
// If the buffer supports scaling, update the frame immediately since the client may
// want to scale the existing buffer to the new size.
mSize = mRequestedSize;
- // We only need to update the scale if we've received at least one buffer. The reason
- // for this is the scale is calculated based on the requested size and buffer size.
- // If there's no buffer, the scale will always be 1.
SurfaceComposerClient::Transaction* destFrameTransaction =
(outTransaction) ? outTransaction : &t;
- if (mSurfaceControl != nullptr && mLastBufferInfo.hasBuffer) {
+ if (mSurfaceControl != nullptr) {
destFrameTransaction->setDestinationFrame(mSurfaceControl,
Rect(0, 0, newSize.getWidth(),
newSize.getHeight()));
@@ -290,13 +282,13 @@
// case, we don't actually want to flush the frames in between since they will get
// processed and merged with the sync transaction and released earlier than if they
// were sent to SF
- if (mWaitForTransactionCallback && mNextTransaction == nullptr &&
+ if (mWaitForTransactionCallback && mSyncTransaction == nullptr &&
currFrameNumber >= mLastAcquiredFrameNumber) {
mWaitForTransactionCallback = false;
flushShadowQueue();
}
} else {
- BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+ BQA_LOGE("Failed to find matching SurfaceControl in transactionCommittedCallback");
}
} else {
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
@@ -346,7 +338,7 @@
stat.frameEventStats.dequeueReadyTime);
}
} else {
- BQA_LOGE("Failed to find matching SurfaceControl in transaction callback");
+ BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback");
}
} else {
BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was "
@@ -407,20 +399,11 @@
// Release all buffers that are beyond the ones that we need to hold
while (mPendingRelease.size() > numPendingBuffersToHold) {
- const auto releaseBuffer = mPendingRelease.front();
+ const auto releasedBuffer = mPendingRelease.front();
mPendingRelease.pop_front();
- auto it = mSubmitted.find(releaseBuffer.callbackId);
- if (it == mSubmitted.end()) {
- BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
- releaseBuffer.callbackId.to_string().c_str());
- return;
- }
- mNumAcquired--;
- BQA_LOGV("released %s", releaseBuffer.callbackId.to_string().c_str());
- mBufferItemConsumer->releaseBuffer(it->second, releaseBuffer.releaseFence);
- mSubmitted.erase(it);
+ releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence);
// Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let
- // onFrameAvailable handle processing them since it will merge with the nextTransaction.
+ // onFrameAvailable handle processing them since it will merge with the syncTransaction.
if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
}
@@ -432,6 +415,20 @@
mCallbackCV.notify_all();
}
+void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
+ const sp<Fence>& releaseFence) {
+ auto it = mSubmitted.find(callbackId);
+ if (it == mSubmitted.end()) {
+ BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s",
+ callbackId.to_string().c_str());
+ return;
+ }
+ mNumAcquired--;
+ BQA_LOGV("released %s", callbackId.to_string().c_str());
+ mBufferItemConsumer->releaseBuffer(it->second, releaseFence);
+ mSubmitted.erase(it);
+}
+
void BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
ATRACE_CALL();
@@ -502,11 +499,9 @@
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
- incStrong((void*)transactionCommittedCallbackThunk);
- const bool sizeHasChanged = mRequestedSize != mSize;
+ const bool updateDestinationFrame = mRequestedSize != mSize;
mSize = mRequestedSize;
- const bool updateDestinationFrame = sizeHasChanged || !mLastBufferInfo.hasBuffer;
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
@@ -522,7 +517,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) {
@@ -589,48 +584,83 @@
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
+void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
+ if (mWaitForTransactionCallback && mNumFrameAvailable > 0) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked. The release callback will be async so we need to wait
+ // on max acquired to make sure we have the capacity to acquire another buffer.
+ if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting to flush shadow queue...");
+ mCallbackCV.wait(lock);
+ }
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ }
+
+ while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
+ BQA_LOGD("waiting for free buffer.");
+ mCallbackCV.wait(lock);
+ }
+}
+
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
ATRACE_CALL();
std::unique_lock _lock{mMutex};
- const bool nextTransactionSet = mNextTransaction != nullptr;
- BQA_LOGV("onFrameAvailable-start nextTransactionSet=%s", boolToString(nextTransactionSet));
- if (nextTransactionSet) {
- if (mWaitForTransactionCallback) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mNextTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(_lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
+ const bool syncTransactionSet = mSyncTransaction != nullptr;
+ BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
+
+ if (syncTransactionSet) {
+ bool mayNeedToWaitForBuffer = true;
+ // If we are going to re-use the same mSyncTransaction, release the buffer that may already
+ // be set in the Transaction. This is to allow us a free slot early to continue processing
+ // a new buffer.
+ if (!mAcquireSingleBuffer) {
+ auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
+ if (bufferData) {
+ BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
+ bufferData->frameNumber);
+ releaseBuffer(bufferData->releaseCallbackId, bufferData->acquireFence);
+ // Because we just released a buffer, we know there's no need to wait for a free
+ // buffer.
+ mayNeedToWaitForBuffer = false;
}
}
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(_lock);
+ if (mayNeedToWaitForBuffer) {
+ flushAndWaitForFreeBuffer(_lock);
}
}
// add to shadow queue
mNumFrameAvailable++;
+ if (mWaitForTransactionCallback && mNumFrameAvailable == 2) {
+ acquireAndReleaseBuffer();
+ }
ATRACE_INT(mQueuedBufferTrace.c_str(),
mNumFrameAvailable + mNumAcquired - mPendingRelease.size());
- BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber,
- boolToString(nextTransactionSet));
+ BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s", item.mFrameNumber,
+ boolToString(syncTransactionSet));
- if (nextTransactionSet) {
- acquireNextBufferLocked(std::move(mNextTransaction));
- mNextTransaction = nullptr;
+ 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;
+ }
mWaitForTransactionCallback = true;
} else if (!mWaitForTransactionCallback) {
acquireNextBufferLocked(std::nullopt);
@@ -652,9 +682,11 @@
mDequeueTimestamps.erase(bufferId);
};
-void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
+void BLASTBufferQueue::setSyncTransaction(SurfaceComposerClient::Transaction* t,
+ bool acquireSingleBuffer) {
std::lock_guard _lock{mMutex};
- mNextTransaction = t;
+ mSyncTransaction = t;
+ mAcquireSingleBuffer = mSyncTransaction ? acquireSingleBuffer : true;
}
bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
diff --git a/libs/gui/CpuConsumer.cpp b/libs/gui/CpuConsumer.cpp
index 8edf604..a626970 100644
--- a/libs/gui/CpuConsumer.cpp
+++ b/libs/gui/CpuConsumer.cpp
@@ -71,6 +71,7 @@
case HAL_PIXEL_FORMAT_Y8:
case HAL_PIXEL_FORMAT_Y16:
case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
index c986b82..ee80082 100644
--- a/libs/gui/DisplayEventDispatcher.cpp
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -33,10 +33,13 @@
// using just a few large reads.
static const size_t EVENT_BUFFER_SIZE = 100;
+static constexpr nsecs_t WAITING_FOR_VSYNC_TIMEOUT = ms2ns(300);
+
DisplayEventDispatcher::DisplayEventDispatcher(
const sp<Looper>& looper, ISurfaceComposer::VsyncSource vsyncSource,
ISurfaceComposer::EventRegistrationFlags eventRegistration)
- : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false) {
+ : mLooper(looper), mReceiver(vsyncSource, eventRegistration), mWaitingForVsync(false),
+ mLastVsyncCount(0), mLastScheduleVsyncTime(0) {
ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
}
@@ -86,6 +89,7 @@
}
mWaitingForVsync = true;
+ mLastScheduleVsyncTime = systemTime(SYSTEM_TIME_MONOTONIC);
}
return OK;
}
@@ -124,9 +128,21 @@
this, ns2ms(vsyncTimestamp), to_string(vsyncDisplayId).c_str(), vsyncCount,
vsyncEventData.id);
mWaitingForVsync = false;
+ mLastVsyncCount = vsyncCount;
dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount, vsyncEventData);
}
+ if (mWaitingForVsync) {
+ const nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ const nsecs_t vsyncScheduleDelay = currentTime - mLastScheduleVsyncTime;
+ if (vsyncScheduleDelay > WAITING_FOR_VSYNC_TIMEOUT) {
+ ALOGW("Vsync time out! vsyncScheduleDelay=%" PRId64 "ms", ns2ms(vsyncScheduleDelay));
+ mWaitingForVsync = false;
+ dispatchVsync(currentTime, vsyncDisplayId /* displayId is not used */,
+ ++mLastVsyncCount, vsyncEventData /* empty data */);
+ }
+ }
+
return 1; // keep the callback
}
@@ -166,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/FrameTimelineInfo.cpp b/libs/gui/FrameTimelineInfo.cpp
index 9231a57..3800b88 100644
--- a/libs/gui/FrameTimelineInfo.cpp
+++ b/libs/gui/FrameTimelineInfo.cpp
@@ -33,12 +33,14 @@
status_t FrameTimelineInfo::write(Parcel& output) const {
SAFE_PARCEL(output.writeInt64, vsyncId);
SAFE_PARCEL(output.writeInt32, inputEventId);
+ SAFE_PARCEL(output.writeInt64, startTimeNanos);
return NO_ERROR;
}
status_t FrameTimelineInfo::read(const Parcel& input) {
SAFE_PARCEL(input.readInt64, &vsyncId);
SAFE_PARCEL(input.readInt32, &inputEventId);
+ SAFE_PARCEL(input.readInt64, &startTimeNanos);
return NO_ERROR;
}
@@ -48,16 +50,19 @@
if (other.vsyncId > vsyncId) {
vsyncId = other.vsyncId;
inputEventId = other.inputEventId;
+ startTimeNanos = other.startTimeNanos;
}
} else if (vsyncId == INVALID_VSYNC_ID) {
vsyncId = other.vsyncId;
inputEventId = other.inputEventId;
+ startTimeNanos = other.startTimeNanos;
}
}
void FrameTimelineInfo::clear() {
vsyncId = INVALID_VSYNC_ID;
inputEventId = IInputConstants::INVALID_INPUT_EVENT_ID;
+ startTimeNanos = 0;
}
}; // namespace android
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 30d19e3..b3647d6 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -301,7 +301,7 @@
// continues to use it.
sp<GraphicBuffer> buffer = new GraphicBuffer(
kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
"[GLConsumer debug texture]");
uint32_t* bits;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
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 3c8289f..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;
@@ -1150,41 +1152,6 @@
return reply.readInt32();
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override {
- if (!outToken) return BAD_VALUE;
-
- Parcel data, reply;
- status_t err = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed writing interface token: %s (%d)",
- strerror(-err), -err);
- return err;
- }
-
- err = remote()->transact(BnSurfaceComposer::ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN, data,
- &reply);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed to transact: %s (%d)", strerror(-err),
- err);
- return err;
- }
-
- err = reply.readInt32();
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: call failed: %s (%d)", strerror(-err), err);
- return err;
- }
-
- err = reply.readStrongBinder(outToken);
- if (err != NO_ERROR) {
- ALOGE("acquireFrameRateFlexibilityToken: failed reading binder token: %s (%d)",
- strerror(-err), err);
- return err;
- }
-
- return NO_ERROR;
- }
-
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override {
Parcel data, reply;
@@ -2073,16 +2040,6 @@
reply->writeInt32(result);
return NO_ERROR;
}
- case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> token;
- status_t result = acquireFrameRateFlexibilityToken(&token);
- reply->writeInt32(result);
- if (result == NO_ERROR) {
- reply->writeStrongBinder(token);
- }
- return NO_ERROR;
- }
case SET_FRAME_TIMELINE_INFO: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> binder;
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index f848e4f..ec0573a 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,
@@ -546,9 +550,7 @@
}
bool InputWindowCommands::empty() const {
- bool empty = true;
- empty = focusRequests.empty() && !syncInputWindows;
- return empty;
+ return focusRequests.empty() && !syncInputWindows;
}
void InputWindowCommands::clear() {
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 353a91d..20c4146 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1846,9 +1846,10 @@
ATRACE_CALL();
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
+ auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
ALOGV("Surface::%s", __func__);
- return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId});
+ return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
}
bool Surface::transformToDisplayInverse() const {
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2713be0..aafa5e4 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;
@@ -61,6 +62,14 @@
ANDROID_SINGLETON_STATIC_INSTANCE(ComposerService);
+namespace {
+// Initialize transaction id counter used to generate transaction ids
+std::atomic<uint32_t> idCounter = 0;
+int64_t generateId() {
+ return (((int64_t)getpid()) << 32) | ++idCounter;
+}
+} // namespace
+
ComposerService::ComposerService()
: Singleton<ComposerService>() {
Mutex::Autolock _l(mLock);
@@ -352,6 +361,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 +373,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);
@@ -413,6 +426,14 @@
return callback;
}
+void TransactionCompletedListener::removeReleaseBufferCallback(
+ const ReleaseCallbackId& callbackId) {
+ {
+ std::scoped_lock<std::mutex> lock(mMutex);
+ popReleaseBufferCallbackLocked(callbackId);
+ }
+}
+
// ---------------------------------------------------------------------------
void removeDeadBufferCallback(void* /*context*/, uint64_t graphicBufferId);
@@ -522,10 +543,6 @@
// ---------------------------------------------------------------------------
-// Initialize transaction id counter used to generate transaction ids
-// Transactions will start counting at 1, 0 is used for invalid transactions
-std::atomic<uint32_t> SurfaceComposerClient::Transaction::idCounter = 1;
-
SurfaceComposerClient::Transaction::Transaction() {
mId = generateId();
}
@@ -557,9 +574,6 @@
return nullptr;
}
-int64_t SurfaceComposerClient::Transaction::generateId() {
- return (((int64_t)getpid()) << 32) | idCounter++;
-}
status_t SurfaceComposerClient::Transaction::readFromParcel(const Parcel* parcel) {
const uint32_t forceSynchronous = parcel->readUint32();
@@ -812,7 +826,7 @@
sp<IBinder> applyToken = IInterface::asBinder(TransactionCompletedListener::getIInstance());
sf->setTransactionState(FrameTimelineInfo{}, {}, {}, 0, applyToken, {}, systemTime(), true,
- uncacheBuffer, false, {}, 0 /* Undefined transactionId */);
+ uncacheBuffer, false, {}, generateId());
}
void SurfaceComposerClient::Transaction::cacheBuffers() {
@@ -1307,6 +1321,28 @@
return *this;
}
+std::optional<BufferData> SurfaceComposerClient::Transaction::getAndClearBuffer(
+ const sp<SurfaceControl>& sc) {
+ layer_state_t* s = getLayerState(sc);
+ if (!s) {
+ return std::nullopt;
+ }
+ if (!(s->what & layer_state_t::eBufferChanged)) {
+ return std::nullopt;
+ }
+
+ BufferData bufferData = s->bufferData;
+
+ TransactionCompletedListener::getInstance()->removeReleaseBufferCallback(
+ bufferData.releaseCallbackId);
+ BufferData emptyBufferData;
+ s->what &= ~layer_state_t::eBufferChanged;
+ s->bufferData = emptyBufferData;
+
+ mContainsBuffer = false;
+ return bufferData;
+}
+
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffer(
const sp<SurfaceControl>& sc, const sp<GraphicBuffer>& buffer,
const std::optional<sp<Fence>>& fence, const std::optional<uint64_t>& frameNumber,
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index 5f3a726..e92be01 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -43,6 +43,10 @@
return flags.test(Flag::SPLIT_TOUCH);
}
+bool WindowInfo::isSpy() const {
+ return inputFeatures.test(Feature::SPY);
+}
+
bool WindowInfo::overlaps(const WindowInfo* other) const {
return frameLeft < other->frameRight && frameRight > other->frameLeft &&
frameTop < other->frameBottom && frameBottom > other->frameTop;
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/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 3881620..8a2f392 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -74,6 +74,7 @@
: public ConsumerBase::FrameAvailableListener, public BufferItemConsumer::BufferFreedListener
{
public:
+ BLASTBufferQueue(const std::string& name);
BLASTBufferQueue(const std::string& name, const sp<SurfaceControl>& surface, int width,
int height, int32_t format);
@@ -88,13 +89,13 @@
void onFrameDequeued(const uint64_t) override;
void onFrameCancelled(const uint64_t) override;
- virtual void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats);
- void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats);
+ void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
+ virtual void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats);
void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence,
std::optional<uint32_t> currentMaxAcquiredBufferCount);
- void setNextTransaction(SurfaceComposerClient::Transaction *t);
+ void setSyncTransaction(SurfaceComposerClient::Transaction* t, bool acquireSingleBuffer = true);
void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber);
void applyPendingTransactions(uint64_t frameNumber);
@@ -132,6 +133,9 @@
void flushShadowQueue() REQUIRES(mMutex);
void acquireAndReleaseBuffer() REQUIRES(mMutex);
+ void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
+ REQUIRES(mMutex);
+ void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
@@ -208,7 +212,7 @@
sp<IGraphicBufferProducer> mProducer;
sp<BLASTBufferItemConsumer> mBufferItemConsumer;
- SurfaceComposerClient::Transaction* mNextTransaction GUARDED_BY(mMutex);
+ SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex);
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
@@ -239,7 +243,11 @@
std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex);
uint32_t mCurrentMaxAcquiredBufferCount;
- bool mWaitForTransactionCallback = false;
+ bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false;
+
+ // Flag to determine if syncTransaction should only acquire a single buffer and then clear or
+ // continue to acquire buffers until explicitly cleared
+ bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true;
};
} // namespace android
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
index 92c89b8..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
@@ -80,6 +77,8 @@
sp<Looper> mLooper;
DisplayEventReceiver mReceiver;
bool mWaitingForVsync;
+ uint32_t mLastVsyncCount;
+ nsecs_t mLastScheduleVsyncTime;
std::vector<FrameRateOverride> mFrameRateOverrides;
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/FrameTimelineInfo.h b/libs/gui/include/gui/FrameTimelineInfo.h
index a23c202..255ce56 100644
--- a/libs/gui/include/gui/FrameTimelineInfo.h
+++ b/libs/gui/include/gui/FrameTimelineInfo.h
@@ -36,6 +36,9 @@
// not directly vendor available.
int32_t inputEventId = 0;
+ // The current time in nanoseconds the application started to render the frame.
+ int64_t startTimeNanos = 0;
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
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 408497d..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 {
@@ -512,14 +514,6 @@
int8_t compatibility, int8_t changeFrameRateStrategy) = 0;
/*
- * Acquire a frame rate flexibility token from SurfaceFlinger. While this token is acquired,
- * surface flinger will freely switch between frame rates in any way it sees fit, regardless of
- * the current restrictions applied by DisplayManager. This is useful to get consistent behavior
- * for tests. Release the token by releasing the returned IBinder reference.
- */
- virtual status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) = 0;
-
- /*
* Sets the frame timeline vsync info received from choreographer that corresponds to next
* buffer submitted on that surface.
*/
@@ -616,6 +610,7 @@
GET_GAME_CONTENT_TYPE_SUPPORT, // Deprecated. Use GET_DYNAMIC_DISPLAY_INFO instead.
SET_GAME_CONTENT_TYPE,
SET_FRAME_RATE,
+ // Deprecated. Use DisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN,
SET_FRAME_TIMELINE_INFO,
ADD_TRANSACTION_TRACE_LISTENER,
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 e62c76e..17b4846 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,
@@ -365,8 +366,6 @@
class Transaction : public Parcelable {
private:
- static std::atomic<uint32_t> idCounter;
- int64_t generateId();
void releaseBufferIfOverwriting(const layer_state_t& state);
protected:
@@ -493,6 +492,7 @@
const std::optional<uint64_t>& frameNumber = std::nullopt,
const ReleaseCallbackId& id = ReleaseCallbackId::INVALID_ID,
ReleaseBufferCallback callback = nullptr);
+ std::optional<BufferData> getAndClearBuffer(const sp<SurfaceControl>& sc);
Transaction& setDataspace(const sp<SurfaceControl>& sc, ui::Dataspace dataspace);
Transaction& setHdrMetadata(const sp<SurfaceControl>& sc, const HdrMetadata& hdrMetadata);
Transaction& setSurfaceDamageRegion(const sp<SurfaceControl>& sc,
@@ -751,6 +751,8 @@
void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence,
uint32_t currentMaxAcquiredBufferCount) override;
+ void removeReleaseBufferCallback(const ReleaseCallbackId& callbackId);
+
// For Testing Only
static void setInstance(const sp<TransactionCompletedListener>&);
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index b9ec14a..e5d2684 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -16,6 +16,8 @@
#pragma once
+#include <stdarg.h>
+
#include <cutils/trace.h>
#include <utils/Trace.h>
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index 54a372c..808afe4 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -137,6 +137,7 @@
DISABLE_USER_ACTIVITY = 1u << 2,
DROP_INPUT = 1u << 3,
DROP_INPUT_IF_OBSCURED = 1u << 4,
+ SPY = 1u << 5,
};
/* These values are filled in by the WM and passed through SurfaceFlinger
@@ -215,6 +216,8 @@
bool supportsSplitTouch() const;
+ bool isSpy() const;
+
bool overlaps(const WindowInfo* other) const;
bool operator==(const WindowInfo& inputChannel) const;
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index b2d5048..48b8621 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -72,31 +72,30 @@
int height, int32_t format)
: BLASTBufferQueue(name, surface, width, height, format) {}
- void transactionCommittedCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
- const std::vector<SurfaceControlStats>& stats) override {
- BLASTBufferQueue::transactionCommittedCallback(latchTime, presentFence, stats);
-
+ void transactionCallback(nsecs_t latchTime, const sp<Fence>& presentFence,
+ const std::vector<SurfaceControlStats>& stats) override {
+ BLASTBufferQueue::transactionCallback(latchTime, presentFence, stats);
uint64_t frameNumber = stats[0].frameEventStats.frameNumber;
{
std::unique_lock lock{frameNumberMutex};
- mLastTransactionCommittedFrameNumber = frameNumber;
- mCommittedCV.notify_all();
+ mLastTransactionFrameNumber = frameNumber;
+ mWaitForCallbackCV.notify_all();
}
}
void waitForCallback(int64_t frameNumber) {
std::unique_lock lock{frameNumberMutex};
// Wait until all but one of the submitted buffers have been released.
- while (mLastTransactionCommittedFrameNumber < frameNumber) {
- mCommittedCV.wait(lock);
+ while (mLastTransactionFrameNumber < frameNumber) {
+ mWaitForCallbackCV.wait(lock);
}
}
private:
std::mutex frameNumberMutex;
- std::condition_variable mCommittedCV;
- int64_t mLastTransactionCommittedFrameNumber = -1;
+ std::condition_variable mWaitForCallbackCV;
+ int64_t mLastTransactionFrameNumber = -1;
};
class BLASTBufferQueueHelper {
@@ -110,15 +109,15 @@
mBlastBufferQueueAdapter->update(sc, width, height, PIXEL_FORMAT_RGBA_8888);
}
- void setNextTransaction(Transaction* next) {
- mBlastBufferQueueAdapter->setNextTransaction(next);
+ void setSyncTransaction(Transaction* next, bool acquireSingleBuffer = true) {
+ mBlastBufferQueueAdapter->setSyncTransaction(next, acquireSingleBuffer);
}
int getWidth() { return mBlastBufferQueueAdapter->mSize.width; }
int getHeight() { return mBlastBufferQueueAdapter->mSize.height; }
- Transaction* getNextTransaction() { return mBlastBufferQueueAdapter->mNextTransaction; }
+ Transaction* getSyncTransaction() { return mBlastBufferQueueAdapter->mSyncTransaction; }
sp<IGraphicBufferProducer> getIGraphicBufferProducer() {
return mBlastBufferQueueAdapter->getIGraphicBufferProducer();
@@ -144,6 +143,11 @@
mBlastBufferQueueAdapter->waitForCallback(frameNumber);
}
+ void validateNumFramesSubmitted(int64_t numFramesSubmitted) {
+ std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
+ ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size());
+ }
+
private:
sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter;
};
@@ -299,7 +303,7 @@
auto ret = igbp->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight,
PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
nullptr, nullptr);
- ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_TRUE(ret == IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION || ret == NO_ERROR);
ASSERT_EQ(OK, igbp->requestBuffer(slot, &buf));
uint32_t* bufData;
@@ -338,7 +342,7 @@
ASSERT_EQ(mSurfaceControl, adapter.getSurfaceControl());
ASSERT_EQ(mDisplayWidth, adapter.getWidth());
ASSERT_EQ(mDisplayHeight, adapter.getHeight());
- ASSERT_EQ(nullptr, adapter.getNextTransaction());
+ ASSERT_EQ(nullptr, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, Update) {
@@ -359,11 +363,11 @@
ASSERT_EQ(mDisplayHeight / 2, height);
}
-TEST_F(BLASTBufferQueueTest, SetNextTransaction) {
+TEST_F(BLASTBufferQueueTest, SetSyncTransaction) {
BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
- Transaction next;
- adapter.setNextTransaction(&next);
- ASSERT_EQ(&next, adapter.getNextTransaction());
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
+ ASSERT_EQ(&sync, adapter.getSyncTransaction());
}
TEST_F(BLASTBufferQueueTest, DISABLED_onFrameAvailable_ApplyDesiredPresentTime) {
@@ -802,8 +806,8 @@
sp<IGraphicBufferProducer> igbProducer;
setUpProducer(adapter, igbProducer);
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
// queue non sync buffer, so this one should get blocked
@@ -812,14 +816,14 @@
queueBuffer(igbProducer, r, g, b, presentTimeDelay);
CallbackHelper transactionCallback;
- next.addTransactionCompletedCallback(transactionCallback.function,
+ sync.addTransactionCompletedCallback(transactionCallback.function,
transactionCallback.getContext())
.apply();
CallbackData callbackData;
transactionCallback.getCallbackData(&callbackData);
- // capture screen and verify that it is red
+ // capture screen and verify that it is green
ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(0, 255, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
@@ -842,16 +846,16 @@
Transaction mainTransaction;
- Transaction next;
- adapter.setNextTransaction(&next);
+ Transaction sync;
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 1 buffer to be released even before sending to SurfaceFlinger
mProducerListener->waitOnNumberReleased(1);
@@ -882,24 +886,24 @@
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue another buffer without setting next transaction
+ // queue another buffer without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 1 buffer to be released because the non sync transaction should merge
// with the sync
mProducerListener->waitOnNumberReleased(1);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 2 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(2);
@@ -930,26 +934,26 @@
Transaction mainTransaction;
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 3 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
// Expect 4 buffers to be released due to merging the two syncs.
mProducerListener->waitOnNumberReleased(4);
@@ -987,14 +991,14 @@
// Send a buffer to SF
queueBuffer(igbProducer, 0, 255, 0, 0);
- Transaction next;
+ Transaction sync;
// queue a sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, 0, 255, 0, 0);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
- // queue a few buffers without setting next transaction
+ // queue a few buffers without setting sync transaction
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
queueBuffer(igbProducer, 0, 0, 255, 0);
@@ -1003,13 +1007,13 @@
mainTransaction.apply();
// queue another sync transaction
- adapter.setNextTransaction(&next);
+ adapter.setSyncTransaction(&sync);
queueBuffer(igbProducer, r, g, b, 0);
// Expect 2 buffers to be released because the non sync transactions should merge
// with the sync
mProducerListener->waitOnNumberReleased(3);
- mainTransaction.merge(std::move(next));
+ mainTransaction.merge(std::move(sync));
CallbackHelper transactionCallback;
mainTransaction
@@ -1026,6 +1030,45 @@
checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
}
+TEST_F(BLASTBufferQueueTest, SetSyncTransactionAcquireMultipleBuffers) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ Transaction next;
+ adapter.setSyncTransaction(&next, false);
+ queueBuffer(igbProducer, 0, 255, 0, 0);
+ queueBuffer(igbProducer, 0, 0, 255, 0);
+ // There should only be one frame submitted since the first frame will be released.
+ adapter.validateNumFramesSubmitted(1);
+ adapter.setSyncTransaction(nullptr);
+
+ // queue non sync buffer, so this one should get blocked
+ // Add a present delay to allow the first screenshot to get taken.
+ nsecs_t presentTimeDelay = std::chrono::nanoseconds(500ms).count();
+ queueBuffer(igbProducer, 255, 0, 0, presentTimeDelay);
+
+ CallbackHelper transactionCallback;
+ next.addTransactionCompletedCallback(transactionCallback.function,
+ transactionCallback.getContext())
+ .apply();
+
+ CallbackData callbackData;
+ transactionCallback.getCallbackData(&callbackData);
+
+ // capture screen and verify that it is blue
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+
+ mProducerListener->waitOnNumberReleased(2);
+ // capture screen and verify that it is red
+ ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight}));
+}
+
class TestProducerListener : public BnProducerListener {
public:
sp<IGraphicBufferProducer> mIgbp;
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 a9f4d09..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;
@@ -885,10 +887,6 @@
return NO_ERROR;
}
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* /*outToken*/) override {
- return NO_ERROR;
- }
-
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& /*surface*/,
const FrameTimelineInfo& /*frameTimelineInfo*/) override {
return NO_ERROR;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index d018800..f0b97a7 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,17 +61,17 @@
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 shouldDisregardTransformation(uint32_t source) {
+ // Do not apply any transformations to axes from joysticks or touchpads.
+ return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) ||
+ isFromSource(source, AINPUT_SOURCE_CLASS_POSITION);
}
-bool shouldDisregardTranslation(uint32_t source) {
+bool shouldDisregardOffset(uint32_t source) {
// Pointer events are the only type of events that refer to absolute coordinates on the display,
// so we should apply the entire window transform. For other types of events, we should make
// sure to not apply the window translation/offset.
- return (source & AINPUT_SOURCE_CLASS_POINTER) == 0;
+ return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER);
}
} // namespace
@@ -114,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: {
@@ -138,12 +145,55 @@
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()},
event.getAction(),
- event.getDownTime(),
event.getFlags() & VERIFIED_KEY_EVENT_FLAGS,
+ event.getDownTime(),
event.getKeyCode(),
event.getScanCode(),
event.getMetaState(),
@@ -156,8 +206,8 @@
event.getRawX(0),
event.getRawY(0),
event.getActionMasked(),
- event.getDownTime(),
event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS,
+ event.getDownTime(),
event.getMetaState(),
event.getButtonState()};
}
@@ -457,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;
@@ -707,7 +775,7 @@
#endif
bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) {
- if (source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) {
// Specifically excludes HOVER_MOVE and SCROLL.
switch (action & AMOTION_EVENT_ACTION_MASK) {
case AMOTION_EVENT_ACTION_DOWN:
@@ -764,17 +832,31 @@
return android::base::StringPrintf("%" PRId32, action);
}
+// Apply the given transformation to the point without checking whether the entire transform
+// should be disregarded altogether for the provided source.
+static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform,
+ const vec2& xy) {
+ return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy)
+ : transform.transform(xy);
+}
+
vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform,
const vec2& xy) {
- return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy)
- : transform.transform(xy);
+ if (shouldDisregardTransformation(source)) {
+ return xy;
+ }
+ return calculateTransformedXYUnchecked(source, transform, xy);
}
float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source,
const ui::Transform& transform,
const PointerCoords& coords) {
+ if (shouldDisregardTransformation(source)) {
+ return coords.getAxisValue(axis);
+ }
+
if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) {
- const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue());
+ const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue());
static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1);
return xy[axis];
}
@@ -796,17 +878,15 @@
// --- FocusEvent ---
-void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) {
+void FocusEvent::initialize(int32_t id, bool hasFocus) {
InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
ADISPLAY_ID_NONE, INVALID_HMAC);
mHasFocus = hasFocus;
- mInTouchMode = inTouchMode;
}
void FocusEvent::initialize(const FocusEvent& from) {
InputEvent::initialize(from);
mHasFocus = from.mHasFocus;
- mInTouchMode = from.mInTouchMode;
}
// --- CaptureEvent ---
diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp
index 69ae9a0..ac84627 100644
--- a/libs/input/InputDevice.cpp
+++ b/libs/input/InputDevice.cpp
@@ -89,8 +89,15 @@
// Treblized input device config files will be located /product/usr, /system_ext/usr,
// /odm/usr or /vendor/usr.
- const char* rootsForPartition[]{"/product", "/system_ext", "/odm", "/vendor",
- getenv("ANDROID_ROOT")};
+ // These files may also be in the com.android.input.config APEX.
+ const char* rootsForPartition[]{
+ "/product",
+ "/system_ext",
+ "/odm",
+ "/vendor",
+ "/apex/com.android.input.config/etc",
+ getenv("ANDROID_ROOT"),
+ };
for (size_t i = 0; i < size(rootsForPartition); i++) {
if (rootsForPartition[i] == nullptr) {
continue;
@@ -201,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/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 02a5a08..a065ce2 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -278,7 +278,6 @@
case InputMessage::Type::FOCUS: {
msg->body.focus.eventId = body.focus.eventId;
msg->body.focus.hasFocus = body.focus.hasFocus;
- msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
case InputMessage::Type::CAPTURE: {
@@ -622,13 +621,10 @@
return mChannel->sendMessage(&msg);
}
-status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus,
- bool inTouchMode) {
+status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) {
if (ATRACE_ENABLED()) {
- std::string message =
- StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
- mChannel->getName().c_str(), toString(hasFocus),
- toString(inTouchMode));
+ std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)",
+ mChannel->getName().c_str(), toString(hasFocus));
ATRACE_NAME(message.c_str());
}
@@ -637,7 +633,6 @@
msg.header.seq = seq;
msg.body.focus.eventId = eventId;
msg.body.focus.hasFocus = hasFocus;
- msg.body.focus.inTouchMode = inTouchMode;
return mChannel->sendMessage(&msg);
}
@@ -1371,8 +1366,7 @@
}
void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
- event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus,
- msg->body.focus.inTouchMode);
+ event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus);
}
void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) {
@@ -1491,9 +1485,8 @@
break;
}
case InputMessage::Type::FOCUS: {
- out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s",
- toString(msg.body.focus.hasFocus),
- toString(msg.body.focus.inTouchMode));
+ out += android::base::StringPrintf("hasFocus=%s",
+ toString(msg.body.focus.hasFocus));
break;
}
case InputMessage::Type::CAPTURE: {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index a44f0b7..a6465ee 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -18,10 +18,10 @@
//#define LOG_NDEBUG 0
// Log debug messages about velocity tracking.
-#define DEBUG_VELOCITY 0
+static constexpr bool DEBUG_VELOCITY = false;
// Log debug messages about the progress of the algorithm itself.
-#define DEBUG_STRATEGY 0
+static constexpr bool DEBUG_STRATEGY = false;
#include <array>
#include <inttypes.h>
@@ -30,7 +30,6 @@
#include <optional>
#include <android-base/stringprintf.h>
-#include <cutils/properties.h>
#include <input/VelocityTracker.h>
#include <utils/BitSet.h>
#include <utils/Timers.h>
@@ -64,7 +63,6 @@
return sqrtf(r);
}
-#if DEBUG_STRATEGY || DEBUG_VELOCITY
static std::string vectorToString(const float* a, uint32_t m) {
std::string str;
str += "[";
@@ -77,9 +75,11 @@
str += " ]";
return str;
}
-#endif
-#if DEBUG_STRATEGY
+static std::string vectorToString(const std::vector<float>& v) {
+ return vectorToString(v.data(), v.size());
+}
+
static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) {
std::string str;
str = "[";
@@ -99,7 +99,6 @@
str += " ]";
return str;
}
-#endif
// --- VelocityTracker ---
@@ -133,12 +132,18 @@
VelocityTracker::Strategy strategy) {
switch (strategy) {
case VelocityTracker::Strategy::IMPULSE:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing impulse strategy");
+ }
return std::make_unique<ImpulseVelocityTrackerStrategy>();
case VelocityTracker::Strategy::LSQ1:
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(1);
case VelocityTracker::Strategy::LSQ2:
+ if (DEBUG_STRATEGY) {
+ ALOGI("Initializing lsq2 strategy");
+ }
return std::make_unique<LeastSquaresVelocityTrackerStrategy>(2);
case VelocityTracker::Strategy::LSQ3:
@@ -204,10 +209,10 @@
if ((mCurrentPointerIdBits.value & idBits.value)
&& eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) {
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
- (eventTime - mLastEventTime) * 0.000001f);
-#endif
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.",
+ (eventTime - mLastEventTime) * 0.000001f);
+ }
// We have not received any movements for too long. Assume that all pointers
// have stopped.
mStrategy->clear();
@@ -221,24 +226,24 @@
mStrategy->addMovement(eventTime, idBits, positions);
-#if DEBUG_VELOCITY
- ALOGD("VelocityTracker: addMovement eventTime=%" PRId64 ", idBits=0x%08x, activePointerId=%d",
- eventTime, idBits.value, mActivePointerId);
- for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) {
- uint32_t id = iterBits.firstMarkedBit();
- uint32_t index = idBits.getIndexOfBit(id);
- iterBits.clearBit(id);
- Estimator estimator;
- getEstimator(id, &estimator);
- ALOGD(" %d: position (%0.3f, %0.3f), "
- "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
- id, positions[index].x, positions[index].y,
- int(estimator.degree),
- vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
- vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
- estimator.confidence);
+ if (DEBUG_VELOCITY) {
+ ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
+ ", idBits=0x%08x, activePointerId=%d",
+ eventTime, idBits.value, mActivePointerId);
+ for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+ uint32_t id = iterBits.firstMarkedBit();
+ uint32_t index = idBits.getIndexOfBit(id);
+ iterBits.clearBit(id);
+ Estimator estimator;
+ getEstimator(id, &estimator);
+ ALOGD(" %d: position (%0.3f, %0.3f), "
+ "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+ id, positions[index].x, positions[index].y, int(estimator.degree),
+ vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+ vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+ estimator.confidence);
+ }
}
-#endif
}
void VelocityTracker::addMovement(const MotionEvent* event) {
@@ -419,11 +424,10 @@
static bool solveLeastSquares(const std::vector<float>& x, const std::vector<float>& y,
const std::vector<float>& w, uint32_t n, float* outB, float* outDet) {
const size_t m = x.size();
-#if DEBUG_STRATEGY
- ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
- vectorToString(x, m).c_str(), vectorToString(y, m).c_str(),
- vectorToString(w, m).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n),
+ vectorToString(x).c_str(), vectorToString(y).c_str(), vectorToString(w).c_str());
+ }
LOG_ALWAYS_FATAL_IF(m != y.size() || m != w.size(), "Mismatched vector sizes");
// Expand the X vector to a matrix A, pre-multiplied by the weights.
@@ -434,9 +438,9 @@
a[i][h] = a[i - 1][h] * x[h];
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).c_str());
+ }
// Apply the Gram-Schmidt process to A to obtain its QR decomposition.
float q[n][m]; // orthonormal basis, column-major order
@@ -455,9 +459,9 @@
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
-#if DEBUG_STRATEGY
- ALOGD(" - no solution, norm=%f", norm);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - no solution, norm=%f", norm);
+ }
return false;
}
@@ -469,22 +473,22 @@
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
}
}
-#if DEBUG_STRATEGY
- ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
- ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).c_str());
+ ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).c_str());
- // calculate QR, if we factored A correctly then QR should equal A
- float qr[n][m];
- for (uint32_t h = 0; h < m; h++) {
- for (uint32_t i = 0; i < n; i++) {
- qr[i][h] = 0;
- for (uint32_t j = 0; j < n; j++) {
- qr[i][h] += q[j][h] * r[j][i];
+ // calculate QR, if we factored A correctly then QR should equal A
+ float qr[n][m];
+ for (uint32_t h = 0; h < m; h++) {
+ for (uint32_t i = 0; i < n; i++) {
+ qr[i][h] = 0;
+ for (uint32_t j = 0; j < n; j++) {
+ qr[i][h] += q[j][h] * r[j][i];
+ }
}
}
+ ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
}
- ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).c_str());
-#endif
// Solve R B = Qt W Y to find B. This is easy because R is upper triangular.
// We just work from bottom-right to top-left calculating B's coefficients.
@@ -500,9 +504,9 @@
}
outB[i] /= r[i][i];
}
-#if DEBUG_STRATEGY
- ALOGD(" - b=%s", vectorToString(outB, n).c_str());
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - b=%s", vectorToString(outB, n).c_str());
+ }
// Calculate the coefficient of determination as 1 - (SSerr / SStot) where
// SSerr is the residual sum of squares (variance of the error),
@@ -528,11 +532,11 @@
sstot += w[h] * w[h] * var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_STRATEGY
- ALOGD(" - sserr=%f", sserr);
- ALOGD(" - sstot=%f", sstot);
- ALOGD(" - det=%f", *outDet);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD(" - sserr=%f", sserr);
+ ALOGD(" - sstot=%f", sstot);
+ ALOGD(" - det=%f", *outDet);
+ }
return true;
}
@@ -655,13 +659,11 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
-#if DEBUG_STRATEGY
- ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
- int(outEstimator->degree),
- vectorToString(outEstimator->xCoeff, n).c_str(),
- vectorToString(outEstimator->yCoeff, n).c_str(),
- outEstimator->confidence);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+ int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+ vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
+ }
return true;
}
}
@@ -1169,9 +1171,9 @@
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = 2; // similar results to 2nd degree fit
outEstimator->confidence = 1;
-#if DEBUG_STRATEGY
- ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
-#endif
+ if (DEBUG_STRATEGY) {
+ ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]);
+ }
return true;
}
diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp
index 1b594f1..a92016b 100644
--- a/libs/input/tests/InputEvent_test.cpp
+++ b/libs/input/tests/InputEvent_test.cpp
@@ -647,9 +647,8 @@
ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001);
}
-MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
- const ui::Transform& transform,
- const ui::Transform& rawTransform) {
+MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy,
+ const ui::Transform& transform, const ui::Transform& rawTransform) {
std::vector<PointerProperties> pointerProperties;
pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER});
std::vector<PointerCoords> pointerCoords;
@@ -660,8 +659,8 @@
pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy);
nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
MotionEvent event;
- event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN,
- /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN,
+ event.initialize(InputEvent::nextId(), /* deviceId */ 1, source,
+ /* displayId */ 0, INVALID_HMAC, action,
/* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE,
/* buttonState */ 0, MotionClassification::NONE, transform,
/* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
@@ -670,6 +669,13 @@
return event;
}
+MotionEvent createTouchDownEvent(float x, float y, float dx, float dy,
+ const ui::Transform& transform,
+ const ui::Transform& rawTransform) {
+ return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy,
+ transform, rawTransform);
+}
+
TEST_F(MotionEventTest, ApplyTransform) {
// Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
ui::Transform identity;
@@ -708,16 +714,39 @@
changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001);
}
+TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) {
+ constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_JOYSTICK,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonTransformedSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 0, 0, transform, transform);
+
+ // These events should not be transformed in any way.
+ ASSERT_EQ(60, event.getX(0));
+ ASSERT_EQ(100, event.getY(0));
+ ASSERT_EQ(event.getRawX(0), event.getX(0));
+ ASSERT_EQ(event.getRawY(0), event.getY(0));
+ }
+}
+
TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) {
- constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL,
- AINPUT_SOURCE_MOUSE_RELATIVE,
- AINPUT_SOURCE_JOYSTICK};
- for (uint32_t source : NON_POINTER_SOURCES) {
- // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
- ui::Transform transform(ui::Transform::ROT_90, 800, 400);
- transform.set(transform.tx() + 20, transform.ty() + 40);
- MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform);
- event.setSource(source);
+ constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL,
+ AMOTION_EVENT_ACTION_DOWN),
+ std::pair(AINPUT_SOURCE_MOUSE_RELATIVE,
+ AMOTION_EVENT_ACTION_MOVE)};
+ // Create a rotate-90 transform with an offset (like a window which isn't fullscreen).
+ ui::Transform transform(ui::Transform::ROT_90, 800, 400);
+ transform.set(transform.tx() + 20, transform.ty() + 40);
+
+ for (const auto& [source, action] : kNonPointerSources) {
+ const MotionEvent event =
+ createMotionEvent(source, action, 60, 100, 42, 96, transform, transform);
// Since this event comes from a non-pointer source, it should include rotation but not
// translation/offset.
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index 973194c..05bc0bc 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -290,10 +290,9 @@
constexpr uint32_t seq = 15;
int32_t eventId = InputEvent::nextId();
constexpr bool hasFocus = true;
- constexpr bool inTouchMode = true;
const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC);
- status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode);
+ status = mPublisher->publishFocusEvent(seq, eventId, hasFocus);
ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK";
uint32_t consumeSeq;
@@ -309,7 +308,6 @@
EXPECT_EQ(seq, consumeSeq);
EXPECT_EQ(eventId, focusEvent->getId());
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
- EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
status = mConsumer->sendFinishedSignal(seq, true);
ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 2f88704..b6a9476 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -84,8 +84,7 @@
CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
- CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5);
- CHECK_OFFSET(InputMessage::Body::Focus, empty, 6);
+ CHECK_OFFSET(InputMessage::Body::Focus, empty, 5);
CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0);
CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4);
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 3039362..a87b187 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -343,7 +343,7 @@
{ 235089162955851ns, {{560.66, 843.82}} }, // ACTION_UP
};
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_X,
- 872.794617);
+ 764.345703);
computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X,
951.698181);
computeAndCheckVelocity(VelocityTracker::Strategy::IMPULSE, motions, AMOTION_EVENT_AXIS_Y,
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/EGLConsumer.cpp b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
index 2f31888..6882ea3 100644
--- a/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
+++ b/libs/nativedisplay/surfacetexture/EGLConsumer.cpp
@@ -191,7 +191,7 @@
// continues to use it.
sp<GraphicBuffer> buffer =
new GraphicBuffer(kDebugData.width, kDebugData.height, PIXEL_FORMAT_RGBA_8888,
- GraphicBuffer::USAGE_SW_WRITE_RARELY,
+ DEFAULT_USAGE_FLAGS | GraphicBuffer::USAGE_SW_WRITE_RARELY,
"[EGLConsumer debug texture]");
uint32_t* bits;
buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
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..cb3361b 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -30,7 +30,7 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.1/types.h>
-
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints
@@ -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);
@@ -588,8 +588,12 @@
"HAL and AHardwareBuffer pixel format don't match");
static_assert(HAL_PIXEL_FORMAT_YCBCR_422_I == AHARDWAREBUFFER_FORMAT_YCbCr_422_I,
"HAL and AHardwareBuffer pixel format don't match");
+ static_assert(static_cast<int>(aidl::android::hardware::graphics::common::PixelFormat::R_8) ==
+ AHARDWAREBUFFER_FORMAT_R8_UNORM,
+ "HAL and AHardwareBuffer pixel format don't match");
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
@@ -641,6 +645,8 @@
uint32_t AHardwareBuffer_bytesPerPixel(uint32_t format) {
switch (format) {
+ case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+ return 1;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
return 2;
diff --git a/libs/nativewindow/ANativeWindow.cpp b/libs/nativewindow/ANativeWindow.cpp
index 93e7239..18a4b2d 100644
--- a/libs/nativewindow/ANativeWindow.cpp
+++ b/libs/nativewindow/ANativeWindow.cpp
@@ -162,6 +162,8 @@
static_assert(static_cast<int>(ADATASPACE_SCRGB) == static_cast<int>(HAL_DATASPACE_V0_SCRGB));
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) == static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_PQ) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_PQ));
static_assert(static_cast<int>(ADATASPACE_ADOBE_RGB) == static_cast<int>(HAL_DATASPACE_ADOBE_RGB));
static_assert(static_cast<int>(ADATASPACE_JFIF) == static_cast<int>(HAL_DATASPACE_V0_JFIF));
static_assert(static_cast<int>(ADATASPACE_BT601_625) == static_cast<int>(HAL_DATASPACE_V0_BT601_625));
@@ -170,6 +172,12 @@
static_assert(static_cast<int>(ADATASPACE_BT709) == static_cast<int>(HAL_DATASPACE_V0_BT709));
static_assert(static_cast<int>(ADATASPACE_DCI_P3) == static_cast<int>(HAL_DATASPACE_DCI_P3));
static_assert(static_cast<int>(ADATASPACE_SRGB_LINEAR) == static_cast<int>(HAL_DATASPACE_V0_SRGB_LINEAR));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_HLG));
+ static_assert(static_cast<int>(ADATASPACE_BT2020_ITU_HLG) ==
+ static_cast<int>(HAL_DATASPACE_BT2020_ITU_HLG));
+ static_assert(static_cast<int>(DEPTH) == static_cast<int>(HAL_DATASPACE_DEPTH));
+ static_assert(static_cast<int>(DYNAMIC_DEPTH) == static_cast<int>(HAL_DATASPACE_DYNAMIC_DEPTH));
if (!window || !query(window, NATIVE_WINDOW_IS_VALID) ||
!isDataSpaceValid(window, dataSpace)) {
diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h
index a299488..771844f 100644
--- a/libs/nativewindow/include/android/data_space.h
+++ b/libs/nativewindow/include/android/data_space.h
@@ -15,6 +15,13 @@
*/
/**
+ * @defgroup ADataSpace Data Space
+ *
+ * ADataSpace describes how to interpret colors.
+ * @{
+ */
+
+/**
* @file data_space.h
*/
@@ -437,6 +444,15 @@
ADATASPACE_BT2020_PQ = 163971072, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_FULL
/**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television
+ *
+ * Use limited range, SMPTE 2084 (PQ) transfer and BT2020 standard
+ */
+ ADATASPACE_BT2020_ITU_PQ = 298188800, // STANDARD_BT2020 | TRANSFER_ST2084 | RANGE_LIMITED
+
+ /**
* Adobe RGB
*
* Use full range, gamma 2.2 transfer and Adobe RGB primaries
@@ -512,8 +528,38 @@
* components.
*/
ADATASPACE_SRGB_LINEAR = 138477568, // STANDARD_BT709 | TRANSFER_LINEAR | RANGE_FULL
+
+ /**
+ * Hybrid Log Gamma encoding:
+ *
+ * Use full range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_HLG = 168165376, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_FULL
+
+ /**
+ * ITU Hybrid Log Gamma encoding:
+ *
+ * Use limited range, hybrid log gamma transfer and BT2020 standard.
+ */
+ ADATASPACE_BT2020_ITU_HLG = 302383104, // STANDARD_BT2020 | TRANSFER_HLG | RANGE_LIMITED
+
+ /**
+ * Depth:
+ *
+ * This value is valid with formats HAL_PIXEL_FORMAT_Y16 and HAL_PIXEL_FORMAT_BLOB.
+ */
+ DEPTH = 4096,
+
+ /**
+ * ISO 16684-1:2011(E) Dynamic Depth:
+ *
+ * Embedded depth metadata following the dynamic depth specification.
+ */
+ DYNAMIC_DEPTH = 4098
};
__END_DECLS
#endif // ANDROID_DATA_SPACE_H
+
+/** @} */
diff --git a/libs/nativewindow/include/android/hardware_buffer.h b/libs/nativewindow/include/android/hardware_buffer.h
index d5e7cb2..6f1f04d 100644
--- a/libs/nativewindow/include/android/hardware_buffer.h
+++ b/libs/nativewindow/include/android/hardware_buffer.h
@@ -158,6 +158,13 @@
* cube-maps or multi-layered textures.
*/
AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420 = 0x23,
+
+ /**
+ * Corresponding formats:
+ * Vulkan: VK_FORMAT_R8_UNORM
+ * OpenGL ES: GR_GL_R8
+ */
+ AHARDWAREBUFFER_FORMAT_R8_UNORM = 0x38,
};
/**
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index 0bc2b5d..a319769 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1025,10 +1025,11 @@
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
- int64_t frameTimelineVsyncId,
- int32_t inputEventId) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO,
- frameTimelineVsyncId, inputEventId);
+ int64_t frameTimelineVsyncId,
+ int32_t inputEventId,
+ int64_t startTimeNanos) {
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
+ inputEventId, startTimeNanos);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 570c7bc..07c5dd8 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -40,6 +40,11 @@
"libui",
"libutils",
],
+
+ static_libs: [
+ "libshaders",
+ "libtonemap",
+ ],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
}
@@ -94,8 +99,10 @@
"skia/debug/SkiaCapture.cpp",
"skia/debug/SkiaMemoryReporter.cpp",
"skia/filters/BlurFilter.cpp",
+ "skia/filters/GaussianBlurFilter.cpp",
+ "skia/filters/KawaseBlurFilter.cpp",
"skia/filters/LinearEffect.cpp",
- "skia/filters/StretchShaderFactory.cpp"
+ "skia/filters/StretchShaderFactory.cpp",
],
}
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
index a9ea690..c7ad058 100644
--- a/libs/renderengine/RenderEngine.cpp
+++ b/libs/renderengine/RenderEngine.cpp
@@ -27,54 +27,22 @@
namespace renderengine {
std::unique_ptr<RenderEngine> RenderEngine::create(const RenderEngineCreationArgs& args) {
- RenderEngineType renderEngineType = args.renderEngineType;
-
- // Keep the ability to override by PROPERTIES:
- char prop[PROPERTY_VALUE_MAX];
- property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "");
- if (strcmp(prop, "gles") == 0) {
- renderEngineType = RenderEngineType::GLES;
- }
- if (strcmp(prop, "threaded") == 0) {
- renderEngineType = RenderEngineType::THREADED;
- }
- if (strcmp(prop, "skiagl") == 0) {
- renderEngineType = RenderEngineType::SKIA_GL;
- }
- if (strcmp(prop, "skiaglthreaded") == 0) {
- renderEngineType = RenderEngineType::SKIA_GL_THREADED;
- }
-
- switch (renderEngineType) {
+ switch (args.renderEngineType) {
case RenderEngineType::THREADED:
ALOGD("Threaded RenderEngine with GLES Backend");
return renderengine::threaded::RenderEngineThreaded::create(
[args]() { return android::renderengine::gl::GLESRenderEngine::create(args); },
- renderEngineType);
+ args.renderEngineType);
case RenderEngineType::SKIA_GL:
ALOGD("RenderEngine with SkiaGL Backend");
return renderengine::skia::SkiaGLRenderEngine::create(args);
case RenderEngineType::SKIA_GL_THREADED: {
- // These need to be recreated, since they are a constant reference, and we need to
- // let SkiaRE know that it's running as threaded, and all GL operation will happen on
- // the same thread.
- RenderEngineCreationArgs skiaArgs =
- RenderEngineCreationArgs::Builder()
- .setPixelFormat(args.pixelFormat)
- .setImageCacheSize(args.imageCacheSize)
- .setUseColorManagerment(args.useColorManagement)
- .setEnableProtectedContext(args.enableProtectedContext)
- .setPrecacheToneMapperShaderOnly(args.precacheToneMapperShaderOnly)
- .setSupportsBackgroundBlur(args.supportsBackgroundBlur)
- .setContextPriority(args.contextPriority)
- .setRenderEngineType(renderEngineType)
- .build();
ALOGD("Threaded RenderEngine with SkiaGL Backend");
return renderengine::threaded::RenderEngineThreaded::create(
- [skiaArgs]() {
- return android::renderengine::skia::SkiaGLRenderEngine::create(skiaArgs);
+ [args]() {
+ return android::renderengine::skia::SkiaGLRenderEngine::create(args);
},
- renderEngineType);
+ args.renderEngineType);
}
case RenderEngineType::GLES:
default:
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
new file mode 100644
index 0000000..471159f
--- /dev/null
+++ b/libs/renderengine/benchmark/Android.bp
@@ -0,0 +1,59 @@
+// 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_benchmark {
+ name: "librenderengine_bench",
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
+ srcs: [
+ "main.cpp",
+ "Codec.cpp",
+ "Flags.cpp",
+ "RenderEngineBench.cpp",
+ ],
+ static_libs: [
+ "librenderengine",
+ "libshaders",
+ "libtonemap",
+ ],
+ cflags: [
+ "-DLOG_TAG=\"RenderEngineBench\"",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libjnigraphics",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libprocessgroup",
+ "libsync",
+ "libui",
+ "libutils",
+ ],
+
+ data: ["resources/*"],
+}
diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp
new file mode 100644
index 0000000..80e4fc4
--- /dev/null
+++ b/libs/renderengine/benchmark/Codec.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 <RenderEngineBench.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+using namespace android;
+using namespace android::renderengine;
+
+namespace {
+struct DecoderDeleter {
+ void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); }
+};
+
+using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+bool ok(int aImageDecoderResult, const char* path, const char* method) {
+ if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) {
+ return true;
+ }
+
+ ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path,
+ AImageDecoder_resultToString(aImageDecoderResult));
+ return false;
+}
+} // namespace
+
+namespace renderenginebench {
+
+void decode(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_RDONLY)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ AImageDecoder* decoder{nullptr};
+ auto result = AImageDecoder_createFromFd(fd.get(), &decoder);
+ if (!ok(result, path, "createFromFd")) {
+ return;
+ }
+
+ AutoDecoderDeleter deleter(decoder);
+
+ LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0,
+ "Impossible buffer size!");
+ auto width = static_cast<int32_t>(buffer->getWidth());
+ auto height = static_cast<int32_t>(buffer->getHeight());
+ result = AImageDecoder_setTargetSize(decoder, width, height);
+ if (!ok(result, path, "setTargetSize")) {
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride),
+ static_cast<size_t>(stride * height));
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+
+ // For the side effect of logging.
+ (void)ok(result, path, "decodeImage");
+}
+
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) {
+ base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)};
+ if (fd.get() < 0) {
+ ALOGE("Failed to open %s", path);
+ return;
+ }
+
+ void* pixels{nullptr};
+ int32_t stride{0};
+ if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+ nullptr /*outBytesPerPixel*/, &stride);
+ status < 0) {
+ ALOGE("Failed to lock pixels!");
+ return;
+ }
+
+ AndroidBitmapInfo info{
+ .width = buffer->getWidth(),
+ .height = buffer->getHeight(),
+ .stride = static_cast<uint32_t>(stride),
+ .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+ .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+ };
+ int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+ ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd,
+ [](void* fdPtr, const void* data, size_t size) -> bool {
+ const ssize_t bytesWritten =
+ write(reinterpret_cast<base::unique_fd*>(fdPtr)
+ ->get(),
+ data, size);
+ return bytesWritten > 0 &&
+ static_cast<size_t>(bytesWritten) == size;
+ });
+ if (result == ANDROID_BITMAP_RESULT_SUCCESS) {
+ ALOGD("Successfully encoded to '%s'", path);
+ } else {
+ ALOGE("Failed to encode to %s with error %d", path, result);
+ }
+
+ if (auto status = buffer->unlock(); status < 0) {
+ ALOGE("Failed to unlock pixels!");
+ }
+}
+
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp
new file mode 100644
index 0000000..c5d5156
--- /dev/null
+++ b/libs/renderengine/benchmark/Flags.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <log/log.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace {
+bool gSave = false;
+}
+
+namespace renderenginebench {
+
+void parseFlagsForHelp(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--help")) {
+ printf("RenderEngineBench-specific flags:\n");
+ printf("[--save]: Save the output to the device to confirm drawing result.\n");
+ break;
+ }
+ }
+}
+
+void parseFlags(int argc, char** argv) {
+ for (int i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], "--save")) {
+ gSave = true;
+ }
+ }
+}
+
+bool save() {
+ return gSave;
+}
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
new file mode 100644
index 0000000..6c8f8e8
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -0,0 +1,259 @@
+/*
+ * 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 <RenderEngineBench.h>
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
+
+#include <mutex>
+
+using namespace android;
+using namespace android::renderengine;
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for Benchmark::Apply
+///////////////////////////////////////////////////////////////////////////////
+
+std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
+ switch (type) {
+ case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
+ return "skiaglthreaded";
+ case RenderEngine::RenderEngineType::SKIA_GL:
+ return "skiagl";
+ case RenderEngine::RenderEngineType::GLES:
+ case RenderEngine::RenderEngineType::THREADED:
+ LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
+ return "unused";
+ }
+}
+
+/**
+ * Passed (indirectly - see RunSkiaGLThreaded) to Benchmark::Apply to create a
+ * Benchmark which specifies which RenderEngineType it uses.
+ *
+ * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
+ * it obvious which version is being run.
+ *
+ * @param b The benchmark family
+ * @param type The type of RenderEngine to use.
+ */
+static void AddRenderEngineType(benchmark::internal::Benchmark* b,
+ RenderEngine::RenderEngineType type) {
+ b->Arg(static_cast<int64_t>(type));
+ b->ArgName(RenderEngineTypeName(type));
+}
+
+/**
+ * Run a benchmark once using SKIA_GL_THREADED.
+ */
+static void RunSkiaGLThreaded(benchmark::internal::Benchmark* b) {
+ AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL_THREADED);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers for calling drawLayers
+///////////////////////////////////////////////////////////////////////////////
+
+std::pair<uint32_t, uint32_t> getDisplaySize() {
+ // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
+ // to GraphicBuffer, which wants uint32_t.
+ static uint32_t width, height;
+ std::once_flag once;
+ std::call_once(once, []() {
+ auto surfaceComposerClient = SurfaceComposerClient::getDefault();
+ auto displayToken = surfaceComposerClient->getInternalDisplayToken();
+ ui::DisplayMode displayMode;
+ if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+ LOG_ALWAYS_FATAL("Failed to get active display mode!");
+ }
+ auto w = displayMode.resolution.width;
+ auto h = displayMode.resolution.height;
+ LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
+ width = static_cast<uint32_t>(w);
+ height = static_cast<uint32_t>(h);
+ });
+ return std::pair<uint32_t, uint32_t>(width, height);
+}
+
+// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove
+// GLESRenderEngine we can remove this, too.
+static constexpr const bool kUseFrameBufferCache = false;
+
+static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
+ auto args = RenderEngineCreationArgs::Builder()
+ .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+ .setImageCacheSize(1)
+ .setEnableProtectedContext(true)
+ .setPrecacheToneMapperShaderOnly(false)
+ .setSupportsBackgroundBlur(true)
+ .setContextPriority(RenderEngine::ContextPriority::REALTIME)
+ .setRenderEngineType(type)
+ .setUseColorManagerment(true)
+ .build();
+ return RenderEngine::create(args);
+}
+
+static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re, uint32_t width,
+ uint32_t height,
+ uint64_t extraUsageFlags = 0,
+ std::string name = "output") {
+ return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height,
+ HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_HW_RENDER |
+ GRALLOC_USAGE_HW_TEXTURE |
+ extraUsageFlags,
+ std::move(name)),
+ re,
+ ExternalTexture::Usage::READABLE |
+ ExternalTexture::Usage::WRITEABLE);
+}
+
+static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
+ std::shared_ptr<ExternalTexture> original,
+ uint64_t extraUsageFlags, std::string name) {
+ const uint32_t width = original->getBuffer()->getWidth();
+ const uint32_t height = original->getBuffer()->getHeight();
+ auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = original,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ auto layers = std::vector<LayerSettings>{layer};
+
+ auto [status, drawFence] =
+ re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ return texture;
+}
+
+/**
+ * Helper for timing calls to drawLayers.
+ *
+ * Caller needs to create RenderEngine and the LayerSettings, and this takes
+ * care of setting up the display, starting and stopping the timer, calling
+ * drawLayers, and saving (if --save is used).
+ *
+ * This times both the CPU and GPU work initiated by drawLayers. All work done
+ * outside of the for loop is excluded from the timing measurements.
+ */
+static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
+ benchmark::State& benchState, const char* saveFileName) {
+ auto [width, height] = getDisplaySize();
+ auto outputBuffer = allocateBuffer(re, width, height);
+
+ const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+ DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .maxLuminance = 500,
+ };
+
+ // This loop starts and stops the timer.
+ for (auto _ : benchState) {
+ auto [status, drawFence] = re.drawLayers(display, layers, outputBuffer,
+ kUseFrameBufferCache, base::unique_fd())
+ .get();
+ sp<Fence> waitFence = sp<Fence>::make(std::move(drawFence));
+ waitFence->waitForever(LOG_TAG);
+ }
+
+ if (renderenginebench::save() && saveFileName) {
+ // Copy to a CPU-accessible buffer so we can encode it.
+ outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
+
+ std::string outFile = base::GetExecutableDirectory();
+ outFile.append("/");
+ outFile.append(saveFileName);
+ outFile.append(".jpg");
+ renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Benchmarks
+///////////////////////////////////////////////////////////////////////////////
+
+void BM_blur(benchmark::State& benchState) {
+ auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+
+ // Initially use cpu access so we can decode into it with AImageDecoder.
+ auto [width, height] = getDisplaySize();
+ auto srcBuffer =
+ allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN, "decoded_source");
+ {
+ std::string srcImage = base::GetExecutableDirectory();
+ srcImage.append("/resources/homescreen.png");
+ renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+
+ // Now copy into GPU-only buffer for more realistic timing.
+ srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
+ }
+
+ const FloatRect layerRect(0, 0, width, height);
+ LayerSettings layer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .source =
+ PixelSource{
+ .buffer =
+ Buffer{
+ .buffer = srcBuffer,
+ },
+ },
+ .alpha = half(1.0f),
+ };
+ LayerSettings blurLayer{
+ .geometry =
+ Geometry{
+ .boundaries = layerRect,
+ },
+ .alpha = half(1.0f),
+ .skipContentDraw = true,
+ .backgroundBlurRadius = 60,
+ };
+
+ auto layers = std::vector<LayerSettings>{layer, blurLayer};
+ benchDrawLayers(*re, layers, benchState, "blurred");
+}
+
+BENCHMARK(BM_blur)->Apply(RunSkiaGLThreaded);
diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h
new file mode 100644
index 0000000..1a25d77
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.h
@@ -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.
+ */
+
+#include <ui/GraphicBuffer.h>
+
+using namespace android;
+
+/**
+ * Utilities for running benchmarks.
+ */
+namespace renderenginebench {
+/**
+ * Parse RenderEngineBench-specific flags from the command line.
+ *
+ * --save Save the output buffer to a file to verify that it drew as
+ * expected.
+ */
+void parseFlags(int argc, char** argv);
+
+/**
+ * Parse flags for '--help'
+ */
+void parseFlagsForHelp(int argc, char** argv);
+
+/**
+ * Whether to save the drawing result to a file.
+ *
+ * True if --save was used on the command line.
+ */
+bool save();
+
+/**
+ * Decode the image at 'path' into 'buffer'.
+ *
+ * Currently only used for debugging. The image will be scaled to fit the
+ * buffer if necessary.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void decode(const char* path, const sp<GraphicBuffer>& buffer);
+
+/**
+ * Encode the buffer to a jpeg.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer);
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp
new file mode 100644
index 0000000..7a62853
--- /dev/null
+++ b/libs/renderengine/benchmark/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 <RenderEngineBench.h>
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+ // Initialize will exit if it sees '--help', so check for it and print info
+ // about our flags first.
+ renderenginebench::parseFlagsForHelp(argc, argv);
+ benchmark::Initialize(&argc, argv);
+
+ // Calling this separately from parseFlagsForHelp prevents collisions with
+ // google-benchmark's flags, since Initialize will consume and remove flags
+ // it recognizes.
+ renderenginebench::parseFlags(argc, argv);
+ benchmark::RunSpecifiedBenchmarks();
+ return 0;
+}
diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png
new file mode 100644
index 0000000..997b72d
--- /dev/null
+++ b/libs/renderengine/benchmark/resources/homescreen.png
Binary files differ
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index d395d06..b4cab39 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -57,8 +57,10 @@
// orientation.
uint32_t orientation = ui::Transform::ROT_0;
- // SDR white point, -1f if unknown
- float sdrWhitePointNits = -1.f;
+ // Target luminance of the display. -1f if unknown.
+ // All layers will be dimmed by (max(layer white points) / targetLuminanceNits).
+ // If the target luminance is unknown, then no display-level dimming occurs.
+ float targetLuminanceNits = -1.f;
};
static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index 715f3f8..702e8b0 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -171,6 +171,12 @@
// Name associated with the layer for debugging purposes.
std::string name;
+
+ // Luminance of the white point for this layer. Used for linear dimming.
+ // Individual layers will be dimmed by (whitePointNits / maxWhitePoint).
+ // If white point nits are unknown, then this layer is assumed to have the
+ // same luminance as the brightest layer in the scene.
+ float whitePointNits = -1.f;
};
// Keep in sync with custom comparison function in
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 d5ec774..cc90946 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.cpp
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.cpp
@@ -46,6 +46,7 @@
#include <cmath>
#include <cstdint>
#include <memory>
+#include <numeric>
#include "../gl/GLExtensions.h"
#include "Cache.h"
@@ -53,6 +54,8 @@
#include "SkBlendMode.h"
#include "SkImageInfo.h"
#include "filters/BlurFilter.h"
+#include "filters/GaussianBlurFilter.h"
+#include "filters/KawaseBlurFilter.h"
#include "filters/LinearEffect.h"
#include "log/log_main.h"
#include "skia/debug/SkiaCapture.h"
@@ -328,7 +331,7 @@
if (args.supportsBackgroundBlur) {
ALOGD("Background Blurs Enabled");
- mBlurFilter = new BlurFilter();
+ mBlurFilter = new KawaseBlurFilter();
}
mCapture = std::make_unique<SkiaCapture>();
}
@@ -610,33 +613,33 @@
AutoBackendTexture::CleanupManager& mMgr;
};
-sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(sk_sp<SkShader> shader,
- const LayerSettings& layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect) {
- const auto stretchEffect = layer.stretchEffect;
+sk_sp<SkShader> SkiaGLRenderEngine::createRuntimeEffectShader(
+ const RuntimeEffectShaderParameters& parameters) {
// The given surface will be stretched by HWUI via matrix transformation
// which gets similar results for most surfaces
// Determine later on if we need to leverage the stertch shader within
// surface flinger
+ const auto& stretchEffect = parameters.layer.stretchEffect;
+ auto shader = parameters.shader;
if (stretchEffect.hasEffect()) {
- const auto targetBuffer = layer.source.buffer.buffer;
+ const auto targetBuffer = parameters.layer.source.buffer.buffer;
const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr;
- if (graphicBuffer && shader) {
+ if (graphicBuffer && parameters.shader) {
shader = mStretchShaderFactory.createSkShader(shader, stretchEffect);
}
}
- if (requiresLinearEffect) {
- const ui::Dataspace inputDataspace =
- mUseColorManagement ? layer.sourceDataspace : ui::Dataspace::V0_SRGB_LINEAR;
- const ui::Dataspace outputDataspace =
- mUseColorManagement ? display.outputDataspace : ui::Dataspace::V0_SRGB_LINEAR;
+ if (parameters.requiresLinearEffect) {
+ const ui::Dataspace inputDataspace = mUseColorManagement ? parameters.layer.sourceDataspace
+ : ui::Dataspace::V0_SRGB_LINEAR;
+ const ui::Dataspace outputDataspace = mUseColorManagement
+ ? parameters.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 = parameters.undoPremultipliedAlpha};
auto effectIter = mRuntimeEffects.find(effect);
sk_sp<SkRuntimeEffect> runtimeEffect = nullptr;
@@ -646,16 +649,16 @@
} else {
runtimeEffect = effectIter->second;
}
- float maxLuminance = layer.source.buffer.maxLuminanceNits;
- // If the buffer doesn't have a max luminance, treat it as SDR & use the display's SDR
- // white point
- if (maxLuminance <= 0.f) {
- maxLuminance = display.sdrWhitePointNits;
- }
- return createLinearEffectShader(shader, effect, runtimeEffect, layer.colorTransform,
- display.maxLuminance, maxLuminance);
+ mat4 colorTransform = parameters.layer.colorTransform;
+
+ colorTransform *=
+ mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio,
+ parameters.layerDimmingRatio, 1.f));
+ return createLinearEffectShader(parameters.shader, effect, runtimeEffect, colorTransform,
+ parameters.display.maxLuminance,
+ parameters.layer.source.buffer.maxLuminanceNits);
}
- return shader;
+ return parameters.shader;
}
void SkiaGLRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) {
@@ -728,6 +731,11 @@
return roundedRect;
}
+static bool equalsWithinMargin(float expected, float value, float margin) {
+ LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!");
+ return std::abs(expected - value) < margin;
+}
+
void SkiaGLRenderEngine::drawLayersInternal(
const std::shared_ptr<std::promise<RenderEngineResult>>&& resultPromise,
const DisplaySettings& display, const std::vector<LayerSettings>& layers,
@@ -789,6 +797,18 @@
const bool ctModifiesAlpha =
displayColorTransform && !displayColorTransform->isAlphaUnchanged();
+ // Find the max layer white point to determine the max luminance of the scene...
+ const float maxLayerWhitePoint = std::transform_reduce(
+ layers.cbegin(), layers.cend(), 0.f,
+ [](float left, float right) { return std::max(left, right); },
+ [&](const auto& l) { return l.whitePointNits; });
+
+ // ...and compute the dimming ratio if dimming is requested
+ const float displayDimmingRatio = display.targetLuminanceNits > 0.f &&
+ maxLayerWhitePoint > 0.f && display.targetLuminanceNits > maxLayerWhitePoint
+ ? maxLayerWhitePoint / display.targetLuminanceNits
+ : 1.f;
+
// Find if any layers have requested blur, we'll use that info to decide when to render to an
// offscreen buffer and when to render to the native buffer.
sk_sp<SkSurface> activeSurface(dstSurface);
@@ -803,11 +823,11 @@
continue;
}
if (layer.backgroundBlurRadius > 0 &&
- layer.backgroundBlurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
for (auto region : layer.blurRegions) {
- if (region.blurRadius < BlurFilter::kMaxCrossFadeRadius) {
+ if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) {
requiresCompositionLayer = true;
}
}
@@ -962,11 +982,14 @@
drawShadow(canvas, rrect, layer.shadow);
}
+ const float layerDimmingRatio = layer.whitePointNits <= 0.f
+ ? displayDimmingRatio
+ : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio;
+
const bool requiresLinearEffect = layer.colorTransform != mat4() ||
(mUseColorManagement &&
needsToneMapping(layer.sourceDataspace, display.outputDataspace)) ||
- (display.sdrWhitePointNits > 0.f &&
- display.sdrWhitePointNits != display.maxLuminance);
+ !equalsWithinMargin(1.f, layerDimmingRatio, 0.001f);
// quick abort from drawing the remaining portion of the layer
if (layer.skipContentDraw ||
@@ -1065,9 +1088,20 @@
toSkColorSpace(layerDataspace)));
}
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- !item.isOpaque && item.usePremultipliedAlpha,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = !item.isOpaque &&
+ item.usePremultipliedAlpha,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
+
+ // Turn on dithering when dimming beyond this threshold.
+ static constexpr float kDimmingThreshold = 0.2f;
+ if (layerDimmingRatio <= kDimmingThreshold) {
+ paint.setDither(true);
+ }
paint.setAlphaf(layer.alpha);
} else {
ATRACE_NAME("DrawColor");
@@ -1077,9 +1111,13 @@
.fB = color.b,
.fA = layer.alpha},
toSkColorSpace(layerDataspace));
- paint.setShader(createRuntimeEffectShader(shader, layer, display,
- /* undoPremultipliedAlpha */ false,
- requiresLinearEffect));
+ paint.setShader(createRuntimeEffectShader(
+ RuntimeEffectShaderParameters{.shader = shader,
+ .layer = layer,
+ .display = display,
+ .undoPremultipliedAlpha = false,
+ .requiresLinearEffect = requiresLinearEffect,
+ .layerDimmingRatio = layerDimmingRatio}));
}
if (layer.disableBlending) {
diff --git a/libs/renderengine/skia/SkiaGLRenderEngine.h b/libs/renderengine/skia/SkiaGLRenderEngine.h
index 74ce651..a650313 100644
--- a/libs/renderengine/skia/SkiaGLRenderEngine.h
+++ b/libs/renderengine/skia/SkiaGLRenderEngine.h
@@ -106,12 +106,18 @@
void initCanvas(SkCanvas* canvas, const DisplaySettings& display);
void drawShadow(SkCanvas* canvas, const SkRRect& casterRRect,
const ShadowSettings& shadowSettings);
+
// If requiresLinearEffect is true or the layer has a stretchEffect a new shader is returned.
// Otherwise it returns the input shader.
- sk_sp<SkShader> createRuntimeEffectShader(sk_sp<SkShader> shader, const LayerSettings& layer,
- const DisplaySettings& display,
- bool undoPremultipliedAlpha,
- bool requiresLinearEffect);
+ struct RuntimeEffectShaderParameters {
+ sk_sp<SkShader> shader;
+ const LayerSettings& layer;
+ const DisplaySettings& display;
+ bool undoPremultipliedAlpha;
+ bool requiresLinearEffect;
+ float layerDimmingRatio;
+ };
+ sk_sp<SkShader> createRuntimeEffectShader(const RuntimeEffectShaderParameters&);
EGLDisplay mEGLDisplay;
EGLContext mEGLContext;
@@ -133,7 +139,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/BlurFilter.cpp b/libs/renderengine/skia/filters/BlurFilter.cpp
index 2b6833e..6746e47 100644
--- a/libs/renderengine/skia/filters/BlurFilter.cpp
+++ b/libs/renderengine/skia/filters/BlurFilter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -15,7 +15,6 @@
*/
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
#include "BlurFilter.h"
#include <SkCanvas.h>
#include <SkData.h>
@@ -32,33 +31,7 @@
namespace renderengine {
namespace skia {
-BlurFilter::BlurFilter() {
- SkString blurString(R"(
- uniform shader child;
- uniform float2 in_blurOffset;
- uniform float2 in_maxSizeXY;
-
- half4 main(float2 xy) {
- half4 c = child.eval(xy);
- c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp( in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp( in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
- c += child.eval(float2(clamp(-in_blurOffset.x + xy.x, 0, in_maxSizeXY.x),
- clamp(-in_blurOffset.y + xy.y, 0, in_maxSizeXY.y)));
-
- return half4(c.rgb * 0.2, 1.0);
- }
- )");
-
- auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
- if (!blurEffect) {
- LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
- }
- mBlurEffect = std::move(blurEffect);
-
+static sk_sp<SkRuntimeEffect> createMixEffect() {
SkString mixString(R"(
uniform shader blurredInput;
uniform shader originalInput;
@@ -73,58 +46,12 @@
if (!mixEffect) {
LOG_ALWAYS_FATAL("RuntimeShader error: %s", mixError.c_str());
}
- mMixEffect = std::move(mixEffect);
+ return mixEffect;
}
-sk_sp<SkImage> BlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
- const sk_sp<SkImage> input, const SkRect& blurRect) const {
- // Kawase is an approximation of Gaussian, but it behaves differently from it.
- // A radius transformation is required for approximating them, and also to introduce
- // non-integer steps, necessary to smoothly interpolate large radii.
- float tmpRadius = (float)blurRadius / 2.0f;
- float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
- float radiusByPasses = tmpRadius / (float)numberOfPasses;
-
- // create blur surface with the bit depth and colorspace of the original surface
- SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
- std::ceil(blurRect.height() * kInputScale));
-
- const float stepX = radiusByPasses;
- const float stepY = radiusByPasses;
-
- // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
- // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
- // but instead we must do the inverse.
- SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
- blurMatrix.postScale(kInputScale, kInputScale);
-
- // start by downscaling and doing the first blur pass
- SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
- SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
- blurBuilder.child("child") =
- input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * kInputScale, stepY * kInputScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
-
- sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
-
- // And now we'll build our chain of scaled blur stages
- for (auto i = 1; i < numberOfPasses; i++) {
- const float stepScale = (float)i * kInputScale;
- blurBuilder.child("child") =
- tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
- blurBuilder.uniform("in_blurOffset") = SkV2{stepX * stepScale, stepY * stepScale};
- blurBuilder.uniform("in_maxSizeXY") =
- SkV2{blurRect.width() * kInputScale, blurRect.height() * kInputScale};
- tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
- }
-
- return tmpBlur;
-}
-
-static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect, float scale) {
- // 1. Apply the blur shader matrix, which scales up the blured surface to its real size
+static SkMatrix getShaderTransform(const SkCanvas* canvas, const SkRect& blurRect,
+ const float scale) {
+ // 1. Apply the blur shader matrix, which scales up the blurred surface to its real size
auto matrix = SkMatrix::Scale(scale, scale);
// 2. Since the blurred surface has the size of the layer, we align it with the
// top left corner of the layer position.
@@ -139,6 +66,14 @@
return matrix;
}
+BlurFilter::BlurFilter(const float maxCrossFadeRadius)
+ : mMaxCrossFadeRadius(maxCrossFadeRadius),
+ mMixEffect(maxCrossFadeRadius > 0 ? createMixEffect() : nullptr) {}
+
+float BlurFilter::getMaxCrossFadeRadius() const {
+ return mMaxCrossFadeRadius;
+}
+
void BlurFilter::drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
const uint32_t blurRadius, const float blurAlpha,
const SkRect& blurRect, sk_sp<SkImage> blurredImage,
@@ -153,7 +88,7 @@
const auto blurShader = blurredImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
linearSampling, &blurMatrix);
- if (blurRadius < kMaxCrossFadeRadius) {
+ if (blurRadius < mMaxCrossFadeRadius) {
// For sampling Skia's API expects the inverse of what logically seems appropriate. In this
// case you might expect the matrix to simply be the canvas matrix.
SkMatrix inputMatrix;
@@ -166,7 +101,7 @@
blurBuilder.child("originalInput") =
input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linearSampling,
inputMatrix);
- blurBuilder.uniform("mixFactor") = blurRadius / kMaxCrossFadeRadius;
+ blurBuilder.uniform("mixFactor") = blurRadius / mMaxCrossFadeRadius;
paint.setShader(blurBuilder.makeShader(nullptr, true));
} else {
diff --git a/libs/renderengine/skia/filters/BlurFilter.h b/libs/renderengine/skia/filters/BlurFilter.h
index 7110018..9cddc75 100644
--- a/libs/renderengine/skia/filters/BlurFilter.h
+++ b/libs/renderengine/skia/filters/BlurFilter.h
@@ -27,29 +27,19 @@
namespace renderengine {
namespace skia {
-/**
- * This is an implementation of a Kawase blur, as described in here:
- * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
- * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
- */
class BlurFilter {
public:
// Downsample FBO to improve performance
static constexpr float kInputScale = 0.25f;
// Downsample scale factor used to improve performance
static constexpr float kInverseInputScale = 1.0f / kInputScale;
- // Maximum number of render passes
- static constexpr uint32_t kMaxPasses = 4;
- // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
- // image, up to this radius.
- static constexpr float kMaxCrossFadeRadius = 10.0f;
- explicit BlurFilter();
- virtual ~BlurFilter(){};
+ explicit BlurFilter(float maxCrossFadeRadius = 10.0f);
+ virtual ~BlurFilter(){}
// Execute blur, saving it to a texture
- sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
- const sk_sp<SkImage> blurInput, const SkRect& blurRect) const;
+ virtual sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const = 0;
/**
* Draw the blurred content (from the generate method) into the canvas.
@@ -61,13 +51,20 @@
* @param blurredImage down-sampled blurred content that was produced by the generate() method
* @param input original unblurred input that is used to crossfade with the blurredImage
*/
- void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion, const uint32_t blurRadius,
- const float blurAlpha, const SkRect& blurRect, sk_sp<SkImage> blurredImage,
- sk_sp<SkImage> input);
+ void drawBlurRegion(SkCanvas* canvas, const SkRRect& effectRegion,
+ const uint32_t blurRadius, const float blurAlpha,
+ const SkRect& blurRect, sk_sp<SkImage> blurredImage,
+ sk_sp<SkImage> input);
+
+ float getMaxCrossFadeRadius() const;
private:
- sk_sp<SkRuntimeEffect> mBlurEffect;
- sk_sp<SkRuntimeEffect> mMixEffect;
+ // To avoid downscaling artifacts, we interpolate the blurred fbo with the full composited
+ // image, up to this radius.
+ const float mMaxCrossFadeRadius;
+
+ // Optional blend used for crossfade only if mMaxCrossFadeRadius > 0
+ const sk_sp<SkRuntimeEffect> mMixEffect;
};
} // namespace skia
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.cpp b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
new file mode 100644
index 0000000..55867a9
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GaussianBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkImageFilters.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+// This constant approximates the scaling done in the software path's
+// "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
+static const float BLUR_SIGMA_SCALE = 0.57735f;
+
+GaussianBlurFilter::GaussianBlurFilter(): BlurFilter(/* maxCrossFadeRadius= */ 0.0f) {}
+
+sk_sp<SkImage> GaussianBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+ sk_sp<SkSurface> surface = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, scaledInfo);
+
+ SkPaint paint;
+ paint.setBlendMode(SkBlendMode::kSrc);
+ paint.setImageFilter(SkImageFilters::Blur(
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ blurRadius * kInputScale * BLUR_SIGMA_SCALE,
+ SkTileMode::kClamp, nullptr));
+
+ surface->getCanvas()->drawImageRect(
+ input,
+ blurRect,
+ SkRect::MakeWH(scaledInfo.width(), scaledInfo.height()),
+ SkSamplingOptions{SkFilterMode::kLinear, SkMipmapMode::kNone},
+ &paint,
+ SkCanvas::SrcRectConstraint::kFast_SrcRectConstraint);
+ return surface->makeImageSnapshot();
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/GaussianBlurFilter.h b/libs/renderengine/skia/filters/GaussianBlurFilter.h
new file mode 100644
index 0000000..a4febd2
--- /dev/null
+++ b/libs/renderengine/skia/filters/GaussianBlurFilter.h
@@ -0,0 +1,47 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Gaussian blur using Skia's built-in GaussianBlur filter.
+ */
+class GaussianBlurFilter: public BlurFilter {
+public:
+ explicit GaussianBlurFilter();
+ virtual ~GaussianBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.cpp b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
new file mode 100644
index 0000000..bfde06f
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "KawaseBlurFilter.h"
+#include <SkCanvas.h>
+#include <SkData.h>
+#include <SkPaint.h>
+#include <SkRRect.h>
+#include <SkRuntimeEffect.h>
+#include <SkSize.h>
+#include <SkString.h>
+#include <SkSurface.h>
+#include <log/log.h>
+#include <utils/Trace.h>
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+KawaseBlurFilter::KawaseBlurFilter(): BlurFilter() {
+ SkString blurString(R"(
+ uniform shader child;
+ uniform float in_blurOffset;
+
+ half4 main(float2 xy) {
+ half4 c = child.eval(xy);
+ c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));
+ c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));
+ c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));
+ return half4(c.rgb * 0.2, 1.0);
+ }
+ )");
+
+ auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString);
+ if (!blurEffect) {
+ LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str());
+ }
+ mBlurEffect = std::move(blurEffect);
+}
+
+sk_sp<SkImage> KawaseBlurFilter::generate(GrRecordingContext* context, const uint32_t blurRadius,
+ const sk_sp<SkImage> input, const SkRect& blurRect)
+ const {
+ // Kawase is an approximation of Gaussian, but it behaves differently from it.
+ // A radius transformation is required for approximating them, and also to introduce
+ // non-integer steps, necessary to smoothly interpolate large radii.
+ float tmpRadius = (float)blurRadius / 2.0f;
+ float numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius));
+ float radiusByPasses = tmpRadius / (float)numberOfPasses;
+
+ // create blur surface with the bit depth and colorspace of the original surface
+ SkImageInfo scaledInfo = input->imageInfo().makeWH(std::ceil(blurRect.width() * kInputScale),
+ std::ceil(blurRect.height() * kInputScale));
+
+ // For sampling Skia's API expects the inverse of what logically seems appropriate. In this
+ // case you might expect Translate(blurRect.fLeft, blurRect.fTop) X Scale(kInverseInputScale)
+ // but instead we must do the inverse.
+ SkMatrix blurMatrix = SkMatrix::Translate(-blurRect.fLeft, -blurRect.fTop);
+ blurMatrix.postScale(kInputScale, kInputScale);
+
+ // start by downscaling and doing the first blur pass
+ SkSamplingOptions linear(SkFilterMode::kLinear, SkMipmapMode::kNone);
+ SkRuntimeShaderBuilder blurBuilder(mBlurEffect);
+ blurBuilder.child("child") =
+ input->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear, blurMatrix);
+ blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale;
+
+ sk_sp<SkImage> tmpBlur(blurBuilder.makeImage(context, nullptr, scaledInfo, false));
+
+ // And now we'll build our chain of scaled blur stages
+ for (auto i = 1; i < numberOfPasses; i++) {
+ blurBuilder.child("child") =
+ tmpBlur->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, linear);
+ blurBuilder.uniform("in_blurOffset") = (float) i * radiusByPasses * kInputScale;
+ tmpBlur = blurBuilder.makeImage(context, nullptr, scaledInfo, false);
+ }
+
+ return tmpBlur;
+}
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/KawaseBlurFilter.h b/libs/renderengine/skia/filters/KawaseBlurFilter.h
new file mode 100644
index 0000000..0ac5ac8
--- /dev/null
+++ b/libs/renderengine/skia/filters/KawaseBlurFilter.h
@@ -0,0 +1,54 @@
+/*
+ * 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 "BlurFilter.h"
+#include <SkCanvas.h>
+#include <SkImage.h>
+#include <SkRuntimeEffect.h>
+#include <SkSurface.h>
+
+using namespace std;
+
+namespace android {
+namespace renderengine {
+namespace skia {
+
+/**
+ * This is an implementation of a Kawase blur, as described in here:
+ * https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/
+ * 00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
+ */
+class KawaseBlurFilter: public BlurFilter {
+public:
+ // Maximum number of render passes
+ static constexpr uint32_t kMaxPasses = 4;
+
+ explicit KawaseBlurFilter();
+ virtual ~KawaseBlurFilter(){}
+
+ // Execute blur, saving it to a texture
+ sk_sp<SkImage> generate(GrRecordingContext* context, const uint32_t radius,
+ const sk_sp<SkImage> blurInput, const SkRect& blurRect) const override;
+
+private:
+ sk_sp<SkRuntimeEffect> mBlurEffect;
+};
+
+} // namespace skia
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/skia/filters/LinearEffect.cpp b/libs/renderengine/skia/filters/LinearEffect.cpp
index 73dadef..36305ae 100644
--- a/libs/renderengine/skia/filters/LinearEffect.cpp
+++ b/libs/renderengine/skia/filters/LinearEffect.cpp
@@ -19,423 +19,19 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include <SkString.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 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, 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:
- shader.append(R"(
- float3 ScaleLuminance(float3 xyz) {
- return xyz * in_inputMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateToneMapInterpolation(ui::Dataspace inputDataspace,
- ui::Dataspace outputDataspace, SkString& shader) {
- switch (inputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- case HAL_DATASPACE_TRANSFER_HLG:
- // 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.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return clamp(xyz, 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.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- float maxInLumi = in_inputMaxLuminance;
- float maxOutLumi = in_displayMaxLuminance;
-
- float nits = 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 (maxInLumi <= maxOutLumi) {
- return xyz;
- } else {
-
- // three control points
- const float x0 = 10.0;
- const float y0 = 17.0;
- float x1 = maxOutLumi * 0.75;
- float y1 = x1;
- float x2 = x1 + (maxInLumi - x1) / 2.0;
- float y2 = y1 + (maxOutLumi - y1) * 0.75;
-
- // horizontal distances between the last three control points
- float h12 = x2 - x1;
- float h23 = maxInLumi - x2;
- // tangents at the last three control points
- float m1 = (y2 - y1) / h12;
- float m3 = (maxOutLumi - y2) / h23;
- float m2 = (m1 + m3) / 2.0;
-
- if (nits < x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return xyz * slope;
- } else if (nits < x1) {
- // scale [x0, x1] to [y0, y1] linearly
- float slope = (y1 - y0) / (x1 - x0);
- nits = y0 + (nits - x0) * slope;
- } else if (nits < x2) {
- // scale [x1, x2] to [y1, y2] using Hermite interp
- float t = (nits - x1) / h12;
- nits = (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
- float t = (nits - x2) / h23;
- nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
- (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
- }
- }
-
- // color.y is greater than x0 and is thus non-zero
- return xyz * (nits / xyz.y);
- }
- )");
- break;
- }
- break;
- default:
- switch (outputDataspace & HAL_DATASPACE_TRANSFER_MASK) {
- case HAL_DATASPACE_TRANSFER_ST2084:
- case HAL_DATASPACE_TRANSFER_HLG:
- // 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.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- const float maxOutLumi = 3000.0;
-
- const float x0 = 5.0;
- const float y0 = 2.5;
- float x1 = in_displayMaxLuminance * 0.7;
- float y1 = maxOutLumi * 0.15;
- float x2 = in_displayMaxLuminance * 0.9;
- float y2 = maxOutLumi * 0.45;
- float x3 = in_displayMaxLuminance;
- float y3 = maxOutLumi;
-
- float c1 = y1 / 3.0;
- float c2 = y2 / 2.0;
- float c3 = y3 / 1.5;
-
- float nits = xyz.y;
-
- if (nits <= x0) {
- // scale [0.0, x0] to [0.0, y0] linearly
- float slope = y0 / x0;
- return xyz * slope;
- } else if (nits <= x1) {
- // scale [x0, x1] to [y0, y1] using a curve
- float t = (nits - x0) / (x1 - x0);
- nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
- } else if (nits <= x2) {
- // scale [x1, x2] to [y1, y2] using a curve
- float t = (nits - x1) / (x2 - x1);
- nits = (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
- float t = (nits - x2) / (x3 - x2);
- nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
- }
-
- // xyz.y is greater than x0 and is thus non-zero
- return xyz * (nits / xyz.y);
- }
- )");
- break;
- default:
- // For completeness, this is tone-mapping from SDR to SDR, where this is just a
- // no-op.
- shader.append(R"(
- float3 ToneMap(float3 xyz) {
- return xyz;
- }
- )");
- break;
- }
- 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_displayMaxLuminance;
- }
- )");
- break;
- }
-}
-
-static void generateOOTF(ui::Dataspace inputDataspace, ui::Dataspace outputDataspace,
- SkString& shader) {
- // Input uniforms
- shader.append(R"(
- uniform float in_displayMaxLuminance;
- uniform float in_inputMaxLuminance;
- )");
-
- generateLuminanceScalesForOOTF(inputDataspace, shader);
- generateToneMapInterpolation(inputDataspace, outputDataspace, shader);
- generateLuminanceNormalizationForOOTF(outputDataspace, shader);
-
- shader.append(R"(
- float3 OOTF(float3 xyz) {
- return NormalizeLuminance(ToneMap(ScaleLuminance(xyz)));
- }
- )");
-}
-
-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"(
- c.rgb = OETF(ToRGB(OOTF(ToXYZ(EOTF(c.rgb)))));
- )");
- 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) {
@@ -444,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) {
@@ -453,23 +50,13 @@
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);
+ const auto uniforms = shaders::buildLinearEffectUniforms(linearEffect, colorTransform,
+ maxDisplayLuminance, maxLuminance);
- effectBuilder.uniform("in_rgbToXyz") = mat4(inputColorSpace.getRGBtoXYZ());
- effectBuilder.uniform("in_xyzToRgb") =
- colorTransform * mat4(outputColorSpace.getXYZtoRGB());
+ for (const auto& uniform : uniforms) {
+ effectBuilder.uniform(uniform.name.c_str()).set(uniform.value.data(), uniform.value.size());
}
- effectBuilder.uniform("in_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
- effectBuilder.uniform("in_inputMaxLuminance") =
- maxLuminance > 0 ? maxLuminance : maxDisplayLuminance;
return effectBuilder.makeShader(nullptr, false);
}
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 d0e19dd..a426850 100644
--- a/libs/renderengine/tests/Android.bp
+++ b/libs/renderengine/tests/Android.bp
@@ -23,7 +23,10 @@
cc_test {
name: "librenderengine_test",
- defaults: ["skia_deps", "surfaceflinger_defaults"],
+ defaults: [
+ "skia_deps",
+ "surfaceflinger_defaults",
+ ],
test_suites: ["device-tests"],
srcs: [
"RenderEngineTest.cpp",
@@ -36,6 +39,8 @@
"libgmock",
"librenderengine",
"librenderengine_mocks",
+ "libshaders",
+ "libtonemap",
],
shared_libs: [
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index c2c05f4..eb2b2dc 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>
@@ -205,6 +208,21 @@
renderengine::ExternalTexture::Usage::WRITEABLE);
}
+ std::shared_ptr<renderengine::ExternalTexture> allocateAndFillSourceBuffer(uint32_t width,
+ uint32_t height,
+ ubyte4 color) {
+ const auto buffer = allocateSourceBuffer(width, height);
+ uint8_t* pixels;
+ buffer->getBuffer()->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
+ pixels[0] = color.r;
+ pixels[1] = color.g;
+ pixels[2] = color.b;
+ pixels[3] = color.a;
+ buffer->getBuffer()->unlock();
+ return buffer;
+ }
+
RenderEngineTest() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
@@ -282,6 +300,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 +315,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 +329,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 +356,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 +519,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 +908,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 +1232,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 +1414,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 +1546,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 +1656,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 +1766,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 +1839,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 +1855,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 +1876,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 +1898,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 +1921,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);
@@ -1977,6 +2210,133 @@
expectBufferColor(rect, 0, 128, 0, 128);
}
+TEST_P(RenderEngineTest, testDimming) {
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+ initializeRenderEngine();
+
+ const auto displayRect = Rect(3, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = 1000.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+ const auto redBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(255, 0, 0, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f / 51.f,
+ };
+
+ const renderengine::LayerSettings redLayer{
+ .geometry.boundaries = FloatRect(2.f, 0.f, 3.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = redBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ // When the white point is not set for a layer, just ignore it and treat it as the same
+ // as the max layer
+ .whitePointNits = -1.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer, redLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 5, 255, 1);
+ expectBufferColor(Rect(2, 0, 3, 1), 51, 0, 0, 255, 1);
+}
+
+TEST_P(RenderEngineTest, testDimming_withoutTargetLuminance) {
+ initializeRenderEngine();
+ if (GetParam()->type() == renderengine::RenderEngine::RenderEngineType::GLES) {
+ return;
+ }
+
+ const auto displayRect = Rect(2, 1);
+ const renderengine::DisplaySettings display{
+ .physicalDisplay = displayRect,
+ .clip = displayRect,
+ .outputDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .targetLuminanceNits = -1.f,
+ };
+
+ const auto greenBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 255, 0, 255));
+ const auto blueBuffer = allocateAndFillSourceBuffer(1, 1, ubyte4(0, 0, 255, 255));
+
+ const renderengine::LayerSettings greenLayer{
+ .geometry.boundaries = FloatRect(0.f, 0.f, 1.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = greenBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 200.f,
+ };
+
+ const renderengine::LayerSettings blueLayer{
+ .geometry.boundaries = FloatRect(1.f, 0.f, 2.f, 1.f),
+ .source =
+ renderengine::PixelSource{
+ .buffer =
+ renderengine::Buffer{
+ .buffer = blueBuffer,
+ .usePremultipliedAlpha = true,
+ },
+ },
+ .alpha = 1.0f,
+ .sourceDataspace = ui::Dataspace::V0_SRGB_LINEAR,
+ .whitePointNits = 1000.f,
+ };
+
+ std::vector<renderengine::LayerSettings> layers{greenLayer, blueLayer};
+ invokeDraw(display, layers);
+
+ expectBufferColor(Rect(1, 1), 0, 51, 0, 255, 1);
+ expectBufferColor(Rect(1, 0, 2, 1), 0, 0, 255, 255);
+}
+
TEST_P(RenderEngineTest, test_isOpaque) {
initializeRenderEngine();
@@ -2027,6 +2387,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/renderengine/threaded/RenderEngineThreaded.cpp b/libs/renderengine/threaded/RenderEngineThreaded.cpp
index 3d446e8..08fc814 100644
--- a/libs/renderengine/threaded/RenderEngineThreaded.cpp
+++ b/libs/renderengine/threaded/RenderEngineThreaded.cpp
@@ -178,6 +178,10 @@
void RenderEngineThreaded::genTextures(size_t count, uint32_t* names) {
ATRACE_CALL();
+ // This is a no-op in SkiaRenderEngine.
+ if (getRenderEngineType() != RenderEngineType::THREADED) {
+ return;
+ }
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
@@ -194,6 +198,10 @@
void RenderEngineThreaded::deleteTextures(size_t count, uint32_t const* names) {
ATRACE_CALL();
+ // This is a no-op in SkiaRenderEngine.
+ if (getRenderEngineType() != RenderEngineType::THREADED) {
+ return;
+ }
std::promise<void> resultPromise;
std::future<void> resultFuture = resultPromise.get_future();
{
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
new file mode 100644
index 0000000..5360fe2
--- /dev/null
+++ b/libs/tonemap/Android.bp
@@ -0,0 +1,43 @@
+// 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: "libtonemap",
+ vendor_available: true,
+
+ export_include_dirs: ["include"],
+ local_include_dirs: ["include"],
+
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libmath",
+ ],
+
+ srcs: [
+ "tonemap.cpp",
+ ],
+}
diff --git a/libs/tonemap/OWNERS b/libs/tonemap/OWNERS
new file mode 100644
index 0000000..6d91da3
--- /dev/null
+++ b/libs/tonemap/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/tonemap/TEST_MAPPING b/libs/tonemap/TEST_MAPPING
new file mode 100644
index 0000000..00f83ba
--- /dev/null
+++ b/libs/tonemap/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ },
+ {
+ "name": "libtonemap_test"
+ }
+ ]
+}
diff --git a/libs/tonemap/include/tonemap/tonemap.h b/libs/tonemap/include/tonemap/tonemap.h
new file mode 100644
index 0000000..bd7b72d
--- /dev/null
+++ b/libs/tonemap/include/tonemap/tonemap.h
@@ -0,0 +1,119 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <math/vec3.h>
+
+#include <string>
+#include <vector>
+
+namespace android::tonemap {
+
+// Describes a shader uniform
+// The shader uniform is intended to be passed into a SkRuntimeShaderBuilder, i.e.:
+//
+// SkRuntimeShaderBuilder builder;
+// builder.uniform(<uniform name>).set(<uniform value>.data(), <uniform value>.size());
+struct ShaderUniform {
+ // The name of the uniform, used for binding into a shader.
+ // The shader must contain a uniform whose name matches this.
+ std::string name;
+
+ // The value for the uniform, which should be bound to the uniform identified by <name>
+ std::vector<uint8_t> value;
+};
+
+// Describes metadata which may be used for constructing the shader uniforms.
+// This metadata should not be used for manipulating the source code of the shader program directly,
+// as otherwise caching by other system of these shaders may break.
+struct Metadata {
+ float displayMaxLuminance = 0.0;
+ float contentMaxLuminance = 0.0;
+};
+
+class ToneMapper {
+public:
+ virtual ~ToneMapper() {}
+ // 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);
+ //
+ // The arguments are:
+ // * linearRGB is the absolute nits of the RGB pixels in linear space
+ // * xyz is linearRGB converted into XYZ
+ //
+ // libtonemap_LookupTonemapGain() returns a float representing the amount by which to scale the
+ // absolute nits of the pixels. This function may be plugged into any existing SkSL shader, and
+ // is expected to look something like this:
+ //
+ // vec3 rgb = ...;
+ // // apply the EOTF based on the incoming dataspace to convert to linear nits.
+ // vec3 linearRGB = applyEOTF(rgb);
+ // // apply a RGB->XYZ matrix float3
+ // vec3 xyz = toXYZ(linearRGB);
+ // // Scale the luminance based on the content standard
+ // vec3 absoluteRGB = ScaleLuminance(linearRGB);
+ // vec3 absoluteXYZ = ScaleLuminance(xyz);
+ // float gain = libtonemap_LookupTonemapGain(absoluteRGB, absoluteXYZ);
+ // // Normalize the luminance back down to a [0, 1] range
+ // xyz = NormalizeLuminance(absoluteXYZ * gain);
+ // // apply a XYZ->RGB matrix and apply the output OETf.
+ // vec3 finalColor = applyOETF(ToRGB(xyz));
+ // ...
+ //
+ // Helper methods in this shader should be prefixed with "libtonemap_". Accordingly, libraries
+ // which consume this shader must *not* contain any methods prefixed with "libtonemap_" to
+ // guarantee that there are no conflicts in name resolution.
+ virtual std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) = 0;
+
+ // Constructs uniform descriptions that correspond to those that are generated for the tonemap
+ // shader. Uniforms must be prefixed with "in_libtonemap_". Libraries which consume this shader
+ // must not bind any new uniforms that begin with this prefix.
+ //
+ // Downstream shaders may assume the existence of the uniform in_libtonemap_displayMaxLuminance
+ // and in_libtonemap_inputMaxLuminance, in order to assist with scaling and normalizing
+ // luminance as described in the documentation for generateTonemapGainShaderSkSL(). That is,
+ // shaders plugging in a tone-mapping shader returned by generateTonemapGainShaderSkSL() may
+ // 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.
+// This instance is globally constructed.
+ToneMapper* getToneMapper();
+
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/tonemap/tests/Android.bp b/libs/tonemap/tests/Android.bp
new file mode 100644
index 0000000..f46f3fa
--- /dev/null
+++ b/libs/tonemap/tests/Android.bp
@@ -0,0 +1,39 @@
+// 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_test {
+ name: "libtonemap_test",
+ test_suites: ["device-tests"],
+ srcs: [
+ "tonemap_test.cpp",
+ ],
+ shared_libs: [
+ "android.hardware.graphics.common-V3-ndk",
+ ],
+ static_libs: [
+ "libmath",
+ "libgmock",
+ "libgtest",
+ "libtonemap",
+ ],
+}
diff --git a/libs/tonemap/tests/tonemap_test.cpp b/libs/tonemap/tests/tonemap_test.cpp
new file mode 100644
index 0000000..7a7958f
--- /dev/null
+++ b/libs/tonemap/tests/tonemap_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 <tonemap/tonemap.h>
+#include <cmath>
+
+namespace android {
+
+using testing::HasSubstr;
+
+struct TonemapTest : public ::testing::Test {};
+
+TEST_F(TonemapTest, generateShaderSkSLUniforms_containsDefaultUniforms) {
+ static const constexpr float kDisplayMaxLuminance = 1.f;
+ static const constexpr float kContentMaxLuminance = 2.f;
+ tonemap::Metadata metadata{.displayMaxLuminance = kDisplayMaxLuminance,
+ .contentMaxLuminance = kContentMaxLuminance};
+ const auto uniforms = tonemap::getToneMapper()->generateShaderSkSLUniforms(metadata);
+
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ }));
+ ASSERT_EQ(1, std::count_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ }));
+
+ // Smoke check that metadata values are "real", specifically that they're non-zero and actually
+ // numbers. This is to help avoid shaders using these uniforms from dividing by zero or other
+ // catastrophic errors.
+ const auto& displayLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_displayMaxLuminance";
+ })->value;
+
+ float displayLumFloat = 0.f;
+ std::memcpy(&displayLumFloat, displayLum.data(), displayLum.size());
+ EXPECT_FALSE(std::isnan(displayLumFloat));
+ EXPECT_GT(displayLumFloat, 0);
+
+ const auto& contentLum = std::find_if(uniforms.cbegin(), uniforms.cend(), [](const auto& data) {
+ return data.name == "in_libtonemap_inputMaxLuminance";
+ })->value;
+
+ float contentLumFloat = 0.f;
+ std::memcpy(&contentLumFloat, contentLum.data(), contentLum.size());
+ EXPECT_FALSE(std::isnan(contentLumFloat));
+ EXPECT_GT(contentLumFloat, 0);
+}
+
+TEST_F(TonemapTest, generateTonemapGainShaderSkSL_containsEntryPoint) {
+ const auto shader =
+ tonemap::getToneMapper()
+ ->generateTonemapGainShaderSkSL(aidl::android::hardware::graphics::common::
+ Dataspace::BT2020_ITU_PQ,
+ aidl::android::hardware::graphics::common::
+ Dataspace::DISPLAY_P3);
+
+ // Other tests such as librenderengine_test will plug in the shader to check compilation.
+ EXPECT_THAT(shader, HasSubstr("float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz)"));
+}
+
+} // namespace android
diff --git a/libs/tonemap/tonemap.cpp b/libs/tonemap/tonemap.cpp
new file mode 100644
index 0000000..c2372fe
--- /dev/null
+++ b/libs/tonemap/tonemap.cpp
@@ -0,0 +1,666 @@
+/*
+ * 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 <tonemap/tonemap.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <mutex>
+#include <type_traits>
+
+namespace android::tonemap {
+
+namespace {
+
+// Flag containing the variant of tone map algorithm to use.
+enum class ToneMapAlgorithm {
+ AndroidO, // Default algorithm in place since Android O,
+ Android13, // Algorithm used in Android 13.
+};
+
+static const constexpr auto kToneMapAlgorithm = ToneMapAlgorithm::Android13;
+
+static const constexpr auto kTransferMask =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_MASK);
+static const constexpr auto kTransferST2084 =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_ST2084);
+static const constexpr auto kTransferHLG =
+ static_cast<int32_t>(aidl::android::hardware::graphics::common::Dataspace::TRANSFER_HLG);
+
+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;
+}
+
+class ToneMapperO : public ToneMapper {
+public:
+ std::string generateTonemapGainShaderSkSL(
+ aidl::android::hardware::graphics::common::Dataspace sourceDataspace,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Define required uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return 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.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return clamp(xyz.y, 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.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = 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 (maxInLumi <= maxOutLumi) {
+ return xyz.y;
+ } else {
+
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three
+ // control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return nits * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (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
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) *
+ (1.0 - t) * (1.0 - t) + (maxOutLumi *
+ (3.0 - 2.0 * t) + h23 * m3 *
+ (t - 1.0)) * t * t;
+ }
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ 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.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = in_libtonemap_displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = in_libtonemap_displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = in_libtonemap_displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = xyz.y;
+
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return nits * slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 +
+ 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (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
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 +
+ 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ default:
+ // For completeness, this is tone-mapping from SDR to SDR, where this is
+ // just a no-op.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(vec3 xyz) {
+ return xyz.y;
+ }
+ )");
+ break;
+ }
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ if (xyz.y <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(xyz) / xyz.y;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ std::vector<ShaderUniform> uniforms;
+
+ uniforms.reserve(2);
+
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .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,
+ aidl::android::hardware::graphics::common::Dataspace destinationDataspace) override {
+ const int32_t sourceDataspaceInt = static_cast<int32_t>(sourceDataspace);
+ const int32_t destinationDataspaceInt = static_cast<int32_t>(destinationDataspace);
+
+ std::string program;
+ // Input uniforms
+ program.append(R"(
+ uniform float in_libtonemap_displayMaxLuminance;
+ uniform float in_libtonemap_inputMaxLuminance;
+ )");
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ case kTransferHLG:
+ switch (destinationDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return 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.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return clamp(maxRGB, 0.0, 1000.0);
+ }
+ )");
+ break;
+
+ default:
+ switch (sourceDataspaceInt & kTransferMask) {
+ case kTransferST2084:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 10000.0;
+ 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 = pow(channel, float(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, float(m2));
+ }
+ )");
+ break;
+ case kTransferHLG:
+ program.append(R"(
+ float libtonemap_OETFTone(float channel) {
+ channel = channel / 1000.0;
+ 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;
+ }
+ )");
+ break;
+ }
+ // Here we're mapping from HDR to SDR content, so interpolate using a
+ // Hermitian polynomial onto the smaller luminance range.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ float maxInLumi = in_libtonemap_inputMaxLuminance;
+ float maxOutLumi = in_libtonemap_displayMaxLuminance;
+
+ float nits = maxRGB;
+
+ float x1 = maxOutLumi * 0.65;
+ float y1 = x1;
+
+ float x3 = maxInLumi;
+ float y3 = maxOutLumi;
+
+ float x2 = x1 + (x3 - x1) * 4.0 / 17.0;
+ float y2 = maxOutLumi * 0.9;
+
+ float greyNorm1 = libtonemap_OETFTone(x1);
+ float greyNorm2 = libtonemap_OETFTone(x2);
+ float greyNorm3 = libtonemap_OETFTone(x3);
+
+ float slope1 = 0;
+ float slope2 = (y2 - y1) / (greyNorm2 - greyNorm1);
+ float slope3 = (y3 - y2 ) / (greyNorm3 - greyNorm2);
+
+ if (nits < x1) {
+ return nits;
+ }
+
+ if (nits > maxInLumi) {
+ return maxOutLumi;
+ }
+
+ float greyNits = libtonemap_OETFTone(nits);
+
+ if (greyNits <= greyNorm2) {
+ nits = (greyNits - greyNorm2) * slope2 + y2;
+ } else if (greyNits <= greyNorm3) {
+ nits = (greyNits - greyNorm3) * slope3 + y3;
+ } else {
+ nits = maxOutLumi;
+ }
+
+ return nits;
+ }
+ )");
+ break;
+ }
+ break;
+ default:
+ // Inverse tone-mapping and SDR-SDR mapping is not supported.
+ program.append(R"(
+ float libtonemap_ToneMapTargetNits(float maxRGB) {
+ return maxRGB;
+ }
+ )");
+ break;
+ }
+
+ program.append(R"(
+ float libtonemap_LookupTonemapGain(vec3 linearRGB, vec3 xyz) {
+ float maxRGB = max(linearRGB.r, max(linearRGB.g, linearRGB.b));
+ if (maxRGB <= 0.0) {
+ return 1.0;
+ }
+ return libtonemap_ToneMapTargetNits(maxRGB) / maxRGB;
+ }
+ )");
+ return program;
+ }
+
+ std::vector<ShaderUniform> generateShaderSkSLUniforms(const Metadata& metadata) override {
+ // Hardcode the max content luminance to a "reasonable" level
+ static const constexpr float kContentMaxLuminance = 4000.f;
+ std::vector<ShaderUniform> uniforms;
+ uniforms.reserve(2);
+ uniforms.push_back({.name = "in_libtonemap_displayMaxLuminance",
+ .value = buildUniformValue<float>(metadata.displayMaxLuminance)});
+ uniforms.push_back({.name = "in_libtonemap_inputMaxLuminance",
+ .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
+
+ToneMapper* getToneMapper() {
+ static std::once_flag sOnce;
+ static std::unique_ptr<ToneMapper> sToneMapper;
+
+ std::call_once(sOnce, [&] {
+ switch (kToneMapAlgorithm) {
+ case ToneMapAlgorithm::AndroidO:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapperO());
+ break;
+ case ToneMapAlgorithm::Android13:
+ sToneMapper = std::unique_ptr<ToneMapper>(new ToneMapper13());
+ }
+ });
+
+ return sToneMapper.get();
+}
+} // namespace android::tonemap
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 36d001f..006c478 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -138,6 +138,7 @@
"HdrCapabilities.cpp",
"PixelFormat.cpp",
"PublicFormat.cpp",
+ "StaticAsserts.cpp",
"StaticDisplayInfo.cpp",
],
@@ -225,6 +226,8 @@
"libui_headers",
],
min_sdk_version: "29",
+
+ afdo: true,
}
cc_library_headers {
diff --git a/libs/ui/DebugUtils.cpp b/libs/ui/DebugUtils.cpp
index 1f006ce..073da89 100644
--- a/libs/ui/DebugUtils.cpp
+++ b/libs/ui/DebugUtils.cpp
@@ -302,6 +302,8 @@
return std::string("RGB_565");
case android::PIXEL_FORMAT_BGRA_8888:
return std::string("BGRA_8888");
+ case android::PIXEL_FORMAT_R_8:
+ return std::string("R_8");
default:
return StringPrintf("Unknown %#08x", format);
}
diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp
index e88fdd5..799fbc9 100644
--- a/libs/ui/PixelFormat.cpp
+++ b/libs/ui/PixelFormat.cpp
@@ -35,25 +35,8 @@
case PIXEL_FORMAT_RGBA_5551:
case PIXEL_FORMAT_RGBA_4444:
return 2;
- }
- 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;
+ case PIXEL_FORMAT_R_8:
+ return 1;
}
return 0;
}
diff --git a/libs/ui/StaticAsserts.cpp b/libs/ui/StaticAsserts.cpp
new file mode 100644
index 0000000..85da64f
--- /dev/null
+++ b/libs/ui/StaticAsserts.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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 <ui/PixelFormat.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+
+// Ideally, PIXEL_FORMAT_R_8 would simply be defined to match the aidl PixelFormat, but
+// PixelFormat.h (where PIXEL_FORMAT_R_8 is defined) is pulled in by builds for
+// which there is no aidl build (e.g. Windows).
+static_assert(android::PIXEL_FORMAT_R_8 ==static_cast<int32_t>(
+ aidl::android::hardware::graphics::common::PixelFormat::R_8));
diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h
index 7634007..9aae145 100644
--- a/libs/ui/include/ui/Fence.h
+++ b/libs/ui/include/ui/Fence.h
@@ -124,7 +124,7 @@
// getStatus() returns whether the fence has signaled yet. Prefer this to
// getSignalTime() or wait() if all you care about is whether the fence has
// signaled.
- inline Status getStatus() {
+ virtual inline Status getStatus() {
// The sync_wait call underlying wait() has been measured to be
// significantly faster than the sync_fence_info call underlying
// getSignalTime(), which might otherwise appear to be the more obvious
diff --git a/libs/ui/include/ui/PixelFormat.h b/libs/ui/include/ui/PixelFormat.h
index 02773d9..f422ce4 100644
--- a/libs/ui/include/ui/PixelFormat.h
+++ b/libs/ui/include/ui/PixelFormat.h
@@ -62,12 +62,12 @@
PIXEL_FORMAT_RGBA_4444 = 7, // 16-bit ARGB
PIXEL_FORMAT_RGBA_FP16 = HAL_PIXEL_FORMAT_RGBA_FP16, // 64-bit RGBA
PIXEL_FORMAT_RGBA_1010102 = HAL_PIXEL_FORMAT_RGBA_1010102, // 32-bit RGBA
+ PIXEL_FORMAT_R_8 = 0x38,
};
typedef int32_t PixelFormat;
uint32_t bytesPerPixel(PixelFormat format);
-uint32_t bitsPerPixel(PixelFormat format);
}; // namespace android
diff --git a/libs/ui/include_mock/ui/MockFence.h b/libs/ui/include_mock/ui/MockFence.h
index 162ec02..71adee4 100644
--- a/libs/ui/include_mock/ui/MockFence.h
+++ b/libs/ui/include_mock/ui/MockFence.h
@@ -27,6 +27,7 @@
virtual ~MockFence() = default;
MOCK_METHOD(nsecs_t, getSignalTime, (), (const, override));
+ MOCK_METHOD(Status, getStatus, (), (override));
};
}; // namespace android::mock
diff --git a/libs/ui/include_types/ui/DataspaceUtils.h b/libs/ui/include_types/ui/DataspaceUtils.h
new file mode 100644
index 0000000..a461cb4
--- /dev/null
+++ b/libs/ui/include_types/ui/DataspaceUtils.h
@@ -0,0 +1,29 @@
+/*
+ * 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 <ui/GraphicTypes.h>
+
+namespace android {
+
+inline bool isHdrDataspace(ui::Dataspace dataspace) {
+ const auto transfer = dataspace & HAL_DATASPACE_TRANSFER_MASK;
+
+ return transfer == HAL_DATASPACE_TRANSFER_ST2084 || transfer == HAL_DATASPACE_TRANSFER_HLG;
+}
+
+} // namespace android
\ No newline at end of file
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/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 0ee15f2..22fbf45 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -163,3 +163,13 @@
"-Werror",
],
}
+
+cc_test {
+ name: "DataspaceUtils_test",
+ shared_libs: ["libui"],
+ srcs: ["DataspaceUtils_test.cpp"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libs/ui/tests/DataspaceUtils_test.cpp b/libs/ui/tests/DataspaceUtils_test.cpp
new file mode 100644
index 0000000..3e09671
--- /dev/null
+++ b/libs/ui/tests/DataspaceUtils_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "DataspaceUtilsTest"
+
+#include <gtest/gtest.h>
+#include <ui/DataspaceUtils.h>
+
+namespace android {
+
+class DataspaceUtilsTest : public testing::Test {};
+
+TEST_F(DataspaceUtilsTest, isHdrDataspace) {
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_HLG));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_ITU_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_PQ));
+ EXPECT_TRUE(isHdrDataspace(ui::Dataspace::BT2020_HLG));
+
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB_LINEAR));
+ // scRGB defines a very wide gamut but not an expanded luminance range
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_SCRGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_JFIF));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_625));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT601_525));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::V0_BT709));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DCI_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_P3));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::ADOBE_RGB));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_LINEAR));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::BT2020_ITU));
+ EXPECT_FALSE(isHdrDataspace(ui::Dataspace::DISPLAY_BT2020));
+}
+
+} // namespace android
diff --git a/libs/ui/tests/MockFence_test.cpp b/libs/ui/tests/MockFence_test.cpp
index 6e520b1..40dddc3 100644
--- a/libs/ui/tests/MockFence_test.cpp
+++ b/libs/ui/tests/MockFence_test.cpp
@@ -42,4 +42,16 @@
EXPECT_EQ(1234, fence->getSignalTime());
}
+TEST_F(MockFenceTest, getStatus) {
+ sp<Fence> fence = getFenceForTesting();
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Unsignaled));
+ EXPECT_EQ(Fence::Status::Unsignaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Signaled));
+ EXPECT_EQ(Fence::Status::Signaled, fence->getStatus());
+
+ EXPECT_CALL(getMockFence(), getStatus).WillOnce(Return(Fence::Status::Invalid));
+ EXPECT_EQ(Fence::Status::Invalid, fence->getStatus());
+}
} // namespace android::ui
diff --git a/opengl/libs/EGL/GLES_layers.md b/opengl/libs/EGL/GLES_layers.md
index bfc44db..f6a8f14 100644
--- a/opengl/libs/EGL/GLES_layers.md
+++ b/opengl/libs/EGL/GLES_layers.md
@@ -251,7 +251,7 @@
- Secondly, if you want to determine from an application that can't call out to ADB for this, you can check for the [EGL_ANDROID_GLES_layers](../../specs/EGL_ANDROID_GLES_layers.txt). It simply indicates support of this layering system:
```cpp
std::string display_extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
- if (display_extension.find("EGL_ANDROID_GLES_layers") != std::string::npos)
+ if (display_extensions.find("EGL_ANDROID_GLES_layers") != std::string::npos)
{
// Layers are supported!
}
diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp
index de36a7a..f4dbe49 100644
--- a/opengl/libs/EGL/egl_platform_entries.cpp
+++ b/opengl/libs/EGL/egl_platform_entries.cpp
@@ -29,6 +29,7 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <stdlib.h>
#include <string.h>
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
#include <condition_variable>
#include <deque>
@@ -564,9 +565,11 @@
newList.push_back(EGL_NONE);
}
+using PixelFormat = aidl::android::hardware::graphics::common::PixelFormat;
+
// Gets the native pixel format corrsponding to the passed EGLConfig.
void getNativePixelFormat(EGLDisplay dpy, egl_connection_t* cnx, EGLConfig config,
- android_pixel_format* format) {
+ PixelFormat* format) {
// Set the native window's buffers format to match what this config requests.
// Whether to use sRGB gamma is not part of the EGLconfig, but is part
// of our native format. So if sRGB gamma is requested, we have to
@@ -599,28 +602,30 @@
// strip colorspace from attribs.
// endif
if (a == 0) {
- if (colorDepth <= 16) {
- *format = HAL_PIXEL_FORMAT_RGB_565;
+ if (8 == r && 0 == g && 0 == b) {
+ *format = PixelFormat::R_8;
+ } else if (colorDepth <= 16) {
+ *format = PixelFormat::RGB_565;
} else {
if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ *format = PixelFormat::RGBA_1010102;
} else {
- *format = HAL_PIXEL_FORMAT_RGBX_8888;
+ *format = PixelFormat::RGBX_8888;
}
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ *format = PixelFormat::RGBA_FP16;
}
}
} else {
if (componentType == EGL_COLOR_COMPONENT_TYPE_FIXED_EXT) {
if (colorDepth > 24) {
- *format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ *format = PixelFormat::RGBA_1010102;
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_8888;
+ *format = PixelFormat::RGBA_8888;
}
} else {
- *format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ *format = PixelFormat::RGBA_FP16;
}
}
}
@@ -678,7 +683,7 @@
}
EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
+ PixelFormat format;
getNativePixelFormat(iDpy, cnx, config, &format);
// now select correct colorspace and dataspace based on user's attribute list
@@ -694,7 +699,7 @@
attrib_list = strippedAttribList.data();
if (!cnx->useAngle) {
- int err = native_window_set_buffers_format(window, format);
+ int err = native_window_set_buffers_format(window, static_cast<int>(format));
if (err != 0) {
ALOGE("error setting native window pixel format: %s (%d)", strerror(-err), err);
native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
@@ -818,7 +823,7 @@
if (!dp) return EGL_NO_SURFACE;
EGLDisplay iDpy = dp->disp.dpy;
- android_pixel_format format;
+ PixelFormat format;
getNativePixelFormat(iDpy, cnx, config, &format);
// Select correct colorspace based on user's attribute list
diff --git a/services/gpuservice/gpumem/Android.bp b/services/gpuservice/gpumem/Android.bp
index 830e53d..24087ac 100644
--- a/services/gpuservice/gpumem/Android.bp
+++ b/services/gpuservice/gpumem/Android.bp
@@ -28,7 +28,7 @@
],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libcutils",
"liblog",
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/gpuservice/gpuservice.rc b/services/gpuservice/gpuservice.rc
index 65a5c27..0da8bd3 100644
--- a/services/gpuservice/gpuservice.rc
+++ b/services/gpuservice/gpuservice.rc
@@ -1,4 +1,4 @@
service gpu /system/bin/gpuservice
class core
user gpu_service
- group graphics
+ group graphics readtracefs
diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp
index 220952d..d033453 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -84,6 +84,38 @@
}
}
+void GpuStats::purgeOldDriverStats() {
+ ALOG_ASSERT(mAppStats.size() == MAX_NUM_APP_RECORDS);
+
+ struct GpuStatsApp {
+ // Key is <app package name>+<driver version code>.
+ const std::string *appStatsKey = nullptr;
+ const std::chrono::time_point<std::chrono::system_clock> *lastAccessTime = nullptr;
+ };
+ std::vector<GpuStatsApp> gpuStatsApps(MAX_NUM_APP_RECORDS);
+
+ // Create a list of pointers to package names and their last access times.
+ int index = 0;
+ for (const auto & [appStatsKey, gpuStatsAppInfo] : mAppStats) {
+ GpuStatsApp &gpuStatsApp = gpuStatsApps[index];
+ gpuStatsApp.appStatsKey = &appStatsKey;
+ gpuStatsApp.lastAccessTime = &gpuStatsAppInfo.lastAccessTime;
+ ++index;
+ }
+
+ // Sort the list with the oldest access times at the front.
+ std::sort(gpuStatsApps.begin(), gpuStatsApps.end(), [](GpuStatsApp a, GpuStatsApp b) -> bool {
+ return *a.lastAccessTime < *b.lastAccessTime;
+ });
+
+ // Remove the oldest packages from mAppStats to make room for new apps.
+ for (int i = 0; i < APP_RECORD_HEADROOM; ++i) {
+ mAppStats.erase(*gpuStatsApps[i].appStatsKey);
+ gpuStatsApps[i].appStatsKey = nullptr;
+ gpuStatsApps[i].lastAccessTime = nullptr;
+ }
+}
+
void GpuStats::insertDriverStats(const std::string& driverPackageName,
const std::string& driverVersionName, uint64_t driverVersionCode,
int64_t driverBuildTime, const std::string& appPackageName,
@@ -123,19 +155,22 @@
const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
if (!mAppStats.count(appStatsKey)) {
if (mAppStats.size() >= MAX_NUM_APP_RECORDS) {
- ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats.");
- return;
+ ALOGV("GpuStatsAppInfo has reached maximum size. Removing old stats to make room.");
+ purgeOldDriverStats();
}
GpuStatsAppInfo appInfo;
addLoadingTime(driver, driverLoadingTime, &appInfo);
appInfo.appPackageName = appPackageName;
appInfo.driverVersionCode = driverVersionCode;
+ appInfo.angleInUse = driverPackageName == "angle";
+ appInfo.lastAccessTime = std::chrono::system_clock::now();
mAppStats.insert({appStatsKey, appInfo});
- return;
+ } else {
+ mAppStats[appStatsKey].angleInUse = driverPackageName == "angle";
+ addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
+ mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
}
-
- addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
}
void GpuStats::insertTargetStats(const std::string& appPackageName,
@@ -311,7 +346,8 @@
angleDriverBytes.length()),
ele.second.cpuVulkanInUse,
ele.second.falsePrerotation,
- ele.second.gles1InUse);
+ ele.second.gles1InUse,
+ ele.second.angleInUse);
}
}
diff --git a/services/gpuservice/gpustats/include/gpustats/GpuStats.h b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
index 55f0da1..2aba651 100644
--- a/services/gpuservice/gpustats/include/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/include/gpustats/GpuStats.h
@@ -46,6 +46,11 @@
// This limits the worst case number of loading times tracked.
static const size_t MAX_NUM_LOADING_TIMES = 50;
+ // Below limits the memory usage of GpuStats to be less than 10KB. This is
+ // the preferred number for statsd while maintaining nice data quality.
+ static const size_t MAX_NUM_APP_RECORDS = 100;
+ // The number of apps to remove when mAppStats fills up.
+ static const size_t APP_RECORD_HEADROOM = 10;
private:
// Friend class for testing.
@@ -55,6 +60,10 @@
static AStatsManager_PullAtomCallbackReturn pullAtomCallback(int32_t atomTag,
AStatsEventList* data,
void* cookie);
+
+ // Remove old packages from mAppStats.
+ void purgeOldDriverStats();
+
// Pull global into into global atom.
AStatsManager_PullAtomCallbackReturn pullGlobalInfoAtom(AStatsEventList* data);
// Pull app into into app atom.
@@ -68,9 +77,6 @@
// Registers statsd callbacks if they have not already been registered
void registerStatsdCallbacksIfNeeded();
- // Below limits the memory usage of GpuStats to be less than 10KB. This is
- // the preferred number for statsd while maintaining nice data quality.
- static const size_t MAX_NUM_APP_RECORDS = 100;
// GpuStats access should be guarded by mLock.
std::mutex mLock;
// True if statsd callbacks have been registered.
diff --git a/services/gpuservice/tests/unittests/Android.bp b/services/gpuservice/tests/unittests/Android.bp
index 6d87c45..5b69f96 100644
--- a/services/gpuservice/tests/unittests/Android.bp
+++ b/services/gpuservice/tests/unittests/Android.bp
@@ -34,7 +34,7 @@
],
shared_libs: [
"libbase",
- "libbpf",
+ "libbpf_bcc",
"libbpf_android",
"libcutils",
"libgfxstats",
diff --git a/services/gpuservice/tests/unittests/GpuStatsTest.cpp b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
index 37ebeae..20c8ccf 100644
--- a/services/gpuservice/tests/unittests/GpuStatsTest.cpp
+++ b/services/gpuservice/tests/unittests/GpuStatsTest.cpp
@@ -17,6 +17,7 @@
#undef LOG_TAG
#define LOG_TAG "gpuservice_unittest"
+#include <unistd.h>
#include <cutils/properties.h>
#include <gmock/gmock.h>
#include <gpustats/GpuStats.h>
@@ -221,6 +222,51 @@
EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
}
+// Verify we always have the most recently used apps in mAppStats, even when we fill it.
+TEST_F(GpuStatsTest, canInsertMoreThanMaxNumAppRecords) {
+ constexpr int kNumExtraApps = 15;
+ static_assert(kNumExtraApps > GpuStats::APP_RECORD_HEADROOM);
+
+ // Insert stats for GpuStats::MAX_NUM_APP_RECORDS so we fill it up.
+ for (int i = 0; i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps; ++i) {
+ std::stringstream nameStream;
+ nameStream << "testapp" << "_" << i;
+ std::string fullPkgName = nameStream.str();
+
+ mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
+ BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME,
+ fullPkgName, VULKAN_VERSION, GpuStatsInfo::Driver::GL, true,
+ DRIVER_LOADING_TIME_1);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::CPU_VULKAN_IN_USE, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::FALSE_PREROTATION, 0);
+ mGpuStats->insertTargetStats(fullPkgName, BUILTIN_DRIVER_VER_CODE,
+ GpuStatsInfo::Stats::GLES_1_IN_USE, 0);
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("cpuVulkanInUse = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("falsePrerotation = 1"));
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr("gles1InUse = 1"));
+ }
+
+ // mAppStats purges GpuStats::APP_RECORD_HEADROOM apps removed everytime it's filled up.
+ int numPurges = kNumExtraApps / GpuStats::APP_RECORD_HEADROOM;
+ numPurges += (kNumExtraApps % GpuStats::APP_RECORD_HEADROOM) == 0 ? 0 : 1;
+
+ // Verify the remaining apps are present.
+ for (int i = numPurges * GpuStats::APP_RECORD_HEADROOM;
+ i < GpuStats::MAX_NUM_APP_RECORDS + kNumExtraApps;
+ ++i) {
+ std::stringstream nameStream;
+ // Add a newline to search for the exact package name.
+ nameStream << "testapp" << "_" << i << "\n";
+ std::string fullPkgName = nameStream.str();
+
+ EXPECT_THAT(inputCommand(InputCommand::DUMP_APP), HasSubstr(fullPkgName.c_str()));
+ }
+}
+
TEST_F(GpuStatsTest, canDumpAllBeforeClearAll) {
mGpuStats->insertDriverStats(BUILTIN_DRIVER_PKG_NAME, BUILTIN_DRIVER_VER_NAME,
BUILTIN_DRIVER_VER_CODE, BUILTIN_DRIVER_BUILD_TIME, APP_PKG_NAME_1,
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 73e5749..8cdb706 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -73,7 +73,6 @@
"libui",
"lib-platform-compat-native-api",
"server_configurable_flags",
- "InputFlingerProperties",
],
static_libs: [
"libattestation",
@@ -125,7 +124,7 @@
"InputListener.cpp",
"InputReaderBase.cpp",
"InputThread.cpp",
- "VibrationElement.cpp"
+ "VibrationElement.cpp",
],
}
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/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 171f2b5..4757d31 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -68,7 +68,6 @@
"libutils",
"lib-platform-compat-native-api",
"server_configurable_flags",
- "InputFlingerProperties",
],
static_libs: [
"libattestation",
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..936ecf9 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -32,8 +32,8 @@
return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source,
entry.displayId},
entry.action,
- entry.downTime,
entry.flags & VERIFIED_KEY_EVENT_FLAGS,
+ entry.downTime,
entry.keyCode,
entry.scanCode,
entry.metaState,
@@ -50,8 +50,8 @@
rawXY.x,
rawXY.y,
actionMasked,
- entry.downTime,
entry.flags & VERIFIED_MOTION_EVENT_FLAGS,
+ entry.downTime,
entry.metaState,
entry.buttonState};
}
@@ -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 176cf89..6ff2450 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -19,7 +19,6 @@
#define LOG_NDEBUG 1
-#include <InputFlingerProperties.sysprop.h>
#include <android-base/chrono_utils.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
@@ -112,15 +111,6 @@
std::mutex& mMutex;
};
-// When per-window-input-rotation is enabled, InputFlinger works in the un-rotated display
-// coordinates and SurfaceFlinger includes the display rotation in the input window transforms.
-bool isPerWindowInputRotationEnabled() {
- static const bool PER_WINDOW_INPUT_ROTATION =
- sysprop::InputFlingerProperties::per_window_input_rotation().value_or(true);
-
- return PER_WINDOW_INPUT_ROTATION;
-}
-
// Default input dispatching timeout if there is no focused application or paused window
// from which to determine an appropriate dispatching timeout.
const std::chrono::duration DEFAULT_INPUT_DISPATCHING_TIMEOUT = std::chrono::milliseconds(
@@ -342,18 +332,6 @@
std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
std::shared_ptr<EventEntry> eventEntry,
int32_t inputTargetFlags) {
- if (eventEntry->type == EventEntry::Type::MOTION) {
- const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) ||
- (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) {
- const ui::Transform identityTransform;
- // Use identity transform for joystick and position-based (touchpad) events because they
- // don't depend on the window transform.
- return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, identityTransform,
- identityTransform, 1.0f /*globalScaleFactor*/);
- }
- }
-
if (inputTarget.useDefaultPointerTransform()) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
@@ -528,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);
@@ -555,6 +529,23 @@
}
}
+// Returns true if the given window can accept pointer events at the given display location.
+bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, int32_t x, int32_t y) {
+ if (windowInfo.displayId != displayId || !windowInfo.visible) {
+ return false;
+ }
+ const auto flags = windowInfo.flags;
+ if (flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
+ return false;
+ }
+ const bool isModalWindow = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
+ !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ if (!isModalWindow && !windowInfo.touchableRegionContainsPoint(x, y)) {
+ return false;
+ }
+ return true;
+}
+
} // namespace
// --- InputDispatcher ---
@@ -1060,36 +1051,46 @@
LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
}
// Traverse windows from front to back to find touched window.
- const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
+ const auto& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
continue;
}
- const WindowInfo* windowInfo = windowHandle->getInfo();
- if (windowInfo->displayId == displayId) {
- auto flags = windowInfo->flags;
- if (windowInfo->visible) {
- if (!flags.test(WindowInfo::Flag::NOT_TOUCHABLE)) {
- bool isTouchModal = !flags.test(WindowInfo::Flag::NOT_FOCUSABLE) &&
- !flags.test(WindowInfo::Flag::NOT_TOUCH_MODAL);
- if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) {
- // Found window.
- return windowHandle;
- }
- }
+ const WindowInfo& info = *windowHandle->getInfo();
+ if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y)) {
+ return windowHandle;
+ }
- if (addOutsideTargets && flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
- touchState->addOrUpdateWindow(windowHandle,
- InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
- BitSet32(0));
- }
- }
+ if (addOutsideTargets && info.flags.test(WindowInfo::Flag::WATCH_OUTSIDE_TOUCH)) {
+ touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
+ BitSet32(0));
}
}
return nullptr;
}
+std::vector<sp<WindowInfoHandle>> InputDispatcher::findTouchedSpyWindowsAtLocked(int32_t displayId,
+ int32_t x,
+ int32_t y) const {
+ // Traverse windows from front to back and gather the touched spy windows.
+ std::vector<sp<WindowInfoHandle>> spyWindows;
+ const auto& windowHandles = getWindowHandlesLocked(displayId);
+ for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
+ const WindowInfo& info = *windowHandle->getInfo();
+
+ if (!windowAcceptsTouchAt(info, displayId, x, y)) {
+ continue;
+ }
+ if (!info.isSpy()) {
+ // The first touched non-spy window was found, so return the spy windows touched so far.
+ return spyWindows;
+ }
+ spyWindows.push_back(windowHandle);
+ }
+ return spyWindows;
+}
+
void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason dropReason) {
const char* reason;
switch (dropReason) {
@@ -1775,7 +1776,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);
@@ -1985,9 +1986,9 @@
// For security reasons, we defer updating the touch state until we are sure that
// event injection will be allowed.
- int32_t displayId = entry.displayId;
- int32_t action = entry.action;
- int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
+ const int32_t displayId = entry.displayId;
+ const int32_t action = entry.action;
+ const int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK;
// Update the touch state as needed based on the properties of the touch event.
InputEventInjectionResult injectionResult = InputEventInjectionResult::PENDING;
@@ -2000,10 +2001,8 @@
// If no state for the specified display exists, then our initial state will be empty.
const TouchState* oldState = nullptr;
TouchState tempTouchState;
- std::unordered_map<int32_t, TouchState>::iterator oldStateIt =
- mTouchStatesByDisplay.find(displayId);
- if (oldStateIt != mTouchStatesByDisplay.end()) {
- oldState = &(oldStateIt->second);
+ if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
+ oldState = &(it->second);
tempTouchState.copyFrom(*oldState);
}
@@ -2011,11 +2010,12 @@
bool switchedDevice = tempTouchState.deviceId >= 0 && tempTouchState.displayId >= 0 &&
(tempTouchState.deviceId != entry.deviceId || tempTouchState.source != entry.source ||
tempTouchState.displayId != displayId);
- bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
- maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
- bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
- maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
+
+ const bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
+ maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
+ const bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN ||
+ maskedAction == AMOTION_EVENT_ACTION_SCROLL || isHoverAction);
const bool isFromMouse = isFromSource(entry.source, AINPUT_SOURCE_MOUSE);
bool wrongDevice = false;
if (newGesture) {
@@ -2052,7 +2052,7 @@
int32_t x;
int32_t y;
- int32_t pointerIndex = getMotionEventActionPointerIndex(action);
+ const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
// Always dispatch mouse events to cursor position.
if (isFromMouse) {
x = int32_t(entry.xCursorPosition);
@@ -2065,17 +2065,6 @@
newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
isDown /*addOutsideTargets*/);
- // Figure out whether splitting will be allowed for this window.
- if (newTouchedWindowHandle != nullptr &&
- newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- // New window supports splitting, but we should never split mouse events.
- isSplit = !isFromMouse;
- } else if (isSplit) {
- // New window does not support splitting but we have already split events.
- // Ignore the new window.
- newTouchedWindowHandle = nullptr;
- }
-
// Handle the case where we did not find a window.
if (newTouchedWindowHandle == nullptr) {
ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
@@ -2084,79 +2073,97 @@
newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
}
- if (newTouchedWindowHandle != nullptr && newTouchedWindowHandle->getInfo()->paused) {
- ALOGI("Not sending touch event to %s because it is paused",
- newTouchedWindowHandle->getName().c_str());
- newTouchedWindowHandle = nullptr;
- }
-
- // Ensure the window has a connection and the connection is responsive
+ // Figure out whether splitting will be allowed for this window.
if (newTouchedWindowHandle != nullptr) {
- const bool isResponsive = hasResponsiveConnectionLocked(*newTouchedWindowHandle);
- if (!isResponsive) {
- ALOGW("%s will not receive the new gesture at %" PRIu64,
- newTouchedWindowHandle->getName().c_str(), entry.eventTime);
+ if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
+ // New window supports splitting, but we should never split mouse events.
+ isSplit = !isFromMouse;
+ } else if (isSplit) {
+ // New window does not support splitting but we have already split events.
+ // Ignore the new window.
newTouchedWindowHandle = nullptr;
}
+ } else {
+ // No window is touched, so set split to true. This will allow the next pointer down to
+ // be delivered to a new window which supports split touch. Pointers from a mouse device
+ // should never be split.
+ tempTouchState.split = isSplit = !isFromMouse;
}
- // Drop events that can't be trusted due to occlusion
- if (newTouchedWindowHandle != nullptr &&
- mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
- TouchOcclusionInfo occlusionInfo =
- computeTouchOcclusionInfoLocked(newTouchedWindowHandle, x, y);
- if (!isTouchTrustedLocked(occlusionInfo)) {
- if (DEBUG_TOUCH_OCCLUSION) {
- ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
- for (const auto& log : occlusionInfo.debugInfo) {
- ALOGD("%s", log.c_str());
- }
- }
- sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
- if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
- ALOGW("Dropping untrusted touch event due to %s/%d",
- occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
- newTouchedWindowHandle = nullptr;
- }
- }
- }
-
- // Drop touch events if requested by input feature
- if (newTouchedWindowHandle != nullptr && shouldDropInput(entry, newTouchedWindowHandle)) {
- newTouchedWindowHandle = nullptr;
- }
-
- const std::vector<Monitor> newGestureMonitors = isDown
- ? selectResponsiveMonitorsLocked(
- getValueByKey(mGestureMonitorsByDisplay, displayId))
- : std::vector<Monitor>{};
-
- if (newTouchedWindowHandle == nullptr && newGestureMonitors.empty()) {
- ALOGI("Dropping event because there is no touchable window or gesture monitor at "
- "(%d, %d) in display %" PRId32 ".",
- x, y, displayId);
- injectionResult = InputEventInjectionResult::FAILED;
- goto Failed;
- }
-
+ // Update hover state.
if (newTouchedWindowHandle != nullptr) {
- // Set target flags.
- int32_t targetFlags = InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS;
- if (isSplit) {
- targetFlags |= InputTarget::FLAG_SPLIT;
- }
- if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
- } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
- targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
- }
-
- // Update hover state.
if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
newHoverWindowHandle = nullptr;
} else if (isHoverAction) {
newHoverWindowHandle = newTouchedWindowHandle;
}
+ }
+
+ std::vector<sp<WindowInfoHandle>> newTouchedWindows =
+ findTouchedSpyWindowsAtLocked(displayId, x, y);
+ if (newTouchedWindowHandle != nullptr) {
+ // Process the foreground window first so that it is the first to receive the event.
+ newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
+ }
+
+ for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
+ const WindowInfo& info = *windowHandle->getInfo();
+
+ if (info.paused) {
+ ALOGI("Not sending touch event to %s because it is paused",
+ windowHandle->getName().c_str());
+ continue;
+ }
+
+ // Ensure the window has a connection and the connection is responsive
+ const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle);
+ if (!isResponsive) {
+ ALOGW("Not sending touch gesture to %s because it is not responsive",
+ windowHandle->getName().c_str());
+ continue;
+ }
+
+ // Drop events that can't be trusted due to occlusion
+ if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
+ TouchOcclusionInfo occlusionInfo =
+ computeTouchOcclusionInfoLocked(windowHandle, x, y);
+ if (!isTouchTrustedLocked(occlusionInfo)) {
+ if (DEBUG_TOUCH_OCCLUSION) {
+ ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
+ for (const auto& log : occlusionInfo.debugInfo) {
+ ALOGD("%s", log.c_str());
+ }
+ }
+ sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
+ if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
+ ALOGW("Dropping untrusted touch event due to %s/%d",
+ occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
+ continue;
+ }
+ }
+ }
+
+ // Drop touch events if requested by input feature
+ if (shouldDropInput(entry, windowHandle)) {
+ continue;
+ }
+
+ // Set target flags.
+ int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;
+
+ if (!info.isSpy()) {
+ // There should only be one new foreground (non-spy) window at this location.
+ targetFlags |= InputTarget::FLAG_FOREGROUND;
+ }
+
+ if (isSplit) {
+ targetFlags |= InputTarget::FLAG_SPLIT;
+ }
+ if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
+ } else if (isWindowObscuredLocked(windowHandle)) {
+ targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
+ }
// Update the temporary touch state.
BitSet32 pointerIds;
@@ -2164,10 +2171,26 @@
uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
pointerIds.markBit(pointerId);
}
- tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
+
+ tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
+ }
+
+ const std::vector<Monitor> newGestureMonitors = isDown
+ ? selectResponsiveMonitorsLocked(
+ getValueByKey(mGestureMonitorsByDisplay, displayId))
+ : std::vector<Monitor>{};
+
+ if (newTouchedWindows.empty() && newGestureMonitors.empty() &&
+ tempTouchState.gestureMonitors.empty()) {
+ ALOGI("Dropping event because there is no touchable window or gesture monitor at "
+ "(%d, %d) in display %" PRId32 ".",
+ x, y, displayId);
+ injectionResult = InputEventInjectionResult::FAILED;
+ goto Failed;
}
tempTouchState.addGestureMonitors(newGestureMonitors);
+
} else {
/* Case 2: Pointer move, up, cancel or non-splittable pointer down. */
@@ -2187,8 +2210,8 @@
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
- int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
- int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<WindowInfoHandle> oldTouchedWindowHandle =
tempTouchState.getFirstForegroundWindowHandle();
@@ -2214,7 +2237,7 @@
// Make a slippery entrance into the new window.
if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
- isSplit = true;
+ isSplit = !isFromMouse;
}
int32_t targetFlags =
@@ -2237,9 +2260,10 @@
}
}
+ // Update dispatching for hover enter and exit.
if (newHoverWindowHandle != mLastHoverWindowHandle) {
- // Let the previous window know that the hover sequence is over, unless we already did it
- // when dispatching it as is to newTouchedWindowHandle.
+ // Let the previous window know that the hover sequence is over, unless we already did
+ // it when dispatching it as is to newTouchedWindowHandle.
if (mLastHoverWindowHandle != nullptr &&
(maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
mLastHoverWindowHandle != newTouchedWindowHandle)) {
@@ -2269,10 +2293,17 @@
// Check permission to inject into all touched foreground windows and ensure there
// is at least one touched foreground window.
{
- bool haveForegroundWindow = false;
+ bool haveForegroundOrSpyWindow = false;
for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
- if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) {
- haveForegroundWindow = true;
+ const bool isForeground =
+ (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
+ if (touchedWindow.windowHandle->getInfo()->isSpy()) {
+ haveForegroundOrSpyWindow = true;
+ LOG_ALWAYS_FATAL_IF(isForeground,
+ "Spy window cannot be dispatched as a foreground window.");
+ }
+ if (isForeground) {
+ haveForegroundOrSpyWindow = true;
if (!checkInjectionPermission(touchedWindow.windowHandle, entry.injectionState)) {
injectionResult = InputEventInjectionResult::PERMISSION_DENIED;
injectionPermission = INJECTION_PERMISSION_DENIED;
@@ -2281,8 +2312,8 @@
}
}
bool hasGestureMonitor = !tempTouchState.gestureMonitors.empty();
- if (!haveForegroundWindow && !hasGestureMonitor) {
- ALOGI("Dropping event because there is no touched foreground window in display "
+ if (!haveForegroundOrSpyWindow && !hasGestureMonitor) {
+ ALOGI("Dropping event because there is no touched window in display "
"%" PRId32 " or gesture monitor to receive it.",
displayId);
injectionResult = InputEventInjectionResult::FAILED;
@@ -2531,8 +2562,7 @@
if (displayInfoIt != mDisplayInfos.end()) {
inputTarget.displayTransform = displayInfoIt->second.transform;
} else {
- ALOGI_IF(isPerWindowInputRotationEnabled(),
- "DisplayInfo not found for window on display: %d", windowInfo->displayId);
+ ALOGE("DisplayInfo not found for window on display: %d", windowInfo->displayId);
}
inputTargets.push_back(inputTarget);
it = inputTargets.end() - 1;
@@ -2852,10 +2882,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;
}
@@ -3169,7 +3200,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 =
@@ -3257,8 +3288,7 @@
const FocusEntry& focusEntry = static_cast<const FocusEntry&>(eventEntry);
status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq,
focusEntry.id,
- focusEntry.hasFocus,
- mInTouchMode);
+ focusEntry.hasFocus);
break;
}
@@ -3391,8 +3421,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;
}
@@ -3419,8 +3449,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.
@@ -3428,7 +3458,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());
};
@@ -3564,7 +3593,7 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const sp<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -3637,7 +3666,7 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const sp<Connection>& connection) {
- if (connection->status == Connection::STATUS_BROKEN) {
+ if (connection->status == Connection::Status::BROKEN) {
return;
}
@@ -4717,8 +4746,10 @@
if (wallpaper != nullptr) {
sp<Connection> wallpaperConnection =
getConnectionLocked(wallpaper->getToken());
- synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
- options);
+ if (wallpaperConnection != nullptr) {
+ synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
+ options);
+ }
}
}
}
@@ -4737,21 +4768,19 @@
}
}
- if (isPerWindowInputRotationEnabled()) {
- // Determine if the orientation of any of the input windows have changed, and cancel all
- // pointer events if necessary.
- for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
- const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
- if (newWindowHandle != nullptr &&
- newWindowHandle->getInfo()->transform.getOrientation() !=
- oldWindowOrientations[oldWindowHandle->getId()]) {
- std::shared_ptr<InputChannel> inputChannel =
- getInputChannelLocked(newWindowHandle->getToken());
- if (inputChannel != nullptr) {
- CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "touched window's orientation changed");
- synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
- }
+ // Determine if the orientation of any of the input windows have changed, and cancel all
+ // pointer events if necessary.
+ for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
+ const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
+ if (newWindowHandle != nullptr &&
+ newWindowHandle->getInfo()->transform.getOrientation() !=
+ oldWindowOrientations[oldWindowHandle->getId()]) {
+ std::shared_ptr<InputChannel> inputChannel =
+ getInputChannelLocked(newWindowHandle->getToken());
+ if (inputChannel != nullptr) {
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "touched window's orientation changed");
+ synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
}
}
}
@@ -5316,7 +5345,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()) {
@@ -5489,7 +5519,7 @@
nsecs_t currentTime = now();
abortBrokenDispatchCycleLocked(currentTime, connection, notify);
- connection->status = Connection::STATUS_ZOMBIE;
+ connection->status = Connection::Status::ZOMBIE;
return OK;
}
@@ -5523,57 +5553,78 @@
status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
{ // acquire lock
std::scoped_lock _l(mLock);
- std::optional<int32_t> foundDisplayId = findGestureMonitorDisplayByTokenLocked(token);
- if (!foundDisplayId) {
- ALOGW("Attempted to pilfer pointers from an un-registered monitor or invalid token");
- return BAD_VALUE;
- }
- int32_t displayId = foundDisplayId.value();
-
- std::unordered_map<int32_t, TouchState>::iterator stateIt =
- mTouchStatesByDisplay.find(displayId);
- if (stateIt == mTouchStatesByDisplay.end()) {
- ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
- return BAD_VALUE;
- }
-
- TouchState& state = stateIt->second;
+ TouchState* statePtr = nullptr;
std::shared_ptr<InputChannel> requestingChannel;
- std::optional<int32_t> foundDeviceId;
- for (const auto& monitor : state.gestureMonitors) {
- if (monitor.inputChannel->getConnectionToken() == token) {
- requestingChannel = monitor.inputChannel;
- foundDeviceId = state.deviceId;
+ int32_t displayId;
+ int32_t deviceId;
+ const std::optional<int32_t> foundGestureMonitorDisplayId =
+ findGestureMonitorDisplayByTokenLocked(token);
+
+ // TODO: Optimize this function for pilfering from windows when removing gesture monitors.
+ if (foundGestureMonitorDisplayId) {
+ // A gesture monitor has requested to pilfer pointers.
+ displayId = *foundGestureMonitorDisplayId;
+ auto stateIt = mTouchStatesByDisplay.find(displayId);
+ if (stateIt == mTouchStatesByDisplay.end()) {
+ ALOGW("Failed to pilfer pointers: no pointers on display %" PRId32 ".", displayId);
+ return BAD_VALUE;
+ }
+ statePtr = &stateIt->second;
+
+ for (const auto& monitor : statePtr->gestureMonitors) {
+ if (monitor.inputChannel->getConnectionToken() == token) {
+ requestingChannel = monitor.inputChannel;
+ deviceId = statePtr->deviceId;
+ }
+ }
+ } else {
+ // Check if a window has requested to pilfer pointers.
+ for (auto& [curDisplayId, state] : mTouchStatesByDisplay) {
+ const sp<WindowInfoHandle>& windowHandle = state.getWindow(token);
+ if (windowHandle != nullptr) {
+ displayId = curDisplayId;
+ requestingChannel = getInputChannelLocked(token);
+ deviceId = state.deviceId;
+ statePtr = &state;
+ break;
+ }
}
}
- if (!foundDeviceId || !state.down) {
- ALOGW("Attempted to pilfer points from a monitor without any on-going pointer streams."
+
+ if (requestingChannel == nullptr) {
+ ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
+ return BAD_VALUE;
+ }
+ TouchState& state = *statePtr;
+ if (!state.down) {
+ ALOGW("Attempted to pilfer points from a channel without any on-going pointer streams."
" Ignoring.");
return BAD_VALUE;
}
- int32_t deviceId = foundDeviceId.value();
// Send cancel events to all the input channels we're stealing from.
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
- "gesture monitor stole pointer stream");
+ "input channel stole pointer stream");
options.deviceId = deviceId;
options.displayId = displayId;
- std::string canceledWindows = "[";
+ std::string canceledWindows;
for (const TouchedWindow& window : state.windows) {
std::shared_ptr<InputChannel> channel =
getInputChannelLocked(window.windowHandle->getToken());
- if (channel != nullptr) {
+ if (channel != nullptr && channel->getConnectionToken() != token) {
synthesizeCancelationEventsForInputChannelLocked(channel, options);
- canceledWindows += channel->getName() + ", ";
+ canceledWindows += canceledWindows.empty() ? "[" : ", ";
+ canceledWindows += channel->getName();
}
}
- canceledWindows += "]";
- ALOGI("Monitor %s is stealing touch from %s", requestingChannel->getName().c_str(),
+ canceledWindows += canceledWindows.empty() ? "[]" : "]";
+ ALOGI("Channel %s is stealing touch from %s", requestingChannel->getName().c_str(),
canceledWindows.c_str());
// Then clear the current touch state so we stop dispatching to them as well.
- state.filterNonMonitors();
+ state.split = false;
+ state.filterWindowsExcept(token);
}
return OK;
}
@@ -5708,7 +5759,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 {
@@ -6006,7 +6057,7 @@
mLock.lock();
- if (connection->status != Connection::STATUS_NORMAL) {
+ if (connection->status != Connection::Status::NORMAL) {
connection->inputState.removeFallbackKey(originalKeyCode);
return false;
}
@@ -6320,4 +6371,19 @@
mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
}
+void InputDispatcher::cancelCurrentTouch() {
+ {
+ std::scoped_lock _l(mLock);
+ ALOGD("Canceling all ongoing pointer gestures on all displays.");
+ CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
+ "cancel current touch");
+ synthesizeCancelationEventsForAllConnectionsLocked(options);
+
+ mTouchStatesByDisplay.clear();
+ mLastHoverWindowHandle.clear();
+ }
+ // Wake up poll loop since there might be work to do.
+ mLooper->wake();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 8a551cf..fb9c81b 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -146,6 +146,8 @@
void onWindowInfosChanged(const std::vector<android::gui::WindowInfo>& windowInfos,
const std::vector<android::gui::DisplayInfo>& displayInfos);
+ void cancelCurrentTouch() override;
+
private:
enum class DropReason {
NOT_DROPPED,
@@ -238,6 +240,11 @@
bool ignoreDragWindow = false)
REQUIRES(mLock);
+ std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(int32_t displayId,
+ int32_t x,
+ int32_t y) const
+ REQUIRES(mLock);
+
sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 759b3e7..d624e99 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -105,8 +105,11 @@
}
}
-void TouchState::filterNonMonitors() {
- windows.clear();
+void TouchState::filterWindowsExcept(const sp<IBinder>& token) {
+ auto it = std::remove_if(windows.begin(), windows.end(), [&token](const TouchedWindow& w) {
+ return w.windowHandle->getToken() != token;
+ });
+ windows.erase(it, windows.end());
}
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
@@ -144,4 +147,14 @@
return nullptr;
}
+sp<WindowInfoHandle> TouchState::getWindow(const sp<IBinder>& token) const {
+ for (const TouchedWindow& touchedWindow : windows) {
+ const auto& windowHandle = touchedWindow.windowHandle;
+ if (windowHandle->getToken() == token) {
+ return windowHandle;
+ }
+ }
+ return nullptr;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 4a62051..83ca901 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -44,14 +44,14 @@
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const sp<android::gui::WindowInfoHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
- void addPortalWindow(const sp<android::gui::WindowInfoHandle>& windowHandle);
void addGestureMonitors(const std::vector<Monitor>& monitors);
void removeWindowByToken(const sp<IBinder>& token);
void filterNonAsIsTouchWindows();
- void filterNonMonitors();
+ void filterWindowsExcept(const sp<IBinder>& token);
sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
+ sp<android::gui::WindowInfoHandle> getWindow(const sp<IBinder>&) const;
};
} // namespace inputdispatcher
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 714e7a0..c469ec3 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -211,6 +211,11 @@
* Called when a display has been removed from the system.
*/
virtual void displayRemoved(int32_t displayId) = 0;
+
+ /*
+ * Abort the current touch stream.
+ */
+ virtual void cancelCurrentTouch() = 0;
};
} // namespace android
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/Android.bp b/services/inputflinger/reader/Android.bp
index ee7b392..51546ce 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -71,7 +71,6 @@
"libstatslog",
"libui",
"libutils",
- "InputFlingerProperties",
],
static_libs: [
"libc++fs",
@@ -86,7 +85,7 @@
name: "libinputreader",
defaults: [
"inputflinger_defaults",
- "libinputreader_defaults"
+ "libinputreader_defaults",
],
srcs: [
"InputReaderFactory.cpp",
@@ -100,6 +99,6 @@
"libinputreader_headers",
],
static_libs: [
- "libc++fs"
+ "libc++fs",
],
}
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index d07be3b..7cff672 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -379,21 +379,22 @@
// gamepad button presses are handled by different mappers but they should be dispatched
// in the order received.
for (const RawEvent* rawEvent = rawEvents; count != 0; rawEvent++) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
- rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, rawEvent->when);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%" PRId64,
+ rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value,
+ rawEvent->when);
+ }
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
mDropUntilNextSync = false;
-#if DEBUG_RAW_EVENTS
- ALOGD("Recovered from input event buffer overrun.");
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Recovered from input event buffer overrun.");
+ }
} else {
-#if DEBUG_RAW_EVENTS
- ALOGD("Dropped input event while waiting for next input sync.");
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Dropped input event while waiting for next input sync.");
+ }
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 0b632f7..7704a84 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -113,9 +113,9 @@
if (mNextTimeout != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
if (now >= mNextTimeout) {
-#if DEBUG_RAW_EVENTS
- ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f);
+ }
mNextTimeout = LLONG_MAX;
timeoutExpiredLocked(now);
}
@@ -155,9 +155,9 @@
}
batchSize += 1;
}
-#if DEBUG_RAW_EVENTS
- ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("BatchSize: %zu Count: %zu", batchSize, count);
+ }
processEventsForDeviceLocked(deviceId, rawEvent, batchSize);
} else {
switch (rawEvent->type) {
@@ -782,12 +782,8 @@
std::optional<int32_t> associatedDisplayId = device->getAssociatedDisplayId();
// No associated display. By default, can dispatch to all displays.
- if (!associatedDisplayId) {
- return true;
- }
-
- if (*associatedDisplayId == ADISPLAY_ID_NONE) {
- ALOGW("Device %s is associated with display ADISPLAY_ID_NONE.", device->getName().c_str());
+ if (!associatedDisplayId ||
+ *associatedDisplayId == ADISPLAY_ID_NONE) {
return true;
}
diff --git a/services/inputflinger/reader/Macros.h b/services/inputflinger/reader/Macros.h
index 0dfe7f1..d837689 100644
--- a/services/inputflinger/reader/Macros.h
+++ b/services/inputflinger/reader/Macros.h
@@ -22,28 +22,25 @@
//#define LOG_NDEBUG 0
// Log debug messages for each raw event received from the EventHub.
-#define DEBUG_RAW_EVENTS 0
-
-// Log debug messages about touch screen filtering hacks.
-#define DEBUG_HACKS 0
+static constexpr bool DEBUG_RAW_EVENTS = false;
// Log debug messages about virtual key processing.
-#define DEBUG_VIRTUAL_KEYS 0
+static constexpr bool DEBUG_VIRTUAL_KEYS = false;
// Log debug messages about pointers.
static constexpr bool DEBUG_POINTERS = false;
// Log debug messages about pointer assignment calculations.
-#define DEBUG_POINTER_ASSIGNMENT 0
+static constexpr bool DEBUG_POINTER_ASSIGNMENT = false;
// Log debug messages about gesture detection.
-#define DEBUG_GESTURES 0
+static constexpr bool DEBUG_GESTURES = false;
// Log debug messages about the vibrator.
-#define DEBUG_VIBRATOR 0
+static constexpr bool DEBUG_VIBRATOR = false;
// Log debug messages about fusing stylus data.
-#define DEBUG_STYLUS_FUSION 0
+static constexpr bool DEBUG_STYLUS_FUSION = false;
#define INDENT " "
#define INDENT2 " "
diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
index f3d7cdc..fcb56ef 100644
--- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp
@@ -188,33 +188,19 @@
if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) {
mOrientation = DISPLAY_ORIENTATION_0;
- mDisplayWidth = 0;
- mDisplayHeight = 0;
const bool isOrientedDevice =
(mParameters.orientationAware && mParameters.hasAssociatedDisplay);
- if (isPerWindowInputRotationEnabled()) {
- // When per-window input rotation is enabled, InputReader works in the un-rotated
- // coordinate space, so we don't need to do anything if the device is already
- // orientation-aware. If the device is not orientation-aware, then we need to apply the
- // inverse rotation of the display so that when the display rotation is applied later
- // as a part of the per-window transform, we get the expected screen coordinates.
- if (!isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = getInverseRotation(internalViewport->orientation);
- mDisplayWidth = internalViewport->deviceWidth;
- mDisplayHeight = internalViewport->deviceHeight;
- }
- }
- } else {
- if (isOrientedDevice) {
- std::optional<DisplayViewport> internalViewport =
- config->getDisplayViewportByType(ViewportType::INTERNAL);
- if (internalViewport) {
- mOrientation = internalViewport->orientation;
- }
+ // InputReader works in the un-rotated display coordinate space, so we don't need to do
+ // anything if the device is already orientation-aware. If the device is not
+ // orientation-aware, then we need to apply the inverse rotation of the display so that
+ // when the display rotation is applied later as a part of the per-window transform, we
+ // get the expected screen coordinates.
+ if (!isOrientedDevice) {
+ std::optional<DisplayViewport> internalViewport =
+ config->getDisplayViewportByType(ViewportType::INTERNAL);
+ if (internalViewport) {
+ mOrientation = getInverseRotation(internalViewport->orientation);
}
}
@@ -345,15 +331,7 @@
mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
if (moved) {
- float dx = deltaX;
- float dy = deltaY;
- if (isPerWindowInputRotationEnabled()) {
- // 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) {
@@ -364,12 +342,7 @@
}
mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
- if (isPerWindowInputRotationEnabled()) {
- // 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);
pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
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 197be98..31a3d2e 100644
--- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
+++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h
@@ -17,7 +17,6 @@
#ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
#define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H
-#include <InputFlingerProperties.sysprop.h>
#include <input/DisplayViewport.h>
#include <stdint.h>
@@ -29,13 +28,6 @@
// --- Static Definitions ---
-// When per-window input rotation is enabled, display transformations such as rotation and
-// projection are part of the input window's transform. This means InputReader should work in the
-// un-rotated coordinate space.
-static bool isPerWindowInputRotationEnabled() {
- return sysprop::InputFlingerProperties::per_window_input_rotation().value_or(true);
-}
-
static int32_t getInverseRotation(int32_t orientation) {
switch (orientation) {
case DISPLAY_ORIENTATION_90:
@@ -72,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 fd33df9..6f49f31 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -168,17 +168,13 @@
: InputMapper(deviceContext),
mSource(0),
mDeviceMode(DeviceMode::DISABLED),
- mRawSurfaceWidth(-1),
- mRawSurfaceHeight(-1),
- mSurfaceLeft(0),
- mSurfaceTop(0),
- mSurfaceRight(0),
- mSurfaceBottom(0),
+ mDisplayWidth(-1),
+ mDisplayHeight(-1),
mPhysicalWidth(-1),
mPhysicalHeight(-1),
mPhysicalLeft(0),
mPhysicalTop(0),
- mSurfaceOrientation(DISPLAY_ORIENTATION_0) {}
+ mInputDeviceOrientation(DISPLAY_ORIENTATION_0) {}
TouchInputMapper::~TouchInputMapper() {}
@@ -266,11 +262,9 @@
dumpRawPointerAxes(dump);
dumpCalibration(dump);
dumpAffineTransformation(dump);
- dumpSurface(dump);
+ dumpDisplay(dump);
dump += StringPrintf(INDENT3 "Translation and Scaling Factors:\n");
- dump += StringPrintf(INDENT4 "XTranslate: %0.3f\n", mXTranslate);
- dump += StringPrintf(INDENT4 "YTranslate: %0.3f\n", mYTranslate);
dump += StringPrintf(INDENT4 "XScale: %0.3f\n", mXScale);
dump += StringPrintf(INDENT4 "YScale: %0.3f\n", mYScale);
dump += StringPrintf(INDENT4 "XPrecision: %0.3f\n", mXPrecision);
@@ -390,9 +384,9 @@
InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT |
InputReaderConfiguration::CHANGE_SHOW_TOUCHES |
InputReaderConfiguration::CHANGE_EXTERNAL_STYLUS_PRESENCE))) {
- // Configure device sources, surface dimensions, orientation and
+ // Configure device sources, display dimensions, orientation and
// scaling factors.
- configureSurface(when, &resetNeeded);
+ configureInputDevice(when, &resetNeeded);
}
if (changes && resetNeeded) {
@@ -615,7 +609,7 @@
return std::make_optional(newViewport);
}
-void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) {
+void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) {
DeviceMode oldDeviceMode = mDeviceMode;
resolveExternalStylusPresence();
@@ -673,31 +667,28 @@
}
// Raw width and height in the natural orientation.
- int32_t rawWidth = mRawPointerAxes.getRawWidth();
- int32_t rawHeight = mRawPointerAxes.getRawHeight();
+ const int32_t rawWidth = mRawPointerAxes.getRawWidth();
+ const int32_t rawHeight = mRawPointerAxes.getRawHeight();
- bool viewportChanged = mViewport != *newViewport;
+ const bool viewportChanged = mViewport != *newViewport;
bool skipViewportUpdate = false;
if (viewportChanged) {
- bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
+ const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation;
mViewport = *newViewport;
if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) {
- // Convert rotated viewport to natural surface coordinates.
- int32_t naturalLogicalWidth, naturalLogicalHeight;
+ // Convert rotated viewport to the natural orientation.
int32_t naturalPhysicalWidth, naturalPhysicalHeight;
int32_t naturalPhysicalLeft, naturalPhysicalTop;
int32_t naturalDeviceWidth, naturalDeviceHeight;
- // Apply the inverse of the input device orientation so that the surface is configured
- // in the same orientation as the device. The input device orientation will be
- // re-applied to mSurfaceOrientation.
- const int32_t naturalSurfaceOrientation =
+ // Apply the inverse of the input device orientation so that the input device is
+ // configured in the same orientation as the viewport. The input device orientation will
+ // be re-applied by mInputDeviceOrientation.
+ const int32_t naturalDeviceOrientation =
(mViewport.orientation - static_cast<int32_t>(mParameters.orientation) + 4) % 4;
- switch (naturalSurfaceOrientation) {
+ switch (naturalDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom;
@@ -706,8 +697,6 @@
naturalDeviceHeight = mViewport.deviceWidth;
break;
case DISPLAY_ORIENTATION_180:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight;
@@ -716,8 +705,6 @@
naturalDeviceHeight = mViewport.deviceHeight;
break;
case DISPLAY_ORIENTATION_270:
- naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop;
- naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft;
naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalLeft = mViewport.physicalTop;
@@ -727,8 +714,6 @@
break;
case DISPLAY_ORIENTATION_0:
default:
- naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft;
- naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop;
naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft;
naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop;
naturalPhysicalLeft = mViewport.physicalLeft;
@@ -749,58 +734,36 @@
mPhysicalLeft = naturalPhysicalLeft;
mPhysicalTop = naturalPhysicalTop;
- if (isPerWindowInputRotationEnabled()) {
- // When per-window input rotation is enabled, InputReader works in the display
- // space, so the surface bounds are the bounds of the display device.
- const int32_t oldSurfaceWidth = mRawSurfaceWidth;
- const int32_t oldSurfaceHeight = mRawSurfaceHeight;
- mRawSurfaceWidth = naturalDeviceWidth;
- mRawSurfaceHeight = naturalDeviceHeight;
- mSurfaceLeft = 0;
- mSurfaceTop = 0;
- mSurfaceRight = mRawSurfaceWidth;
- mSurfaceBottom = mRawSurfaceHeight;
- // When per-window input rotation is enabled, InputReader works in the un-rotated
- // coordinate space, so we don't need to do anything if the device is already
- // orientation-aware. If the device is not orientation-aware, then we need to apply
- // the inverse rotation of the display so that when the display rotation is applied
- // later as a part of the per-window transform, we get the expected screen
- // coordinates.
- mSurfaceOrientation = mParameters.orientationAware
- ? DISPLAY_ORIENTATION_0
- : getInverseRotation(mViewport.orientation);
- // For orientation-aware devices that work in the un-rotated coordinate space, the
- // viewport update should be skipped if it is only a change in the orientation.
- skipViewportUpdate = mParameters.orientationAware &&
- mRawSurfaceWidth == oldSurfaceWidth &&
- mRawSurfaceHeight == oldSurfaceHeight && viewportOrientationChanged;
- } else {
- mRawSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth;
- mRawSurfaceHeight =
- naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight;
- mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth;
- mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight;
- mSurfaceRight = mSurfaceLeft + naturalLogicalWidth;
- mSurfaceBottom = mSurfaceTop + naturalLogicalHeight;
+ const int32_t oldDisplayWidth = mDisplayWidth;
+ const int32_t oldDisplayHeight = mDisplayHeight;
+ mDisplayWidth = naturalDeviceWidth;
+ mDisplayHeight = naturalDeviceHeight;
- mSurfaceOrientation = mParameters.orientationAware ? mViewport.orientation
- : DISPLAY_ORIENTATION_0;
- }
+ // InputReader works in the un-rotated display coordinate space, so we don't need to do
+ // anything if the device is already orientation-aware. If the device is not
+ // orientation-aware, then we need to apply the inverse rotation of the display so that
+ // when the display rotation is applied later as a part of the per-window transform, we
+ // get the expected screen coordinates.
+ mInputDeviceOrientation = mParameters.orientationAware
+ ? DISPLAY_ORIENTATION_0
+ : getInverseRotation(mViewport.orientation);
+ // For orientation-aware devices that work in the un-rotated coordinate space, the
+ // viewport update should be skipped if it is only a change in the orientation.
+ skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth &&
+ mDisplayHeight == oldDisplayHeight && viewportOrientationChanged;
// Apply the input device orientation for the device.
- mSurfaceOrientation =
- (mSurfaceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
+ mInputDeviceOrientation =
+ (mInputDeviceOrientation + static_cast<int32_t>(mParameters.orientation)) % 4;
} else {
mPhysicalWidth = rawWidth;
mPhysicalHeight = rawHeight;
mPhysicalLeft = 0;
mPhysicalTop = 0;
- mRawSurfaceWidth = rawWidth;
- mRawSurfaceHeight = rawHeight;
- mSurfaceLeft = 0;
- mSurfaceTop = 0;
- mSurfaceOrientation = DISPLAY_ORIENTATION_0;
+ mDisplayWidth = rawWidth;
+ mDisplayHeight = rawHeight;
+ mInputDeviceOrientation = DISPLAY_ORIENTATION_0;
}
}
@@ -829,14 +792,12 @@
if ((viewportChanged && !skipViewportUpdate) || deviceModeChanged) {
ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, "
"display id %d",
- getDeviceId(), getDeviceName().c_str(), mRawSurfaceWidth, mRawSurfaceHeight,
- mSurfaceOrientation, mDeviceMode, mViewport.displayId);
+ getDeviceId(), getDeviceName().c_str(), mDisplayWidth, mDisplayHeight,
+ mInputDeviceOrientation, mDeviceMode, mViewport.displayId);
// Configure X and Y factors.
- mXScale = float(mRawSurfaceWidth) / rawWidth;
- mYScale = float(mRawSurfaceHeight) / rawHeight;
- mXTranslate = -mSurfaceLeft;
- mYTranslate = -mSurfaceTop;
+ mXScale = float(mDisplayWidth) / rawWidth;
+ mYScale = float(mDisplayHeight) / rawHeight;
mXPrecision = 1.0f / mXScale;
mYPrecision = 1.0f / mYScale;
@@ -853,7 +814,7 @@
mGeometricScale = avg(mXScale, mYScale);
// Size of diagonal axis.
- float diagonalSize = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
+ float diagonalSize = hypotf(mDisplayWidth, mDisplayHeight);
// Size factors.
if (mCalibration.sizeCalibration != Calibration::SizeCalibration::NONE) {
@@ -1015,21 +976,21 @@
// Compute oriented precision, scales and ranges.
// Note that the maximum value reported is an inclusive maximum value so it is one
- // unit less than the total width or height of surface.
- switch (mSurfaceOrientation) {
+ // unit less than the total width or height of the display.
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
case DISPLAY_ORIENTATION_270:
mOrientedXPrecision = mYPrecision;
mOrientedYPrecision = mXPrecision;
- mOrientedRanges.x.min = mYTranslate;
- mOrientedRanges.x.max = mRawSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.x.min = 0;
+ mOrientedRanges.x.max = mDisplayHeight - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale;
- mOrientedRanges.y.min = mXTranslate;
- mOrientedRanges.y.max = mRawSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.y.min = 0;
+ mOrientedRanges.y.max = mDisplayWidth - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale;
@@ -1039,14 +1000,14 @@
mOrientedXPrecision = mXPrecision;
mOrientedYPrecision = mYPrecision;
- mOrientedRanges.x.min = mXTranslate;
- mOrientedRanges.x.max = mRawSurfaceWidth + mXTranslate - 1;
+ mOrientedRanges.x.min = 0;
+ mOrientedRanges.x.max = mDisplayWidth - 1;
mOrientedRanges.x.flat = 0;
mOrientedRanges.x.fuzz = 0;
mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale;
- mOrientedRanges.y.min = mYTranslate;
- mOrientedRanges.y.max = mRawSurfaceHeight + mYTranslate - 1;
+ mOrientedRanges.y.min = 0;
+ mOrientedRanges.y.max = mDisplayHeight - 1;
mOrientedRanges.y.flat = 0;
mOrientedRanges.y.fuzz = 0;
mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale;
@@ -1059,7 +1020,7 @@
if (mDeviceMode == DeviceMode::POINTER) {
// Compute pointer gesture detection parameters.
float rawDiagonal = hypotf(rawWidth, rawHeight);
- float displayDiagonal = hypotf(mRawSurfaceWidth, mRawSurfaceHeight);
+ float displayDiagonal = hypotf(mDisplayWidth, mDisplayHeight);
// Scale movements such that one whole swipe of the touch pad covers a
// given area relative to the diagonal size of the display when no acceleration
@@ -1093,19 +1054,15 @@
}
}
-void TouchInputMapper::dumpSurface(std::string& dump) {
+void TouchInputMapper::dumpDisplay(std::string& dump) {
dump += StringPrintf(INDENT3 "%s\n", mViewport.toString().c_str());
- dump += StringPrintf(INDENT3 "RawSurfaceWidth: %dpx\n", mRawSurfaceWidth);
- dump += StringPrintf(INDENT3 "RawSurfaceHeight: %dpx\n", mRawSurfaceHeight);
- dump += StringPrintf(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft);
- dump += StringPrintf(INDENT3 "SurfaceTop: %d\n", mSurfaceTop);
- dump += StringPrintf(INDENT3 "SurfaceRight: %d\n", mSurfaceRight);
- dump += StringPrintf(INDENT3 "SurfaceBottom: %d\n", mSurfaceBottom);
+ dump += StringPrintf(INDENT3 "DisplayWidth: %dpx\n", mDisplayWidth);
+ dump += StringPrintf(INDENT3 "DisplayHeight: %dpx\n", mDisplayHeight);
dump += StringPrintf(INDENT3 "PhysicalWidth: %dpx\n", mPhysicalWidth);
dump += StringPrintf(INDENT3 "PhysicalHeight: %dpx\n", mPhysicalHeight);
dump += StringPrintf(INDENT3 "PhysicalLeft: %d\n", mPhysicalLeft);
dump += StringPrintf(INDENT3 "PhysicalTop: %d\n", mPhysicalTop);
- dump += StringPrintf(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation);
+ dump += StringPrintf(INDENT3 "InputDeviceOrientation: %d\n", mInputDeviceOrientation);
}
void TouchInputMapper::configureVirtualKeys() {
@@ -1144,16 +1101,16 @@
int32_t halfHeight = virtualKeyDefinition.height / 2;
virtualKey.hitLeft =
- (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+ (virtualKeyDefinition.centerX - halfWidth) * touchScreenWidth / mDisplayWidth +
touchScreenLeft;
virtualKey.hitRight =
- (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mRawSurfaceWidth +
+ (virtualKeyDefinition.centerX + halfWidth) * touchScreenWidth / mDisplayWidth +
touchScreenLeft;
- virtualKey.hitTop = (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight /
- mRawSurfaceHeight +
+ virtualKey.hitTop =
+ (virtualKeyDefinition.centerY - halfHeight) * touchScreenHeight / mDisplayHeight +
touchScreenTop;
- virtualKey.hitBottom = (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight /
- mRawSurfaceHeight +
+ virtualKey.hitBottom =
+ (virtualKeyDefinition.centerY + halfHeight) * touchScreenHeight / mDisplayHeight +
touchScreenTop;
mVirtualKeys.push_back(virtualKey);
}
@@ -1419,7 +1376,7 @@
void TouchInputMapper::updateAffineTransformation() {
mAffineTransform = getPolicy()->getTouchAffineTransformation(getDeviceContext().getDescriptor(),
- mSurfaceOrientation);
+ mInputDeviceOrientation);
}
void TouchInputMapper::reset(nsecs_t when) {
@@ -1508,14 +1465,14 @@
assignPointerIds(last, next);
}
-#if DEBUG_RAW_EVENTS
- ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
- "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
- last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
- last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
- last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
- next.rawPointerData.canceledIdBits.value);
-#endif
+ if (DEBUG_RAW_EVENTS) {
+ ALOGD("syncTouch: pointerCount %d -> %d, touching ids 0x%08x -> 0x%08x, "
+ "hovering ids 0x%08x -> 0x%08x, canceled ids 0x%08x",
+ last.rawPointerData.pointerCount, next.rawPointerData.pointerCount,
+ last.rawPointerData.touchingIdBits.value, next.rawPointerData.touchingIdBits.value,
+ last.rawPointerData.hoveringIdBits.value, next.rawPointerData.hoveringIdBits.value,
+ next.rawPointerData.canceledIdBits.value);
+ }
if (!next.rawPointerData.touchingIdBits.isEmpty() &&
!next.rawPointerData.hoveringIdBits.isEmpty() &&
@@ -1569,9 +1526,9 @@
nsecs_t when = mExternalStylusFusionTimeout - STYLUS_DATA_LATENCY;
clearStylusDataPendingFlags();
mCurrentRawState.copyFrom(mLastRawState);
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, synthesizing event with new stylus data");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Timeout expired, synthesizing event with new stylus data");
+ }
const nsecs_t readTime = when; // consider this synthetic event to be zero latency
cookAndDispatch(when, readTime);
} else if (mExternalStylusFusionTimeout == LLONG_MAX) {
@@ -1711,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() {
@@ -1757,24 +1715,24 @@
state.rawPointerData.pointerCount != 0;
if (initialDown) {
if (mExternalStylusState.pressure != 0.0f) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Have both stylus and touch data, beginning fusion");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Have both stylus and touch data, beginning fusion");
+ }
mExternalStylusId = state.rawPointerData.touchingIdBits.firstMarkedBit();
} else if (timeout) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Timeout expired, assuming touch is not a stylus.");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Timeout expired, assuming touch is not a stylus.");
+ }
resetExternalStylus();
} else {
if (mExternalStylusFusionTimeout == LLONG_MAX) {
mExternalStylusFusionTimeout = state.when + EXTERNAL_STYLUS_DATA_TIMEOUT;
}
-#if DEBUG_STYLUS_FUSION
- ALOGD("No stylus data but stylus is connected, requesting timeout "
- "(%" PRId64 "ms)",
- mExternalStylusFusionTimeout);
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("No stylus data but stylus is connected, requesting timeout "
+ "(%" PRId64 "ms)",
+ mExternalStylusFusionTimeout);
+ }
getContext()->requestTimeoutAtTime(mExternalStylusFusionTimeout);
return true;
}
@@ -1782,9 +1740,9 @@
// Check if the stylus pointer has gone up.
if (mExternalStylusId != -1 && !state.rawPointerData.touchingIdBits.hasBit(mExternalStylusId)) {
-#if DEBUG_STYLUS_FUSION
- ALOGD("Stylus pointer is going up");
-#endif
+ if (DEBUG_STYLUS_FUSION) {
+ ALOGD("Stylus pointer is going up");
+ }
mExternalStylusId = -1;
}
@@ -1825,10 +1783,10 @@
// Pointer went up while virtual key was down.
mCurrentVirtualKey.down = false;
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Generating key up: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
}
@@ -1852,10 +1810,10 @@
// into the main display surface.
mCurrentVirtualKey.down = false;
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d", mCurrentVirtualKey.keyCode,
- mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Canceling key: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_UP,
AKEY_EVENT_FLAG_FROM_SYSTEM | AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY |
AKEY_EVENT_FLAG_CANCELED);
@@ -1867,8 +1825,10 @@
// Pointer just went down. Check for virtual key press or off-screen touches.
uint32_t id = mCurrentRawState.rawPointerData.touchingIdBits.firstMarkedBit();
const RawPointerData::Pointer& pointer = mCurrentRawState.rawPointerData.pointerForId(id);
- // Exclude unscaled device for inside surface checking.
- if (!isPointInsideSurface(pointer.x, pointer.y) && mDeviceMode != DeviceMode::UNSCALED) {
+ // Skip checking whether the pointer is inside the physical frame if the device is in
+ // unscaled mode.
+ if (!isPointInsidePhysicalFrame(pointer.x, pointer.y) &&
+ mDeviceMode != DeviceMode::UNSCALED) {
// If exactly one pointer went down, check for virtual key hit.
// Otherwise we will drop the entire stroke.
if (mCurrentRawState.rawPointerData.touchingIdBits.count() == 1) {
@@ -1883,10 +1843,10 @@
virtualKey->scanCode);
if (!mCurrentVirtualKey.ignored) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
- mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Generating key down: keyCode=%d, scanCode=%d",
+ mCurrentVirtualKey.keyCode, mCurrentVirtualKey.scanCode);
+ }
dispatchVirtualKey(when, readTime, policyFlags, AKEY_EVENT_ACTION_DOWN,
AKEY_EVENT_FLAG_FROM_SYSTEM |
AKEY_EVENT_FLAG_VIRTUAL_HARD_KEY);
@@ -2137,7 +2097,7 @@
}
// Walk through the the active pointers and map device coordinates onto
- // surface coordinates and adjust for display orientation.
+ // display coordinates and adjust for display orientation.
for (uint32_t i = 0; i < currentPointerCount; i++) {
const RawPointerData::Pointer& in = mCurrentRawState.rawPointerData.pointers[i];
@@ -2297,15 +2257,15 @@
mAffineTransform.applyTo(xTransformed, yTransformed);
rotateAndScale(xTransformed, yTransformed);
- // Adjust X, Y, and coverage coords for surface orientation.
+ // Adjust X, Y, and coverage coords for input device orientation.
float left, top, right, bottom;
- switch (mSurfaceOrientation) {
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_90:
- left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate;
- top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate;
+ left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
+ right = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+ bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
+ top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
orientation -= M_PI_2;
if (mOrientedRanges.haveOrientation &&
orientation < mOrientedRanges.orientation.min) {
@@ -2316,8 +2276,8 @@
case DISPLAY_ORIENTATION_180:
left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale;
right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale;
- bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate;
- top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate;
+ bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
+ top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
orientation -= M_PI;
if (mOrientedRanges.haveOrientation &&
orientation < mOrientedRanges.orientation.min) {
@@ -2328,8 +2288,8 @@
case DISPLAY_ORIENTATION_270:
left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale;
right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale;
- bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
+ bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+ top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
orientation += M_PI_2;
if (mOrientedRanges.haveOrientation &&
orientation > mOrientedRanges.orientation.max) {
@@ -2338,10 +2298,10 @@
}
break;
default:
- left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate;
- bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
- top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate;
+ left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale;
+ right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale;
+ bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale;
+ top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale;
break;
}
@@ -2451,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);
@@ -2603,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();
@@ -2672,9 +2634,9 @@
// Handle TAP timeout.
if (isTimeout) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Processing timeout");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Processing timeout");
+ }
if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) {
if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) {
@@ -2683,9 +2645,9 @@
mConfig.pointerGestureTapDragInterval);
} else {
// The tap is finished.
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP finished");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP finished");
+ }
*outFinishPreviousGesture = true;
mPointerGesture.activeGestureId = -1;
@@ -2781,10 +2743,11 @@
// Switch states based on button and pointer state.
if (isQuietTime) {
// Case 1: Quiet time. (QUIET)
-#if DEBUG_GESTURES
- ALOGD("Gestures: QUIET for next %0.3fms",
- (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: QUIET for next %0.3fms",
+ (mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval - when) *
+ 0.000001f);
+ }
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::QUIET) {
*outFinishPreviousGesture = true;
}
@@ -2808,11 +2771,11 @@
// active. If the user first puts one finger down to click then adds another
// finger to drag then the active pointer should switch to the finger that is
// being dragged.
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
- "currentFingerCount=%d",
- activeTouchId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG activeTouchId=%d, "
+ "currentFingerCount=%d",
+ activeTouchId, currentFingerCount);
+ }
// Reset state when just starting.
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::BUTTON_CLICK_OR_DRAG) {
*outFinishPreviousGesture = true;
@@ -2837,11 +2800,11 @@
}
if (bestId >= 0 && bestId != activeTouchId) {
mPointerGesture.activeTouchId = activeTouchId = bestId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
- "bestId=%d, bestSpeed=%0.3f",
- bestId, bestSpeed);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: BUTTON_CLICK_OR_DRAG switched pointers, "
+ "bestId=%d, bestSpeed=%0.3f",
+ bestId, bestSpeed);
+ }
}
}
@@ -2854,18 +2817,19 @@
deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
// 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();
@@ -2891,12 +2855,13 @@
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
- ALOGD("Gestures: TAP");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP");
+ }
mPointerGesture.tapUpTime = when;
getContext()->requestTimeoutAtTime(when +
@@ -2922,29 +2887,29 @@
tapped = true;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
- y - mPointerGesture.tapY);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP, deltaX=%f, deltaY=%f", x - mPointerGesture.tapX,
+ y - mPointerGesture.tapY);
+ }
}
} else {
-#if DEBUG_GESTURES
- if (mPointerGesture.tapDownTime != LLONG_MIN) {
- ALOGD("Gestures: Not a TAP, %0.3fms since down",
- (when - mPointerGesture.tapDownTime) * 0.000001f);
- } else {
- ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ if (DEBUG_GESTURES) {
+ if (mPointerGesture.tapDownTime != LLONG_MIN) {
+ ALOGD("Gestures: Not a TAP, %0.3fms since down",
+ (when - mPointerGesture.tapDownTime) * 0.000001f);
+ } else {
+ ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+ }
}
-#endif
}
}
mPointerVelocityControl.reset();
if (!tapped) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: NEUTRAL");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: NEUTRAL");
+ }
mPointerGesture.activeGestureId = -1;
mPointerGesture.currentGestureMode = PointerGesture::Mode::NEUTRAL;
mPointerGesture.currentGestureIdBits.clear();
@@ -2959,21 +2924,22 @@
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;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
- x - mPointerGesture.tapX, y - mPointerGesture.tapY);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP_DRAG, deltaX=%f, deltaY=%f",
+ x - mPointerGesture.tapX, y - mPointerGesture.tapY);
+ }
}
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
- (when - mPointerGesture.tapUpTime) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Not a TAP_DRAG, %0.3fms time since up",
+ (when - mPointerGesture.tapUpTime) * 0.000001f);
+ }
}
} else if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) {
mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG;
@@ -2988,26 +2954,26 @@
deltaX = (currentPointer.x - lastPointer.x) * mPointerXMovementScale;
deltaY = (currentPointer.y - lastPointer.y) * mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
// 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();
}
bool down;
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::TAP_DRAG) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: TAP_DRAG");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: TAP_DRAG");
+ }
down = true;
} else {
-#if DEBUG_GESTURES
- ALOGD("Gestures: HOVER");
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: HOVER");
+ }
if (mPointerGesture.lastGestureMode != PointerGesture::Mode::HOVER) {
*outFinishPreviousGesture = true;
}
@@ -3015,7 +2981,8 @@
down = false;
}
- auto [x, y] = getMouseCursorPosition();
+ float x, y;
+ mPointerController->getPosition(&x, &y);
mPointerGesture.currentGestureIdBits.clear();
mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId);
@@ -3060,12 +3027,13 @@
} else if (!settled && currentFingerCount > lastFingerCount) {
// Additional pointers have gone down but not yet settled.
// Reset the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
- when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Resetting gesture since additional pointers went down for "
+ "MULTITOUCH, settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime +
+ mConfig.pointerGestureMultitouchSettleInterval - when) *
+ 0.000001f);
+ }
*outCancelPreviousGesture = true;
} else {
// Continue previous gesture.
@@ -3079,18 +3047,18 @@
mPointerVelocityControl.reset();
// Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
- ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
- "settle time remaining %0.3fms",
- (mPointerGesture.firstTouchTime + mConfig.pointerGestureMultitouchSettleInterval -
- when) * 0.000001f);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+ "settle time remaining %0.3fms",
+ (mPointerGesture.firstTouchTime +
+ mConfig.pointerGestureMultitouchSettleInterval - when) *
+ 0.000001f);
+ }
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.
@@ -3143,10 +3111,10 @@
if (distOverThreshold >= 2) {
if (currentFingerCount > 2) {
// There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
@@ -3162,10 +3130,11 @@
if (mutualDistance > mPointerGestureMaxSwipeWidth) {
// There are two pointers but they are too far apart for a SWIPE,
// switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
- mutualDistance, mPointerGestureMaxSwipeWidth);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > "
+ "%0.3f",
+ mutualDistance, mPointerGestureMaxSwipeWidth);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
} else {
@@ -3190,25 +3159,25 @@
float cosine = dot / (dist1 * dist2); // denominator always > 0
if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) {
// Pointers are moving in the same direction. Switch to SWIPE.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to SWIPE, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f >= %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to SWIPE, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f >= %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ }
mPointerGesture.currentGestureMode = PointerGesture::Mode::SWIPE;
} else {
// Pointers are moving in different directions. Switch to FREEFORM.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS transitioned to FREEFORM, "
- "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
- "cosine %0.3f < %0.3f",
- dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
- mConfig.pointerGestureMultitouchMinDistance, cosine,
- mConfig.pointerGestureSwipeTransitionAngleCosine);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS transitioned to FREEFORM, "
+ "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+ "cosine %0.3f < %0.3f",
+ dist1, mConfig.pointerGestureMultitouchMinDistance, dist2,
+ mConfig.pointerGestureMultitouchMinDistance, cosine,
+ mConfig.pointerGestureSwipeTransitionAngleCosine);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
@@ -3220,10 +3189,10 @@
// Switch from SWIPE to FREEFORM if additional pointers go down.
// Cancel previous gesture.
if (currentFingerCount > 2) {
-#if DEBUG_GESTURES
- ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
- currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: SWIPE transitioned to FREEFORM, number of pointers %d > 2",
+ currentFingerCount);
+ }
*outCancelPreviousGesture = true;
mPointerGesture.currentGestureMode = PointerGesture::Mode::FREEFORM;
}
@@ -3246,7 +3215,7 @@
commonDeltaX *= mPointerXMovementScale;
commonDeltaY *= mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &commonDeltaX, &commonDeltaY);
+ rotateDelta(mInputDeviceOrientation, &commonDeltaX, &commonDeltaY);
mPointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
mPointerGesture.referenceGestureX += commonDeltaX;
@@ -3257,11 +3226,11 @@
if (mPointerGesture.currentGestureMode == PointerGesture::Mode::PRESS ||
mPointerGesture.currentGestureMode == PointerGesture::Mode::SWIPE) {
// PRESS or SWIPE mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: PRESS or SWIPE activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ }
ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
mPointerGesture.currentGestureIdBits.clear();
@@ -3278,11 +3247,11 @@
mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
} else if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) {
// FREEFORM mode.
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM activeTouchId=%d,"
- "activeGestureId=%d, currentTouchPointerCount=%d",
- activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM activeTouchId=%d,"
+ "activeGestureId=%d, currentTouchPointerCount=%d",
+ activeTouchId, mPointerGesture.activeGestureId, currentFingerCount);
+ }
ALOG_ASSERT(mPointerGesture.activeGestureId >= 0);
mPointerGesture.currentGestureIdBits.clear();
@@ -3321,13 +3290,13 @@
}
}
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM follow up "
- "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
- "activeGestureId=%d",
- mappedTouchIdBits.value, usedGestureIdBits.value,
- mPointerGesture.activeGestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM follow up "
+ "mappedTouchIdBits=0x%08x, usedGestureIdBits=0x%08x, "
+ "activeGestureId=%d",
+ mappedTouchIdBits.value, usedGestureIdBits.value,
+ mPointerGesture.activeGestureId);
+ }
BitSet32 idBits(mCurrentCookedState.fingerIdBits);
for (uint32_t i = 0; i < currentFingerCount; i++) {
@@ -3336,18 +3305,18 @@
if (!mappedTouchIdBits.hasBit(touchId)) {
gestureId = usedGestureIdBits.markFirstUnmarkedBit();
mPointerGesture.freeformTouchToGestureIdMap[touchId] = gestureId;
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "new mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM "
+ "new mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
} else {
gestureId = mPointerGesture.freeformTouchToGestureIdMap[touchId];
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM "
- "existing mapping for touch id %d -> gesture id %d",
- touchId, gestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM "
+ "existing mapping for touch id %d -> gesture id %d",
+ touchId, gestureId);
+ }
}
mPointerGesture.currentGestureIdBits.markBit(gestureId);
mPointerGesture.currentGestureIdToIndex[gestureId] = i;
@@ -3356,7 +3325,7 @@
mCurrentRawState.rawPointerData.pointerForId(touchId);
float deltaX = (pointer.x - mPointerGesture.referenceTouchX) * mPointerXZoomScale;
float deltaY = (pointer.y - mPointerGesture.referenceTouchY) * mPointerYZoomScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerGesture.currentGestureProperties[i].clear();
mPointerGesture.currentGestureProperties[i].id = gestureId;
@@ -3376,47 +3345,46 @@
if (mPointerGesture.activeGestureId < 0) {
mPointerGesture.activeGestureId =
mPointerGesture.currentGestureIdBits.firstMarkedBit();
-#if DEBUG_GESTURES
- ALOGD("Gestures: FREEFORM new "
- "activeGestureId=%d",
- mPointerGesture.activeGestureId);
-#endif
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: FREEFORM new activeGestureId=%d",
+ mPointerGesture.activeGestureId);
+ }
}
}
}
mPointerController->setButtonState(mCurrentRawState.buttonState);
-#if DEBUG_GESTURES
- ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
- "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
- "lastGestureMode=%d, lastGestureIdBits=0x%08x",
- toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
- mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
- mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
- for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
- ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ if (DEBUG_GESTURES) {
+ ALOGD("Gestures: finishPreviousGesture=%s, cancelPreviousGesture=%s, "
+ "currentGestureMode=%d, currentGestureIdBits=0x%08x, "
+ "lastGestureMode=%d, lastGestureIdBits=0x%08x",
+ toString(*outFinishPreviousGesture), toString(*outCancelPreviousGesture),
+ mPointerGesture.currentGestureMode, mPointerGesture.currentGestureIdBits.value,
+ mPointerGesture.lastGestureMode, mPointerGesture.lastGestureIdBits.value);
+ for (BitSet32 idBits = mPointerGesture.currentGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.currentGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.currentGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.currentGestureCoords[index];
+ ALOGD(" currentGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
+ for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
+ uint32_t id = idBits.clearFirstMarkedBit();
+ uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
+ const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
+ const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
+ ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
+ "x=%0.3f, y=%0.3f, pressure=%0.3f",
+ id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
+ coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
+ }
}
- for (BitSet32 idBits = mPointerGesture.lastGestureIdBits; !idBits.isEmpty();) {
- uint32_t id = idBits.clearFirstMarkedBit();
- uint32_t index = mPointerGesture.lastGestureIdToIndex[id];
- const PointerProperties& properties = mPointerGesture.lastGestureProperties[index];
- const PointerCoords& coords = mPointerGesture.lastGestureCoords[index];
- ALOGD(" lastGesture[%d]: index=%d, toolType=%d, "
- "x=%0.3f, y=%0.3f, pressure=%0.3f",
- id, index, properties.toolType, coords.getAxisValue(AMOTION_EVENT_AXIS_X),
- coords.getAxisValue(AMOTION_EVENT_AXIS_Y),
- coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
- }
-#endif
return true;
}
@@ -3428,13 +3396,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);
@@ -3472,10 +3442,10 @@
mLastRawState.rawPointerData.pointers[lastIndex].y) *
mPointerYMovementScale;
- rotateDelta(mSurfaceOrientation, &deltaX, &deltaY);
+ rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY);
mPointerVelocityControl.move(when, &deltaX, &deltaY);
- moveMouseCursor(deltaX, deltaY);
+ mPointerController->move(deltaX, deltaY);
} else {
mPointerVelocityControl.reset();
}
@@ -3483,7 +3453,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);
@@ -3523,7 +3494,8 @@
}
int32_t displayId = mPointerController->getDisplayId();
- auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition();
+ float xCursorPosition, yCursorPosition;
+ mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
if (mPointerSimple.down && !down) {
mPointerSimple.down = false;
@@ -3689,15 +3661,13 @@
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();
std::vector<TouchVideoFrame> frames = getDeviceContext().getVideoFrames();
std::for_each(frames.begin(), frames.end(),
- [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); });
+ [this](TouchVideoFrame& frame) { frame.rotate(this->mInputDeviceOrientation); });
NotifyMotionArgs args(getContext()->getNextId(), when, readTime, deviceId, source, displayId,
policyFlags, action, actionButton, flags, metaState, buttonState,
MotionClassification::NONE, edgeFlags, pointerCount, pointerProperties,
@@ -3741,66 +3711,59 @@
abortTouches(when, readTime, 0 /* policyFlags*/);
}
-// Transform raw coordinate to surface coordinate
-void TouchInputMapper::rotateAndScale(float& x, float& y) {
- // Scale to surface coordinate.
+// Transform input device coordinates to display panel coordinates.
+void TouchInputMapper::rotateAndScale(float& x, float& y) const {
const float xScaled = float(x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = float(y - mRawPointerAxes.y.minValue) * mYScale;
const float xScaledMax = float(mRawPointerAxes.x.maxValue - x) * mXScale;
const float yScaledMax = float(mRawPointerAxes.y.maxValue - y) * mYScale;
- // Rotate to surface coordinate.
+ // Rotate to display coordinate.
// 0 - no swap and reverse.
// 90 - swap x/y and reverse y.
// 180 - reverse x, y.
// 270 - swap x/y and reverse x.
- switch (mSurfaceOrientation) {
+ switch (mInputDeviceOrientation) {
case DISPLAY_ORIENTATION_0:
- x = xScaled + mXTranslate;
- y = yScaled + mYTranslate;
+ x = xScaled;
+ y = yScaled;
break;
case DISPLAY_ORIENTATION_90:
- y = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
- x = yScaled + mYTranslate;
+ y = xScaledMax;
+ x = yScaled;
break;
case DISPLAY_ORIENTATION_180:
- x = xScaledMax - (mRawSurfaceWidth - mSurfaceRight);
- y = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+ x = xScaledMax;
+ y = yScaledMax;
break;
case DISPLAY_ORIENTATION_270:
- y = xScaled + mXTranslate;
- x = yScaledMax - (mRawSurfaceHeight - mSurfaceBottom);
+ y = xScaled;
+ x = yScaledMax;
break;
default:
assert(false);
}
}
-bool TouchInputMapper::isPointInsideSurface(int32_t x, int32_t y) {
+bool TouchInputMapper::isPointInsidePhysicalFrame(int32_t x, int32_t y) const {
const float xScaled = (x - mRawPointerAxes.x.minValue) * mXScale;
const float yScaled = (y - mRawPointerAxes.y.minValue) * mYScale;
- if (isPerWindowInputRotationEnabled()) {
- return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
- y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
- }
return x >= mRawPointerAxes.x.minValue && x <= mRawPointerAxes.x.maxValue &&
- xScaled >= mSurfaceLeft && xScaled <= mSurfaceRight &&
+ xScaled >= mPhysicalLeft && xScaled <= (mPhysicalLeft + mPhysicalWidth) &&
y >= mRawPointerAxes.y.minValue && y <= mRawPointerAxes.y.maxValue &&
- yScaled >= mSurfaceTop && yScaled <= mSurfaceBottom;
+ yScaled >= mPhysicalTop && yScaled <= (mPhysicalTop + mPhysicalHeight);
}
const TouchInputMapper::VirtualKey* TouchInputMapper::findVirtualKeyHit(int32_t x, int32_t y) {
for (const VirtualKey& virtualKey : mVirtualKeys) {
-#if DEBUG_VIRTUAL_KEYS
- ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
- "left=%d, top=%d, right=%d, bottom=%d",
- x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft, virtualKey.hitTop,
- virtualKey.hitRight, virtualKey.hitBottom);
-#endif
+ if (DEBUG_VIRTUAL_KEYS) {
+ ALOGD("VirtualKeys: Hit test (%d, %d): keyCode=%d, scanCode=%d, "
+ "left=%d, top=%d, right=%d, bottom=%d",
+ x, y, virtualKey.keyCode, virtualKey.scanCode, virtualKey.hitLeft,
+ virtualKey.hitTop, virtualKey.hitRight, virtualKey.hitBottom);
+ }
if (virtualKey.isHit(x, y)) {
return &virtualKey;
@@ -3897,13 +3860,13 @@
}
}
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
- for (size_t i = 0; i < heapSize; i++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
- heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - initial distance min-heap: size=%d", heapSize);
+ for (size_t i = 0; i < heapSize; i++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, i,
+ heap[i].currentPointerIndex, heap[i].lastPointerIndex, heap[i].distance);
+ }
}
-#endif
// Pull matches out by increasing order of distance.
// To avoid reassigning pointers that have already been matched, the loop keeps track
@@ -3942,13 +3905,14 @@
parentIndex = childIndex;
}
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
- for (size_t j = 0; j < heapSize; j++) {
- ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64, j,
- heap[j].currentPointerIndex, heap[j].lastPointerIndex, heap[j].distance);
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - reduced distance min-heap: size=%d", heapSize);
+ for (size_t j = 0; j < heapSize; j++) {
+ ALOGD(" heap[%zu]: cur=%" PRIu32 ", last=%" PRIu32 ", distance=%" PRIu64,
+ j, heap[j].currentPointerIndex, heap[j].lastPointerIndex,
+ heap[j].distance);
+ }
}
-#endif
}
heapSize -= 1;
@@ -3970,11 +3934,11 @@
currentPointerIndex));
usedIdBits.markBit(id);
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
- ", distance=%" PRIu64,
- lastPointerIndex, currentPointerIndex, id, heap[0].distance);
-#endif
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - matched: cur=%" PRIu32 ", last=%" PRIu32 ", id=%" PRIu32
+ ", distance=%" PRIu64,
+ lastPointerIndex, currentPointerIndex, id, heap[0].distance);
+ }
break;
}
}
@@ -3989,9 +3953,10 @@
current.rawPointerData.markIdBit(id,
current.rawPointerData.isHovering(currentPointerIndex));
-#if DEBUG_POINTER_ASSIGNMENT
- ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex, id);
-#endif
+ if (DEBUG_POINTER_ASSIGNMENT) {
+ ALOGD("assignPointerIds - assigned: cur=%" PRIu32 ", id=%" PRIu32, currentPointerIndex,
+ id);
+ }
}
}
@@ -4047,63 +4012,4 @@
return std::nullopt;
}
-void TouchInputMapper::moveMouseCursor(float dx, float dy) const {
- if (isPerWindowInputRotationEnabled()) {
- // 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 (!isPerWindowInputRotationEnabled()) return {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 {
- if (isPerWindowInputRotationEnabled() && mViewport.isValid()) {
- // 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, mRawSurfaceWidth, mRawSurfaceHeight);
- }
-
- 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);
-
- if (isPerWindowInputRotationEnabled()) {
- // Convert from InputReader's un-rotated coordinate space to PointerController's rotated
- // coordinate space.
- rotatePoint(mViewport.orientation, x, y, mRawSurfaceWidth, mRawSurfaceHeight);
- }
-
- 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 3340672..9b020a6 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.h
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.h
@@ -406,8 +406,8 @@
virtual void dumpParameters(std::string& dump);
virtual void configureRawPointerAxes();
virtual void dumpRawPointerAxes(std::string& dump);
- virtual void configureSurface(nsecs_t when, bool* outResetNeeded);
- virtual void dumpSurface(std::string& dump);
+ virtual void configureInputDevice(nsecs_t when, bool* outResetNeeded);
+ virtual void dumpDisplay(std::string& dump);
virtual void configureVirtualKeys();
virtual void dumpVirtualKeys(std::string& dump);
virtual void parseCalibration();
@@ -426,39 +426,27 @@
// The components of the viewport are specified in the display's rotated orientation.
DisplayViewport mViewport;
- // The surface orientation, width and height set by configureSurface().
- // The width and height are derived from the viewport but are specified
+ // The width and height are obtained from the viewport and are specified
// in the natural orientation.
- // They could be used for calculating diagonal, scaling factors, and virtual keys.
- int32_t mRawSurfaceWidth;
- int32_t mRawSurfaceHeight;
+ int32_t mDisplayWidth;
+ int32_t mDisplayHeight;
- // The surface origin specifies how the surface coordinates should be translated
- // to align with the logical display coordinate space.
- // TODO(b/188939842): Remove surface coordinates when Per-Window Input Rotation is enabled.
- int32_t mSurfaceLeft;
- int32_t mSurfaceTop;
- int32_t mSurfaceRight;
- int32_t mSurfaceBottom;
-
- // Similar to the surface coordinates, but in the raw display coordinate space rather than in
- // the logical coordinate space.
+ // The physical frame is the rectangle in the display's coordinate space that maps to the
+ // the logical display frame.
int32_t mPhysicalWidth;
int32_t mPhysicalHeight;
int32_t mPhysicalLeft;
int32_t mPhysicalTop;
- // The orientation may be different from the viewport orientation as it specifies
- // the rotation of the surface coordinates required to produce the viewport's
- // requested orientation, so it will depend on whether the device is orientation aware.
- int32_t mSurfaceOrientation;
+ // The orientation of the input device relative to that of the display panel. It specifies
+ // the rotation of the input device coordinates required to produce the display panel
+ // orientation, so it will depend on whether the device is orientation aware.
+ int32_t mInputDeviceOrientation;
// Translation and scaling factors, orientation-independent.
- float mXTranslate;
float mXScale;
float mXPrecision;
- float mYTranslate;
float mYScale;
float mYPrecision;
@@ -808,21 +796,13 @@
// touchscreen.
void updateTouchSpots();
- bool isPointInsideSurface(int32_t x, int32_t y);
+ bool isPointInsidePhysicalFrame(int32_t x, int32_t y) const;
const VirtualKey* findVirtualKeyHit(int32_t x, int32_t y);
static void assignPointerIds(const RawState& last, RawState& current);
const char* modeToString(DeviceMode deviceMode);
- void rotateAndScale(float& x, float& y);
-
- // 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);
+ void rotateAndScale(float& x, float& y) const;
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
index 8c7879b..1976fed 100644
--- a/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/VibratorInputMapper.cpp
@@ -41,10 +41,10 @@
void VibratorInputMapper::vibrate(const VibrationSequence& sequence, ssize_t repeat,
int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
- sequence.toString().c_str(), repeat, token);
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%zd, token=%d", getDeviceId(),
+ sequence.toString().c_str(), repeat, token);
+ }
mVibrating = true;
mSequence = sequence;
@@ -59,9 +59,9 @@
}
void VibratorInputMapper::cancelVibrate(int32_t token) {
-#if DEBUG_VIBRATOR
- ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token);
+ }
if (mVibrating && mToken == token) {
stopVibrating();
@@ -87,9 +87,9 @@
}
void VibratorInputMapper::nextStep() {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: index=%d, vibrate deviceId=%d", (int)mIndex, getDeviceId());
+ }
mIndex += 1;
if (size_t(mIndex) >= mSequence.pattern.size()) {
if (mRepeat < 0) {
@@ -102,16 +102,16 @@
const VibrationElement& element = mSequence.pattern[mIndex];
if (element.isOn()) {
-#if DEBUG_VIBRATOR
- std::string description = element.toString();
- ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
- description.c_str());
-#endif
+ if (DEBUG_VIBRATOR) {
+ std::string description = element.toString();
+ ALOGD("nextStep: sending vibrate deviceId=%d, element=%s", getDeviceId(),
+ description.c_str());
+ }
getDeviceContext().vibrate(element);
} else {
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId());
+ }
getDeviceContext().cancelVibrate();
}
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
@@ -119,16 +119,16 @@
std::chrono::duration_cast<std::chrono::nanoseconds>(element.duration);
mNextStepTime = now + duration.count();
getContext()->requestTimeoutAtTime(mNextStepTime);
-#if DEBUG_VIBRATOR
- ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("nextStep: scheduled timeout in %lldms", element.duration.count());
+ }
}
void VibratorInputMapper::stopVibrating() {
mVibrating = false;
-#if DEBUG_VIBRATOR
- ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
-#endif
+ if (DEBUG_VIBRATOR) {
+ ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId());
+ }
getDeviceContext().cancelVibrate();
// Request InputReader to notify InputManagerService for vibration complete.
diff --git a/services/inputflinger/sysprop/Android.bp b/services/inputflinger/sysprop/Android.bp
deleted file mode 100644
index b9d65ee..0000000
--- a/services/inputflinger/sysprop/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-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"],
-}
-
-sysprop_library {
- name: "InputFlingerProperties",
- srcs: ["*.sysprop"],
- api_packages: ["android.sysprop"],
- property_owner: "Platform",
-}
diff --git a/services/inputflinger/sysprop/InputFlingerProperties.sysprop b/services/inputflinger/sysprop/InputFlingerProperties.sysprop
deleted file mode 100644
index 1c7e724..0000000
--- a/services/inputflinger/sysprop/InputFlingerProperties.sysprop
+++ /dev/null
@@ -1,27 +0,0 @@
-# Copyright (C) 2021 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the License);
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an AS IS BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-module: "android.sysprop.InputFlingerProperties"
-owner: Platform
-
-# When per-window-input-rotation is enabled, InputReader works in the un-rotated
-# display coordinate space, and the display rotation is encoded as part of the
-# input window transform that is sent from SurfaceFlinger to InputDispatcher.
-prop {
- api_name: "per_window_input_rotation"
- type: Boolean
- scope: Internal
- access: ReadWrite
- prop_name: "persist.debug.per_window_input_rotation"
-}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ba0ce95..ba70929 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -23,6 +23,7 @@
#include <gtest/gtest.h>
#include <input/Input.h>
#include <linux/input.h>
+#include <sys/epoll.h>
#include <cinttypes>
#include <thread>
@@ -219,21 +220,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 +244,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 +319,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 +347,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 +389,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 {}
@@ -842,7 +874,6 @@
FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
- EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
}
void consumeCaptureEvent(bool hasCapture) {
@@ -923,6 +954,8 @@
sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+ int getChannelFd() { return mConsumer->getChannel()->getFd().get(); }
+
protected:
std::unique_ptr<InputConsumer> mConsumer;
PreallocatedInputEventFactory mEventFactory;
@@ -1011,6 +1044,8 @@
mInfo.transform = translate * displayTransform;
}
+ void setTouchableRegion(const Region& region) { mInfo.touchableRegion = region; }
+
void setType(WindowInfo::Type type) { mInfo.type = type; }
void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; }
@@ -1176,6 +1211,10 @@
mInfo.ownerUid = ownerUid;
}
+ void destroyReceiver() { mInputReceiver = nullptr; }
+
+ int getChannelFd() { return mInputReceiver->getChannelFd(); }
+
private:
const std::string mName;
std::unique_ptr<FakeInputReceiver> mInputReceiver;
@@ -1355,7 +1394,7 @@
static InputEventInjectionResult injectMotionEvent(
const std::unique_ptr<InputDispatcher>& dispatcher, int32_t action, int32_t source,
- int32_t displayId, const PointF& position,
+ int32_t displayId, const PointF& position = {100, 200},
const PointF& cursorPosition = {AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION},
std::chrono::milliseconds injectionTimeout = INJECT_EVENT_TIMEOUT,
@@ -1439,6 +1478,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 =
@@ -1579,6 +1635,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.
@@ -2670,6 +2773,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) {
@@ -2878,6 +2988,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,
@@ -2957,8 +3152,8 @@
const VerifiedKeyEvent& verifiedKey = static_cast<const VerifiedKeyEvent&>(*verified);
ASSERT_EQ(keyArgs.action, verifiedKey.action);
- ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags);
+ ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos);
ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode);
ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode);
ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState);
@@ -3006,65 +3201,12 @@
EXPECT_EQ(rawXY.x, verifiedMotion.rawX);
EXPECT_EQ(rawXY.y, verifiedMotion.rawY);
EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked);
- EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags);
+ EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos);
EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState);
EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState);
}
-TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) {
- std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
- sp<FakeWindowHandle> window =
- new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT);
- const std::string name = window->getName();
-
- // Window gets transformed by offset values.
- window->setWindowOffset(500.0f, 500.0f);
-
- mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- window->setFocusable(true);
-
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}});
-
- // First, we set focused window so that focusedWindowHandle is not null.
- setFocusedWindow(window);
-
- // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked.
- window->consumeFocusEvent(true);
-
- constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD,
- AMOTION_EVENT_ACTION_DOWN),
- std::pair(AINPUT_SOURCE_JOYSTICK,
- AMOTION_EVENT_ACTION_MOVE)};
- for (const auto& [source, action] : nonTransformedSources) {
- const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT);
- mDispatcher->notifyMotion(&motionArgs);
-
- MotionEvent* event = window->consumeMotion();
- ASSERT_NE(event, nullptr);
-
- const MotionEvent& motionEvent = *event;
- EXPECT_EQ(action, motionEvent.getAction());
- EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount());
-
- float expectedX = motionArgs.pointerCoords[0].getX();
- float expectedY = motionArgs.pointerCoords[0].getY();
-
- // Ensure the axis values from the final motion event are not transformed.
- EXPECT_EQ(expectedX, motionEvent.getX(0))
- << "expected " << expectedX << " for x coord of " << name.c_str() << ", got "
- << motionEvent.getX(0);
- EXPECT_EQ(expectedY, motionEvent.getY(0))
- << "expected " << expectedY << " for y coord of " << name.c_str() << ", got "
- << motionEvent.getY(0);
- // Ensure the raw and transformed axis values for the motion event are the same.
- EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0))
- << "expected raw and transformed X-axis values to be equal";
- EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0))
- << "expected raw and transformed Y-axis values to be equal";
- }
-}
-
/**
* Ensure that separate calls to sign the same data are generating the same key.
* We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance
@@ -3661,6 +3803,49 @@
secondWindowInPrimary->consumeKeyDown(ADISPLAY_ID_DEFAULT);
}
+TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) {
+ FakeMonitorReceiver monitorInPrimary =
+ FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitorInSecondary =
+ FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+
+ // Test touch down on primary display.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // Test touch down on second display.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+ monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
+
+ // Trigger cancel touch.
+ mDispatcher->cancelCurrentTouch();
+ windowInPrimary->consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+ monitorInPrimary.consumeMotionCancel(ADISPLAY_ID_DEFAULT);
+ windowInSecondary->consumeMotionCancel(SECOND_DISPLAY_ID);
+ monitorInSecondary.consumeMotionCancel(SECOND_DISPLAY_ID);
+
+ // Test inject a move motion event, no window/monitor should receive the event.
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::FAILED";
+ windowInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
+
+ ASSERT_EQ(InputEventInjectionResult::FAILED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ SECOND_DISPLAY_ID, {110, 200}))
+ << "Inject motion event should return InputEventInjectionResult::FAILED";
+ windowInSecondary->assertNoEvents();
+ monitorInSecondary.assertNoEvents();
+}
+
class InputFilterTest : public InputDispatcherTest {
protected:
void testNotifyMotion(int32_t displayId, bool expectToBeFiltered,
@@ -4044,7 +4229,7 @@
}
}
- void touchAndAssertPositions(int32_t action, std::vector<PointF> touchedPoints,
+ void touchAndAssertPositions(int32_t action, const std::vector<PointF>& touchedPoints,
std::vector<PointF> expectedPoints) {
NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN,
ADISPLAY_ID_DEFAULT, touchedPoints);
@@ -6102,4 +6287,317 @@
mSecondWindow->assertNoEvents();
}
+class InputDispatcherSpyWindowTest : public InputDispatcherTest {
+public:
+ sp<FakeWindowHandle> createSpy(const Flags<WindowInfo::Flag> flags) {
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ std::string name = "Fake Spy ";
+ name += std::to_string(mSpyCount++);
+ sp<FakeWindowHandle> spy =
+ new FakeWindowHandle(application, mDispatcher, name.c_str(), ADISPLAY_ID_DEFAULT);
+ spy->setInputFeatures(WindowInfo::Feature::SPY);
+ spy->addFlags(flags);
+ return spy;
+ }
+
+ sp<FakeWindowHandle> createForeground() {
+ std::shared_ptr<FakeApplicationHandle> application =
+ std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->addFlags(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::SPLIT_TOUCH);
+ return window;
+ }
+
+private:
+ int mSpyCount{0};
+};
+
+/**
+ * Input injection into a display with a spy window but no foreground windows should succeed.
+ */
+TEST_F(InputDispatcherSpyWindowTest, NoForegroundWindow) {
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ spy->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+/**
+ * Verify the order in which different input windows receive events. The touched foreground window
+ * (if there is one) should always receive the event first. When there are multiple spy windows, the
+ * spy windows will receive the event according to their Z-order, where the top-most spy window will
+ * receive events before ones belows it.
+ *
+ * Here, we set up a scenario with four windows in the following Z order from the top:
+ * spy1, spy2, window, spy3.
+ * We then inject an event and verify that the foreground "window" receives it first, followed by
+ * "spy1" and "spy2". The "spy3" does not receive the event because it is underneath the foreground
+ * window.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesInputInOrder) {
+ auto window = createForeground();
+ auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ auto spy3 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window, spy3}}});
+ const std::vector<sp<FakeWindowHandle>> channels{spy1, spy2, window, spy3};
+ const size_t numChannels = channels.size();
+
+ base::unique_fd epollFd(epoll_create1(0 /*flags*/));
+ if (!epollFd.ok()) {
+ FAIL() << "Failed to create epoll fd";
+ }
+
+ for (size_t i = 0; i < numChannels; i++) {
+ struct epoll_event event = {.events = EPOLLIN, .data.u64 = i};
+ if (epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, channels[i]->getChannelFd(), &event) < 0) {
+ FAIL() << "Failed to add fd to epoll";
+ }
+ }
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ std::vector<size_t> eventOrder;
+ std::vector<struct epoll_event> events(numChannels);
+ for (;;) {
+ const int nFds = epoll_wait(epollFd.get(), events.data(), static_cast<int>(numChannels),
+ (100ms).count());
+ if (nFds < 0) {
+ FAIL() << "Failed to call epoll_wait";
+ }
+ if (nFds == 0) {
+ break; // epoll_wait timed out
+ }
+ for (int i = 0; i < nFds; i++) {
+ ASSERT_EQ(EPOLLIN, events[i].events);
+ eventOrder.push_back(events[i].data.u64);
+ channels[i]->consumeMotionDown();
+ }
+ }
+
+ // Verify the order in which the events were received.
+ EXPECT_EQ(3u, eventOrder.size());
+ EXPECT_EQ(2u, eventOrder[0]); // index 2: window
+ EXPECT_EQ(0u, eventOrder[1]); // index 0: spy1
+ EXPECT_EQ(1u, eventOrder[2]); // index 1: spy2
+}
+
+/**
+ * A spy window using the NOT_TOUCHABLE flag does not receive events.
+ */
+TEST_F(InputDispatcherSpyWindowTest, NotTouchable) {
+ auto window = createForeground();
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCHABLE);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ spy->assertNoEvents();
+}
+
+/**
+ * A spy window will only receive gestures that originate within its touchable region. Gestures that
+ * have their ACTION_DOWN outside of the touchable region of the spy window will not be dispatched
+ * to the window.
+ */
+TEST_F(InputDispatcherSpyWindowTest, TouchableRegion) {
+ auto window = createForeground();
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ spy->setTouchableRegion(Region{{0, 0, 20, 20}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ // Inject an event outside the spy window's touchable region.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spy->assertNoEvents();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionUp();
+ spy->assertNoEvents();
+
+ // Inject an event inside the spy window's touchable region.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {5, 10}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spy->consumeMotionDown();
+}
+
+/**
+ * A spy window that is a modal window will receive gestures outside of its frame and touchable
+ * region.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ModalWindow) {
+ auto window = createForeground();
+ auto spy = createSpy(static_cast<WindowInfo::Flag>(0));
+ // This spy window does not have the NOT_TOUCH_MODAL flag set.
+ spy->setFrame(Rect{0, 0, 20, 20});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ // Inject an event outside the spy window's frame and touchable region.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spy->consumeMotionDown();
+}
+
+/**
+ * A spy window can listen for touches outside its touchable region using the WATCH_OUTSIDE_TOUCHES
+ * flag.
+ */
+TEST_F(InputDispatcherSpyWindowTest, WatchOutsideTouches) {
+ auto window = createForeground();
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL | WindowInfo::Flag::WATCH_OUTSIDE_TOUCH);
+ spy->setFrame(Rect{0, 0, 20, 20});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ // Inject an event outside the spy window's frame and touchable region.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spy->consumeMotionOutside();
+}
+
+/**
+ * When configured to block untrusted touches, events will not be dispatched to windows below a spy
+ * window if it is not a trusted overly.
+ */
+TEST_F(InputDispatcherSpyWindowTest, BlockUntrustedTouches) {
+ mDispatcher->setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode::BLOCK);
+
+ auto window = createForeground();
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ window->setOwnerInfo(111, 111);
+ spy->setOwnerInfo(222, 222);
+ spy->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, window}}});
+
+ // Inject an event outside the spy window's frame and touchable region.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ spy->consumeMotionDown();
+ window->assertNoEvents();
+}
+
+/**
+ * A spy window can pilfer pointers. When this happens, touch gestures that are currently sent to
+ * any other windows - including other spy windows - will also be cancelled.
+ */
+TEST_F(InputDispatcherSpyWindowTest, PilferPointers) {
+ auto window = createForeground();
+ auto spy1 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ auto spy2 = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy1, spy2, window}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spy1->consumeMotionDown();
+ spy2->consumeMotionDown();
+
+ // Pilfer pointers from the second spy window.
+ mDispatcher->pilferPointers(spy2->getToken());
+ spy2->assertNoEvents();
+ spy1->consumeMotionCancel();
+ window->consumeMotionCancel();
+
+ // The rest of the gesture should only be sent to the second spy window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ spy2->consumeMotionMove();
+ spy1->assertNoEvents();
+ window->assertNoEvents();
+}
+
+/**
+ * Even when a spy window spans over multiple foreground windows, the spy should receive all
+ * pointers that are down within its bounds.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesMultiplePointers) {
+ auto windowLeft = createForeground();
+ windowLeft->setFrame({0, 0, 100, 200});
+ auto windowRight = createForeground();
+ windowRight->setFrame({100, 0, 200, 200});
+ auto spy = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ spy->setFrame({0, 0, 200, 200});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spy, windowLeft, windowRight}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ windowLeft->consumeMotionDown();
+ spy->consumeMotionDown();
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(
+ PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ windowRight->consumeMotionDown();
+ spy->consumeMotionPointerDown(1 /*pointerIndex*/);
+}
+
+/**
+ * When the first pointer lands outside the spy window and the second pointer lands inside it, the
+ * the spy should receive the second pointer with ACTION_DOWN.
+ */
+TEST_F(InputDispatcherSpyWindowTest, ReceivesSecondPointerAsDown) {
+ auto window = createForeground();
+ window->setFrame({0, 0, 200, 200});
+ auto spyRight = createSpy(WindowInfo::Flag::NOT_TOUCH_MODAL);
+ spyRight->setFrame({100, 0, 200, 200});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyRight, window}}});
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ window->consumeMotionDown();
+ spyRight->assertNoEvents();
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN |
+ (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(
+ PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(150).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->consumeMotionPointerDown(1 /*pointerIndex*/);
+ spyRight->consumeMotionDown();
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 53b03ad..336afc6 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -16,7 +16,6 @@
#include <CursorInputMapper.h>
#include <InputDevice.h>
-#include <InputFlingerProperties.sysprop.h>
#include <InputMapper.h>
#include <InputReader.h>
#include <InputReaderBase.h>
@@ -2695,7 +2694,6 @@
static const int32_t DEVICE_CONTROLLER_NUMBER;
static const Flags<InputDeviceClass> DEVICE_CLASSES;
static const int32_t EVENTHUB_ID;
- static const std::optional<bool> INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE;
std::shared_ptr<FakeEventHub> mFakeEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
@@ -2713,18 +2711,12 @@
}
void SetUp() override {
- // Ensure per_window_input_rotation is enabled.
- sysprop::InputFlingerProperties::per_window_input_rotation(true);
-
SetUp(DEVICE_CLASSES);
}
void TearDown() override {
mFakeListener.reset();
mFakePolicy.clear();
-
- sysprop::InputFlingerProperties::per_window_input_rotation(
- INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE);
}
void addConfigurationProperty(const char* key, const char* value) {
@@ -2836,8 +2828,6 @@
const Flags<InputDeviceClass> InputMapperTest::DEVICE_CLASSES =
Flags<InputDeviceClass>(0); // not needed for current tests
const int32_t InputMapperTest::EVENTHUB_ID = 1;
-const std::optional<bool> InputMapperTest::INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE =
- sysprop::InputFlingerProperties::per_window_input_rotation();
// --- SwitchInputMapperTest ---
diff --git a/services/powermanager/Android.bp b/services/powermanager/Android.bp
index d828aa9..6fbba3f 100644
--- a/services/powermanager/Android.bp
+++ b/services/powermanager/Android.bp
@@ -24,11 +24,11 @@
],
aidl: {
- local_include_dirs: ["include"],
- include_dirs: [
- "frameworks/base/core/java/android/os",
- ],
- export_aidl_headers: true
+ local_include_dirs: ["include"],
+ include_dirs: [
+ "frameworks/base/core/java/android/os",
+ ],
+ export_aidl_headers: true,
},
shared_libs: [
@@ -38,7 +38,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V3-cpp",
],
cflags: [
@@ -50,6 +50,6 @@
local_include_dirs: ["include"],
export_include_dirs: [
- "include",
+ "include",
],
}
diff --git a/services/powermanager/benchmarks/Android.bp b/services/powermanager/benchmarks/Android.bp
index 3997929..fcb012f 100644
--- a/services/powermanager/benchmarks/Android.bp
+++ b/services/powermanager/benchmarks/Android.bp
@@ -38,7 +38,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V3-cpp",
],
static_libs: [
"libtestUtil",
diff --git a/services/powermanager/tests/Android.bp b/services/powermanager/tests/Android.bp
index 659b2d2..2d1558a 100644
--- a/services/powermanager/tests/Android.bp
+++ b/services/powermanager/tests/Android.bp
@@ -46,7 +46,7 @@
"libutils",
"android.hardware.power@1.0",
"android.hardware.power@1.1",
- "android.hardware.power-V2-cpp",
+ "android.hardware.power-V3-cpp",
],
static_libs: [
"libgmock",
diff --git a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
index d890f5c..cb1a77a 100644
--- a/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
+++ b/services/powermanager/tests/PowerHalWrapperAidlTest.cpp
@@ -183,6 +183,10 @@
auto result = mWrapper->setMode(Mode::LAUNCH, true);
ASSERT_TRUE(result.isUnsupported());
+
+ EXPECT_CALL(*mMockHal.get(), isModeSupported(Eq(Mode::CAMERA_STREAMING_HIGH), _))
+ .Times(Exactly(1))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(false), Return(Status())));
result = mWrapper->setMode(Mode::CAMERA_STREAMING_HIGH, true);
ASSERT_TRUE(result.isUnsupported());
}
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 1be5a96..f8d9dc2 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -15,6 +15,7 @@
"CorrectedGyroSensor.cpp",
"Fusion.cpp",
"GravitySensor.cpp",
+ "HidlSensorHalWrapper.cpp",
"LinearAccelerationSensor.cpp",
"OrientationSensor.cpp",
"RecentEventLogger.cpp",
@@ -78,6 +79,8 @@
"libsensorprivacy",
"libpermission",
],
+
+ afdo: true,
}
cc_binary {
diff --git a/services/sensorservice/HidlSensorHalWrapper.cpp b/services/sensorservice/HidlSensorHalWrapper.cpp
new file mode 100644
index 0000000..dbb3da1
--- /dev/null
+++ b/services/sensorservice/HidlSensorHalWrapper.cpp
@@ -0,0 +1,562 @@
+/*
+ * 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.
+ */
+
+#include "HidlSensorHalWrapper.h"
+#include "android/hardware/sensors/2.0/types.h"
+#include "android/hardware/sensors/2.1/ISensorsCallback.h"
+#include "android/hardware/sensors/2.1/types.h"
+#include "convertV2_1.h"
+
+#include <android-base/logging.h>
+
+using android::hardware::hidl_vec;
+using android::hardware::sensors::V1_0::RateLevel;
+using android::hardware::sensors::V1_0::Result;
+using android::hardware::sensors::V1_0::SharedMemFormat;
+using android::hardware::sensors::V1_0::SharedMemInfo;
+using android::hardware::sensors::V1_0::SharedMemType;
+using android::hardware::sensors::V2_0::EventQueueFlagBits;
+using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
+using android::hardware::sensors::V2_1::Event;
+using android::hardware::sensors::V2_1::ISensorsCallback;
+using android::hardware::sensors::V2_1::implementation::convertFromSensorEvent;
+using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
+using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
+using android::hardware::sensors::V2_1::implementation::convertToSensor;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
+using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
+
+namespace android {
+
+namespace {
+
+status_t statusFromResult(Result result) {
+ switch (result) {
+ case Result::OK:
+ return OK;
+ case Result::BAD_VALUE:
+ return BAD_VALUE;
+ case Result::PERMISSION_DENIED:
+ return PERMISSION_DENIED;
+ case Result::INVALID_OPERATION:
+ return INVALID_OPERATION;
+ case Result::NO_MEMORY:
+ return NO_MEMORY;
+ }
+}
+
+template <typename EnumType>
+constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
+ return static_cast<typename std::underlying_type<EnumType>::type>(value);
+}
+
+enum EventQueueFlagBitsInternal : uint32_t {
+ INTERNAL_WAKE = 1 << 16,
+};
+
+} // anonymous namespace
+
+void SensorsHalDeathReceiver::serviceDied(
+ uint64_t /* cookie */, const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
+ ALOGW("Sensors HAL died, attempting to reconnect.");
+ mHidlSensorHalWrapper->prepareForReconnect();
+}
+
+struct SensorsCallback : public ISensorsCallback {
+ using Result = ::android::hardware::sensors::V1_0::Result;
+ using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
+
+ SensorsCallback(ISensorHalWrapper::SensorDeviceCallback* sensorDeviceCallback) {
+ mSensorDeviceCallback = sensorDeviceCallback;
+ }
+
+ Return<void> onDynamicSensorsConnected_2_1(
+ const hidl_vec<SensorInfo>& dynamicSensorsAdded) override {
+ std::vector<sensor_t> sensors;
+ for (const android::hardware::sensors::V2_1::SensorInfo& info : dynamicSensorsAdded) {
+ sensor_t sensor;
+ convertToSensor(info, &sensor);
+ sensors.push_back(sensor);
+ }
+
+ mSensorDeviceCallback->onDynamicSensorsConnected(sensors);
+ return Return<void>();
+ }
+
+ Return<void> onDynamicSensorsConnected(
+ const hidl_vec<android::hardware::sensors::V1_0::SensorInfo>& dynamicSensorsAdded)
+ override {
+ return onDynamicSensorsConnected_2_1(convertToNewSensorInfos(dynamicSensorsAdded));
+ }
+
+ Return<void> onDynamicSensorsDisconnected(
+ const hidl_vec<int32_t>& dynamicSensorHandlesRemoved) override {
+ mSensorDeviceCallback->onDynamicSensorsDisconnected(dynamicSensorHandlesRemoved);
+ return Return<void>();
+ }
+
+private:
+ ISensorHalWrapper::SensorDeviceCallback* mSensorDeviceCallback;
+};
+
+bool HidlSensorHalWrapper::supportsPolling() {
+ return mSensors->supportsPolling();
+}
+
+bool HidlSensorHalWrapper::supportsMessageQueues() {
+ return mSensors->supportsMessageQueues();
+}
+
+bool HidlSensorHalWrapper::connect(SensorDeviceCallback* callback) {
+ mSensorDeviceCallback = callback;
+ bool ret = connectHidlService();
+ if (mEventQueueFlag != nullptr) {
+ mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+ }
+ return ret;
+}
+
+void HidlSensorHalWrapper::prepareForReconnect() {
+ mReconnecting = true;
+ if (mEventQueueFlag != nullptr) {
+ mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+ }
+}
+
+ssize_t HidlSensorHalWrapper::poll(sensors_event_t* buffer, size_t count) {
+ ssize_t err;
+ int numHidlTransportErrors = 0;
+ bool hidlTransportError = false;
+
+ do {
+ auto ret = mSensors->poll(
+ count, [&](auto result, const auto& events, const auto& dynamicSensorsAdded) {
+ if (result == Result::OK) {
+ convertToSensorEventsAndQuantize(convertToNewEvents(events),
+ convertToNewSensorInfos(
+ dynamicSensorsAdded),
+ buffer);
+ err = (ssize_t)events.size();
+ } else {
+ err = statusFromResult(result);
+ }
+ });
+
+ if (ret.isOk()) {
+ hidlTransportError = false;
+ } else {
+ hidlTransportError = true;
+ numHidlTransportErrors++;
+ if (numHidlTransportErrors > 50) {
+ // Log error and bail
+ ALOGE("Max Hidl transport errors this cycle : %d", numHidlTransportErrors);
+ handleHidlDeath(ret.description());
+ } else {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+ }
+ } while (hidlTransportError);
+
+ if (numHidlTransportErrors > 0) {
+ ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
+ HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
+ mHidlTransportErrors.add(errLog);
+ mTotalHidlTransportErrors++;
+ }
+
+ return err;
+}
+
+ssize_t HidlSensorHalWrapper::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
+ ssize_t eventsRead = 0;
+ size_t availableEvents = mSensors->getEventQueue()->availableToRead();
+
+ if (availableEvents == 0) {
+ uint32_t eventFlagState = 0;
+
+ // Wait for events to become available. This is necessary so that the Event FMQ's read() is
+ // able to be called with the correct number of events to read. If the specified number of
+ // events is not available, then read() would return no events, possibly introducing
+ // additional latency in delivering events to applications.
+ if (mEventQueueFlag != nullptr) {
+ mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
+ asBaseType(INTERNAL_WAKE),
+ &eventFlagState);
+ }
+ availableEvents = mSensors->getEventQueue()->availableToRead();
+
+ if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
+ ALOGD("Event FMQ internal wake, returning from poll with no events");
+ return DEAD_OBJECT;
+ }
+ }
+
+ size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
+ if (eventsToRead > 0) {
+ if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
+ // Notify the Sensors HAL that sensor events have been read. This is required to support
+ // the use of writeBlocking by the Sensors HAL.
+ if (mEventQueueFlag != nullptr) {
+ mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
+ }
+
+ for (size_t i = 0; i < eventsToRead; i++) {
+ convertToSensorEvent(mEventBuffer[i], &buffer[i]);
+ android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i],
+ getResolutionForSensor(
+ buffer[i].sensor));
+ }
+ eventsRead = eventsToRead;
+ } else {
+ ALOGW("Failed to read %zu events, currently %zu events available", eventsToRead,
+ availableEvents);
+ }
+ }
+
+ return eventsRead;
+}
+
+std::vector<sensor_t> HidlSensorHalWrapper::getSensorsList() {
+ std::vector<sensor_t> sensorsFound;
+ if (mSensors != nullptr) {
+ checkReturn(mSensors->getSensorsList([&](const auto& list) {
+ for (size_t i = 0; i < list.size(); i++) {
+ sensor_t sensor;
+ convertToSensor(list[i], &sensor);
+ sensorsFound.push_back(sensor);
+
+ // Only disable all sensors on HAL 1.0 since HAL 2.0
+ // handles this in its initialize method
+ if (!mSensors->supportsMessageQueues()) {
+ checkReturn(mSensors->activate(list[i].sensorHandle, 0 /* enabled */));
+ }
+ }
+ }));
+ }
+
+ return sensorsFound;
+}
+
+status_t HidlSensorHalWrapper::setOperationMode(SensorService::Mode mode) {
+ if (mSensors == nullptr) return NO_INIT;
+ return checkReturnAndGetStatus(
+ mSensors->setOperationMode(static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+}
+
+status_t HidlSensorHalWrapper::activate(int32_t sensorHandle, bool enabled) {
+ if (mSensors == nullptr) return NO_INIT;
+ return checkReturnAndGetStatus(mSensors->activate(sensorHandle, enabled));
+}
+
+status_t HidlSensorHalWrapper::batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) {
+ if (mSensors == nullptr) return NO_INIT;
+ return checkReturnAndGetStatus(
+ mSensors->batch(sensorHandle, samplingPeriodNs, maxReportLatencyNs));
+}
+
+status_t HidlSensorHalWrapper::flush(int32_t sensorHandle) {
+ if (mSensors == nullptr) return NO_INIT;
+ return checkReturnAndGetStatus(mSensors->flush(sensorHandle));
+}
+
+status_t HidlSensorHalWrapper::injectSensorData(const sensors_event_t* event) {
+ if (mSensors == nullptr) return NO_INIT;
+
+ Event ev;
+ convertFromSensorEvent(*event, &ev);
+ return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
+}
+
+status_t HidlSensorHalWrapper::registerDirectChannel(const sensors_direct_mem_t* memory,
+ int32_t* /*channelHandle*/) {
+ if (mSensors == nullptr) return NO_INIT;
+
+ SharedMemType type;
+ switch (memory->type) {
+ case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
+ type = SharedMemType::ASHMEM;
+ break;
+ case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
+ type = SharedMemType::GRALLOC;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ SharedMemFormat format;
+ if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
+ return BAD_VALUE;
+ }
+ format = SharedMemFormat::SENSORS_EVENT;
+
+ SharedMemInfo mem = {
+ .type = type,
+ .format = format,
+ .size = static_cast<uint32_t>(memory->size),
+ .memoryHandle = memory->handle,
+ };
+
+ status_t ret;
+ checkReturn(mSensors->registerDirectChannel(mem, [&ret](auto result, auto channelHandle) {
+ if (result == Result::OK) {
+ ret = channelHandle;
+ } else {
+ ret = statusFromResult(result);
+ }
+ }));
+ return ret;
+}
+
+status_t HidlSensorHalWrapper::unregisterDirectChannel(int32_t channelHandle) {
+ if (mSensors == nullptr) return NO_INIT;
+ return checkReturnAndGetStatus(mSensors->unregisterDirectChannel(channelHandle));
+}
+
+status_t HidlSensorHalWrapper::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config) {
+ if (mSensors == nullptr) return NO_INIT;
+
+ RateLevel rate;
+ switch (config->rate_level) {
+ case SENSOR_DIRECT_RATE_STOP:
+ rate = RateLevel::STOP;
+ break;
+ case SENSOR_DIRECT_RATE_NORMAL:
+ rate = RateLevel::NORMAL;
+ break;
+ case SENSOR_DIRECT_RATE_FAST:
+ rate = RateLevel::FAST;
+ break;
+ case SENSOR_DIRECT_RATE_VERY_FAST:
+ rate = RateLevel::VERY_FAST;
+ break;
+ default:
+ return BAD_VALUE;
+ }
+
+ status_t ret;
+ checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
+ [&ret, rate](auto result, auto token) {
+ if (rate == RateLevel::STOP) {
+ ret = statusFromResult(result);
+ } else {
+ if (result == Result::OK) {
+ ret = token;
+ } else {
+ ret = statusFromResult(result);
+ }
+ }
+ }));
+
+ return ret;
+}
+
+void HidlSensorHalWrapper::writeWakeLockHandled(uint32_t count) {
+ if (mWakeLockQueue->write(&count)) {
+ mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
+ } else {
+ ALOGW("Failed to write wake lock handled");
+ }
+}
+
+status_t HidlSensorHalWrapper::checkReturnAndGetStatus(const hardware::Return<Result>& ret) {
+ checkReturn(ret);
+ return (!ret.isOk()) ? DEAD_OBJECT : statusFromResult(ret);
+}
+
+void HidlSensorHalWrapper::handleHidlDeath(const std::string& detail) {
+ if (!mSensors->supportsMessageQueues()) {
+ // restart is the only option at present.
+ LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
+ } else {
+ ALOGD("ISensors HAL died, death recipient will attempt reconnect");
+ }
+}
+
+bool HidlSensorHalWrapper::connectHidlService() {
+ HalConnectionStatus status = connectHidlServiceV2_1();
+ if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+ status = connectHidlServiceV2_0();
+ }
+
+ if (status == HalConnectionStatus::DOES_NOT_EXIST) {
+ status = connectHidlServiceV1_0();
+ }
+ return (status == HalConnectionStatus::CONNECTED);
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV1_0() {
+ // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
+ size_t retry = 10;
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+
+ while (retry-- > 0) {
+ sp<android::hardware::sensors::V1_0::ISensors> sensors =
+ android::hardware::sensors::V1_0::ISensors::getService();
+ if (sensors == nullptr) {
+ // no sensor hidl service found
+ connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+ break;
+ }
+
+ mSensors = new ISensorsWrapperV1_0(sensors);
+ mRestartWaiter->reset();
+ // Poke ISensor service. If it has lingering connection from previous generation of
+ // system server, it will kill itself. There is no intention to handle the poll result,
+ // which will be done since the size is 0.
+ if (mSensors->poll(0, [](auto, const auto&, const auto&) {}).isOk()) {
+ // ok to continue
+ connectionStatus = HalConnectionStatus::CONNECTED;
+ break;
+ }
+
+ // hidl service is restarting, pointer is invalid.
+ mSensors = nullptr;
+ connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+ ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
+ mRestartWaiter->wait();
+ }
+
+ return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV2_0() {
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+ sp<android::hardware::sensors::V2_0::ISensors> sensors =
+ android::hardware::sensors::V2_0::ISensors::getService();
+
+ if (sensors == nullptr) {
+ connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+ } else {
+ mSensors = new ISensorsWrapperV2_0(sensors);
+ connectionStatus = initializeHidlServiceV2_X();
+ }
+
+ return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::connectHidlServiceV2_1() {
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+ sp<android::hardware::sensors::V2_1::ISensors> sensors =
+ android::hardware::sensors::V2_1::ISensors::getService();
+
+ if (sensors == nullptr) {
+ connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
+ } else {
+ mSensors = new ISensorsWrapperV2_1(sensors);
+ connectionStatus = initializeHidlServiceV2_X();
+ }
+
+ return connectionStatus;
+}
+
+ISensorHalWrapper::HalConnectionStatus HidlSensorHalWrapper::initializeHidlServiceV2_X() {
+ HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
+
+ mWakeLockQueue =
+ std::make_unique<WakeLockQueue>(SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
+ true /* configureEventFlagWord */);
+
+ hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+ hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(),
+ &mEventQueueFlag);
+
+ hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+ hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(), &mWakeLockQueueFlag);
+
+ CHECK(mSensors != nullptr && mWakeLockQueue != nullptr && mEventQueueFlag != nullptr &&
+ mWakeLockQueueFlag != nullptr);
+
+ mCallback = new SensorsCallback(mSensorDeviceCallback);
+ status_t status =
+ checkReturnAndGetStatus(mSensors->initialize(*mWakeLockQueue->getDesc(), mCallback));
+
+ if (status != NO_ERROR) {
+ connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
+ ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
+ } else {
+ connectionStatus = HalConnectionStatus::CONNECTED;
+ mSensorsHalDeathReceiver = new SensorsHalDeathReceiver(this);
+ mSensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
+ }
+
+ return connectionStatus;
+}
+
+void HidlSensorHalWrapper::convertToSensorEvent(const Event& src, sensors_event_t* dst) {
+ android::hardware::sensors::V2_1::implementation::convertToSensorEvent(src, dst);
+
+ if (src.sensorType == android::hardware::sensors::V2_1::SensorType::DYNAMIC_SENSOR_META) {
+ const hardware::sensors::V1_0::DynamicSensorInfo& dyn = src.u.dynamic;
+
+ dst->dynamic_sensor_meta.connected = dyn.connected;
+ dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
+ if (dyn.connected) {
+ std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
+ // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked since it
+ // can be received out of order from this event due to a bug in the HIDL spec that
+ // marks it as oneway.
+ auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+ if (it == mConnectedDynamicSensors.end()) {
+ mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT, [&, dyn] {
+ return mConnectedDynamicSensors.find(dyn.sensorHandle) !=
+ mConnectedDynamicSensors.end();
+ });
+ it = mConnectedDynamicSensors.find(dyn.sensorHandle);
+ CHECK(it != mConnectedDynamicSensors.end());
+ }
+
+ dst->dynamic_sensor_meta.sensor = &it->second;
+
+ memcpy(dst->dynamic_sensor_meta.uuid, dyn.uuid.data(),
+ sizeof(dst->dynamic_sensor_meta.uuid));
+ }
+ }
+}
+
+void HidlSensorHalWrapper::convertToSensorEventsAndQuantize(
+ const hidl_vec<Event>& src, const hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ sensors_event_t* dst) {
+ if (dynamicSensorsAdded.size() > 0 && mCallback != nullptr) {
+ mCallback->onDynamicSensorsConnected_2_1(dynamicSensorsAdded);
+ }
+
+ for (size_t i = 0; i < src.size(); ++i) {
+ android::hardware::sensors::V2_1::implementation::convertToSensorEvent(src[i], &dst[i]);
+ android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i],
+ getResolutionForSensor(
+ dst[i].sensor));
+ }
+}
+
+float HidlSensorHalWrapper::getResolutionForSensor(int sensorHandle) {
+ for (size_t i = 0; i < mSensorList.size(); i++) {
+ if (sensorHandle == mSensorList[i].handle) {
+ return mSensorList[i].resolution;
+ }
+ }
+
+ auto it = mConnectedDynamicSensors.find(sensorHandle);
+ if (it != mConnectedDynamicSensors.end()) {
+ return it->second.resolution;
+ }
+
+ return 0;
+}
+
+} // namespace android
diff --git a/services/sensorservice/HidlSensorHalWrapper.h b/services/sensorservice/HidlSensorHalWrapper.h
new file mode 100644
index 0000000..030247f
--- /dev/null
+++ b/services/sensorservice/HidlSensorHalWrapper.h
@@ -0,0 +1,182 @@
+/*
+ * 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_HIDL_SENSOR_HAL_WRAPPER_H
+#define ANDROID_HIDL_SENSOR_HAL_WRAPPER_H
+
+#include <sensor/SensorEventQueue.h>
+#include <utils/Singleton.h>
+
+#include "ISensorHalWrapper.h"
+
+#include "ISensorsWrapper.h"
+#include "SensorDeviceUtils.h"
+
+namespace android {
+
+using android::hardware::sensors::V1_0::Result;
+using android::hardware::sensors::V2_1::Event;
+using android::hardware::sensors::V2_1::SensorInfo;
+
+class HidlTransportErrorLog {
+public:
+ HidlTransportErrorLog() {
+ mTs = 0;
+ mCount = 0;
+ }
+
+ HidlTransportErrorLog(time_t ts, int count) {
+ mTs = ts;
+ mCount = count;
+ }
+
+ String8 toString() const {
+ String8 result;
+ struct tm* timeInfo = localtime(&mTs);
+ result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
+ timeInfo->tm_sec, mCount);
+ return result;
+ }
+
+private:
+ time_t mTs; // timestamp of the error
+ int mCount; // number of transport errors observed
+};
+
+class SensorsHalDeathReceiver : public android::hardware::hidl_death_recipient {
+public:
+ SensorsHalDeathReceiver(ISensorHalWrapper* wrapper) : mHidlSensorHalWrapper(wrapper) {}
+
+ virtual void serviceDied(uint64_t cookie,
+ const wp<::android::hidl::base::V1_0::IBase>& service) override;
+
+private:
+ ISensorHalWrapper* mHidlSensorHalWrapper;
+};
+
+class HidlSensorHalWrapper : public ISensorHalWrapper {
+public:
+ HidlSensorHalWrapper()
+ : mHidlTransportErrors(20),
+ mTotalHidlTransportErrors(0),
+ mRestartWaiter(new SensorDeviceUtils::HidlServiceRegistrationWaiter()),
+ mEventQueueFlag(nullptr),
+ mWakeLockQueueFlag(nullptr) {}
+
+ ~HidlSensorHalWrapper() override {
+ if (mEventQueueFlag != nullptr) {
+ hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
+ mEventQueueFlag = nullptr;
+ }
+ if (mWakeLockQueueFlag != nullptr) {
+ hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
+ mWakeLockQueueFlag = nullptr;
+ }
+ }
+ virtual bool connect(SensorDeviceCallback* callback) override;
+
+ virtual void prepareForReconnect() override;
+
+ virtual bool supportsPolling() override;
+
+ virtual bool supportsMessageQueues() override;
+
+ virtual ssize_t poll(sensors_event_t* buffer, size_t count) override;
+
+ virtual ssize_t pollFmq(sensors_event_t* buffer, size_t count) override;
+
+ virtual std::vector<sensor_t> getSensorsList() override;
+
+ virtual status_t setOperationMode(SensorService::Mode mode) override;
+
+ virtual status_t activate(int32_t sensorHandle, bool enabled) override;
+
+ virtual status_t batch(int32_t sensorHandle, int64_t samplingPeriodNs,
+ int64_t maxReportLatencyNs) override;
+
+ virtual status_t flush(int32_t sensorHandle) override;
+
+ virtual status_t injectSensorData(const sensors_event_t* event) override;
+
+ virtual status_t registerDirectChannel(const sensors_direct_mem_t* memory,
+ int32_t* channelHandle) override;
+
+ virtual status_t unregisterDirectChannel(int32_t channelHandle) override;
+
+ virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config) override;
+
+ virtual void writeWakeLockHandled(uint32_t count) override;
+
+private:
+ sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
+ sp<::android::hardware::sensors::V2_1::ISensorsCallback> mCallback;
+ std::vector<sensor_t> mSensorList;
+ std::unordered_map<int32_t, sensor_t> mConnectedDynamicSensors;
+
+ std::mutex mDynamicSensorsMutex;
+ std::condition_variable mDynamicSensorsCv;
+ static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5};
+
+ // Keep track of any hidl transport failures
+ SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
+ int mTotalHidlTransportErrors;
+
+ SensorDeviceCallback* mSensorDeviceCallback = nullptr;
+
+ // TODO(b/67425500): remove waiter after bug is resolved.
+ sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
+
+ template <typename T>
+ void checkReturn(const hardware::Return<T>& ret) {
+ if (!ret.isOk()) {
+ handleHidlDeath(ret.description());
+ }
+ }
+
+ status_t checkReturnAndGetStatus(const hardware::Return<Result>& ret);
+
+ void handleHidlDeath(const std::string& detail);
+
+ void convertToSensorEvent(const Event& src, sensors_event_t* dst);
+
+ void convertToSensorEventsAndQuantize(const hardware::hidl_vec<Event>& src,
+ const hardware::hidl_vec<SensorInfo>& dynamicSensorsAdded,
+ sensors_event_t* dst);
+
+ bool connectHidlService();
+
+ HalConnectionStatus connectHidlServiceV1_0();
+ HalConnectionStatus connectHidlServiceV2_0();
+ HalConnectionStatus connectHidlServiceV2_1();
+ HalConnectionStatus initializeHidlServiceV2_X();
+
+ typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
+ std::unique_ptr<WakeLockQueue> mWakeLockQueue;
+
+ float getResolutionForSensor(int sensorHandle);
+
+ hardware::EventFlag* mEventQueueFlag;
+ hardware::EventFlag* mWakeLockQueueFlag;
+
+ std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
+
+ sp<SensorsHalDeathReceiver> mSensorsHalDeathReceiver;
+};
+
+} // namespace android
+
+#endif // ANDROID_HIDL_SENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/ISensorHalWrapper.h b/services/sensorservice/ISensorHalWrapper.h
new file mode 100644
index 0000000..3d33540
--- /dev/null
+++ b/services/sensorservice/ISensorHalWrapper.h
@@ -0,0 +1,104 @@
+/*
+ * 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 SensorDeviceCallback {
+ public:
+ virtual void onDynamicSensorsConnected(
+ const std::vector<sensor_t> &dynamicSensorsAdded) = 0;
+
+ virtual void onDynamicSensorsDisconnected(
+ const std::vector<int32_t> &dynamicSensorHandlesRemoved) = 0;
+
+ virtual ~SensorDeviceCallback(){};
+ };
+
+ enum HalConnectionStatus {
+ CONNECTED, // Successfully connected to the HAL
+ DOES_NOT_EXIST, // Could not find the HAL
+ FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
+ UNKNOWN,
+ };
+
+ virtual ~ISensorHalWrapper(){};
+
+ /**
+ * Connects to the underlying sensors HAL. This should also be used for any reconnections
+ * due to HAL resets.
+ */
+ virtual bool connect(SensorDeviceCallback *callback) = 0;
+
+ virtual void prepareForReconnect() = 0;
+
+ virtual bool supportsPolling() = 0;
+
+ virtual bool supportsMessageQueues() = 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;
+
+ virtual ssize_t pollFmq(sensors_event_t *buffer, size_t maxNumEventsToRead) = 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 status_t unregisterDirectChannel(int32_t channelHandle) = 0;
+
+ virtual status_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t *config) = 0;
+
+ virtual void writeWakeLockHandled(uint32_t count) = 0;
+
+ std::atomic_bool mReconnecting = false;
+};
+
+} // namespace android
+
+#endif // ANDROID_ISENSOR_HAL_WRAPPER_H
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index db1a1cc..84a1076 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -17,38 +17,24 @@
#include "SensorDevice.h"
#include "android/hardware/sensors/2.0/types.h"
-#include "android/hardware/sensors/2.1/ISensorsCallback.h"
#include "android/hardware/sensors/2.1/types.h"
#include "convertV2_1.h"
#include <android-base/logging.h>
#include <android/util/ProtoOutputStream.h>
+#include <cutils/atomic.h>
#include <frameworks/base/core/proto/android/service/sensor_service.proto.h>
#include <sensors/convert.h>
-#include <cutils/atomic.h>
#include <utils/Errors.h>
#include <utils/Singleton.h>
-#include <cstddef>
#include <chrono>
#include <cinttypes>
+#include <cstddef>
#include <thread>
using namespace android::hardware::sensors;
-using namespace android::hardware::sensors::V1_0;
-using namespace android::hardware::sensors::V1_0::implementation;
-using android::hardware::sensors::V2_0::EventQueueFlagBits;
-using android::hardware::sensors::V2_0::WakeLockQueueFlagBits;
-using android::hardware::sensors::V2_1::ISensorsCallback;
-using android::hardware::sensors::V2_1::implementation::convertToOldSensorInfo;
-using android::hardware::sensors::V2_1::implementation::convertToNewSensorInfos;
-using android::hardware::sensors::V2_1::implementation::convertToNewEvents;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV1_0;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_0;
-using android::hardware::sensors::V2_1::implementation::ISensorsWrapperV2_1;
-using android::hardware::hidl_vec;
using android::hardware::Return;
-using android::SensorDeviceUtils::HidlServiceRegistrationWaiter;
using android::util::ProtoOutputStream;
namespace android {
@@ -58,22 +44,7 @@
namespace {
-status_t statusFromResult(Result result) {
- switch (result) {
- case Result::OK:
- return OK;
- case Result::BAD_VALUE:
- return BAD_VALUE;
- case Result::PERMISSION_DENIED:
- return PERMISSION_DENIED;
- case Result::INVALID_OPERATION:
- return INVALID_OPERATION;
- case Result::NO_MEMORY:
- return NO_MEMORY;
- }
-}
-
-template<typename EnumType>
+template <typename EnumType>
constexpr typename std::underlying_type<EnumType>::type asBaseType(EnumType value) {
return static_cast<typename std::underlying_type<EnumType>::type>(value);
}
@@ -81,244 +52,101 @@
// Used internally by the framework to wake the Event FMQ. These values must start after
// the last value of EventQueueFlagBits
enum EventQueueFlagBitsInternal : uint32_t {
- INTERNAL_WAKE = 1 << 16,
+ INTERNAL_WAKE = 1 << 16,
};
-} // anonymous namespace
-
-void SensorsHalDeathReceivier::serviceDied(
- uint64_t /* cookie */,
- const wp<::android::hidl::base::V1_0::IBase>& /* service */) {
- ALOGW("Sensors HAL died, attempting to reconnect.");
- SensorDevice::getInstance().prepareForReconnect();
-}
-
-struct SensorsCallback : public ISensorsCallback {
- using Result = ::android::hardware::sensors::V1_0::Result;
- using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
-
- Return<void> onDynamicSensorsConnected_2_1(
- const hidl_vec<SensorInfo> &dynamicSensorsAdded) override {
- return SensorDevice::getInstance().onDynamicSensorsConnected(dynamicSensorsAdded);
- }
-
- Return<void> onDynamicSensorsConnected(
- const hidl_vec<V1_0::SensorInfo> &dynamicSensorsAdded) override {
- return SensorDevice::getInstance().onDynamicSensorsConnected(
- convertToNewSensorInfos(dynamicSensorsAdded));
- }
-
- Return<void> onDynamicSensorsDisconnected(
- const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) override {
- return SensorDevice::getInstance().onDynamicSensorsDisconnected(
- dynamicSensorHandlesRemoved);
- }
+enum DevicePrivateBase : int32_t {
+ DEVICE_PRIVATE_BASE = 65536,
};
-SensorDevice::SensorDevice()
- : mHidlTransportErrors(20),
- mRestartWaiter(new HidlServiceRegistrationWaiter()),
- mEventQueueFlag(nullptr),
- mWakeLockQueueFlag(nullptr),
- mReconnecting(false) {
- if (!connectHidlService()) {
+} // anonymous namespace
+
+SensorDevice::SensorDevice() {
+ if (!connectHalService()) {
return;
}
initializeSensorList();
- mIsDirectReportSupported =
- (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION);
+ mIsDirectReportSupported = (mHalWrapper->unregisterDirectChannel(-1) != INVALID_OPERATION);
}
void SensorDevice::initializeSensorList() {
- checkReturn(mSensors->getSensorsList(
- [&](const auto &list) {
- const size_t count = list.size();
+ if (mHalWrapper == nullptr) {
+ return;
+ }
- mActivationCount.setCapacity(count);
- Info model;
- for (size_t i=0 ; i < count; i++) {
- sensor_t sensor;
- convertToSensor(convertToOldSensorInfo(list[i]), &sensor);
+ auto list = mHalWrapper->getSensorsList();
+ const size_t count = list.size();
- if (sensor.type < static_cast<int>(SensorType::DEVICE_PRIVATE_BASE)) {
- sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
+ mActivationCount.setCapacity(count);
+ Info model;
+ for (size_t i = 0; i < count; i++) {
+ sensor_t sensor = list[i];
- // Some sensors don't have a default resolution and will be left at 0.
- // Don't crash in this case since CTS will verify that devices don't go to
- // production with a resolution of 0.
- if (sensor.resolution != 0) {
- float quantizedRange = sensor.maxRange;
- SensorDeviceUtils::quantizeValue(
- &quantizedRange, sensor.resolution, /*factor=*/ 1);
- // Only rewrite maxRange if the requantization produced a "significant"
- // change, which is fairly arbitrarily defined as resolution / 8.
- // Smaller deltas are permitted, as they may simply be due to floating
- // point representation error, etc.
- if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
- ALOGW("%s's max range %.12f is not a multiple of the resolution "
- "%.12f - updated to %.12f", sensor.name, sensor.maxRange,
- sensor.resolution, quantizedRange);
- sensor.maxRange = quantizedRange;
- }
- } else {
- // Don't crash here or the device will go into a crashloop.
- ALOGW("%s should have a non-zero resolution", sensor.name);
- }
- }
+ if (sensor.type < DEVICE_PRIVATE_BASE) {
+ sensor.resolution = SensorDeviceUtils::resolutionForSensor(sensor);
- // Check and clamp power if it is 0 (or close)
- constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
- if (sensor.power < MIN_POWER_MA) {
- ALOGI("%s's reported power %f invalid, clamped to %f",
- sensor.name, sensor.power, MIN_POWER_MA);
- sensor.power = MIN_POWER_MA;
- }
- mSensorList.push_back(sensor);
-
- mActivationCount.add(list[i].sensorHandle, model);
-
- // Only disable all sensors on HAL 1.0 since HAL 2.0
- // handles this in its initialize method
- if (!mSensors->supportsMessageQueues()) {
- checkReturn(mSensors->activate(list[i].sensorHandle,
- 0 /* enabled */));
- }
+ // Some sensors don't have a default resolution and will be left at 0.
+ // Don't crash in this case since CTS will verify that devices don't go to
+ // production with a resolution of 0.
+ if (sensor.resolution != 0) {
+ float quantizedRange = sensor.maxRange;
+ SensorDeviceUtils::quantizeValue(&quantizedRange, sensor.resolution,
+ /*factor=*/1);
+ // Only rewrite maxRange if the requantization produced a "significant"
+ // change, which is fairly arbitrarily defined as resolution / 8.
+ // Smaller deltas are permitted, as they may simply be due to floating
+ // point representation error, etc.
+ if (fabsf(sensor.maxRange - quantizedRange) > sensor.resolution / 8) {
+ ALOGW("%s's max range %.12f is not a multiple of the resolution "
+ "%.12f - updated to %.12f",
+ sensor.name, sensor.maxRange, sensor.resolution, quantizedRange);
+ sensor.maxRange = quantizedRange;
}
- }));
-}
-
-SensorDevice::~SensorDevice() {
- if (mEventQueueFlag != nullptr) {
- hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
- mEventQueueFlag = nullptr;
- }
-
- if (mWakeLockQueueFlag != nullptr) {
- hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
- mWakeLockQueueFlag = nullptr;
- }
-}
-
-bool SensorDevice::connectHidlService() {
- HalConnectionStatus status = connectHidlServiceV2_1();
- if (status == HalConnectionStatus::DOES_NOT_EXIST) {
- status = connectHidlServiceV2_0();
- }
-
- if (status == HalConnectionStatus::DOES_NOT_EXIST) {
- status = connectHidlServiceV1_0();
- }
- return (status == HalConnectionStatus::CONNECTED);
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV1_0() {
- // SensorDevice will wait for HAL service to start if HAL is declared in device manifest.
- size_t retry = 10;
- HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-
- while (retry-- > 0) {
- sp<V1_0::ISensors> sensors = V1_0::ISensors::getService();
- if (sensors == nullptr) {
- // no sensor hidl service found
- connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
- break;
+ } else {
+ // Don't crash here or the device will go into a crashloop.
+ ALOGW("%s should have a non-zero resolution", sensor.name);
+ }
}
- mSensors = new ISensorsWrapperV1_0(sensors);
- mRestartWaiter->reset();
- // Poke ISensor service. If it has lingering connection from previous generation of
- // system server, it will kill itself. There is no intention to handle the poll result,
- // which will be done since the size is 0.
- if(mSensors->poll(0, [](auto, const auto &, const auto &) {}).isOk()) {
- // ok to continue
- connectionStatus = HalConnectionStatus::CONNECTED;
- break;
+ // Check and clamp power if it is 0 (or close)
+ constexpr float MIN_POWER_MA = 0.001; // 1 microAmp
+ if (sensor.power < MIN_POWER_MA) {
+ ALOGI("%s's reported power %f invalid, clamped to %f", sensor.name, sensor.power,
+ MIN_POWER_MA);
+ sensor.power = MIN_POWER_MA;
}
+ mSensorList.push_back(sensor);
- // hidl service is restarting, pointer is invalid.
- mSensors = nullptr;
- connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
- ALOGI("%s unsuccessful, remaining retry %zu.", __FUNCTION__, retry);
- mRestartWaiter->wait();
+ mActivationCount.add(list[i].handle, model);
+
+ // Only disable all sensors on HAL 1.0 since HAL 2.0
+ // handles this in its initialize method
+ if (!mHalWrapper->supportsMessageQueues()) {
+ mHalWrapper->activate(list[i].handle, 0 /* enabled */);
+ }
}
-
- return connectionStatus;
}
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_0() {
- HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
- sp<V2_0::ISensors> sensors = V2_0::ISensors::getService();
+SensorDevice::~SensorDevice() {}
- if (sensors == nullptr) {
- connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
- } else {
- mSensors = new ISensorsWrapperV2_0(sensors);
- connectionStatus = initializeHidlServiceV2_X();
+bool SensorDevice::connectHalService() {
+ std::unique_ptr<ISensorHalWrapper> hidl_wrapper = std::make_unique<HidlSensorHalWrapper>();
+ if (hidl_wrapper->connect(this)) {
+ mHalWrapper = std::move(hidl_wrapper);
+ return true;
}
-
- return connectionStatus;
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::connectHidlServiceV2_1() {
- HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
- sp<V2_1::ISensors> sensors = V2_1::ISensors::getService();
-
- if (sensors == nullptr) {
- connectionStatus = HalConnectionStatus::DOES_NOT_EXIST;
- } else {
- mSensors = new ISensorsWrapperV2_1(sensors);
- connectionStatus = initializeHidlServiceV2_X();
- }
-
- return connectionStatus;
-}
-
-SensorDevice::HalConnectionStatus SensorDevice::initializeHidlServiceV2_X() {
- HalConnectionStatus connectionStatus = HalConnectionStatus::UNKNOWN;
-
- mWakeLockQueue = std::make_unique<WakeLockQueue>(
- SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT,
- true /* configureEventFlagWord */);
-
- hardware::EventFlag::deleteEventFlag(&mEventQueueFlag);
- hardware::EventFlag::createEventFlag(mSensors->getEventQueue()->getEventFlagWord(), &mEventQueueFlag);
-
- hardware::EventFlag::deleteEventFlag(&mWakeLockQueueFlag);
- hardware::EventFlag::createEventFlag(mWakeLockQueue->getEventFlagWord(),
- &mWakeLockQueueFlag);
-
- CHECK(mSensors != nullptr && mWakeLockQueue != nullptr &&
- mEventQueueFlag != nullptr && mWakeLockQueueFlag != nullptr);
-
- status_t status = checkReturnAndGetStatus(mSensors->initialize(
- *mWakeLockQueue->getDesc(),
- new SensorsCallback()));
-
- if (status != NO_ERROR) {
- connectionStatus = HalConnectionStatus::FAILED_TO_CONNECT;
- ALOGE("Failed to initialize Sensors HAL (%s)", strerror(-status));
- } else {
- connectionStatus = HalConnectionStatus::CONNECTED;
- mSensorsHalDeathReceiver = new SensorsHalDeathReceivier();
- mSensors->linkToDeath(mSensorsHalDeathReceiver, 0 /* cookie */);
- }
-
- return connectionStatus;
+ // TODO: check aidl connection;
+ return false;
}
void SensorDevice::prepareForReconnect() {
- mReconnecting = true;
-
- // Wake up the polling thread so it returns and allows the SensorService to initiate
- // a reconnect.
- mEventQueueFlag->wake(asBaseType(INTERNAL_WAKE));
+ mHalWrapper->prepareForReconnect();
}
void SensorDevice::reconnect() {
Mutex::Autolock _l(mLock);
- mSensors = nullptr;
auto previousActivations = mActivationCount;
auto previousSensorList = mSensorList;
@@ -326,7 +154,7 @@
mActivationCount.clear();
mSensorList.clear();
- if (connectHidlServiceV2_0() == HalConnectionStatus::CONNECTED) {
+ if (mHalWrapper->connect(this)) {
initializeSensorList();
if (sensorHandlesChanged(previousSensorList, mSensorList)) {
@@ -335,11 +163,11 @@
reactivateSensors(previousActivations);
}
}
- mReconnecting = false;
+ mHalWrapper->mReconnecting = false;
}
-bool SensorDevice::sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
- const Vector<sensor_t>& newSensorList) {
+bool SensorDevice::sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+ const std::vector<sensor_t>& newSensorList) {
bool didChange = false;
if (oldSensorList.size() != newSensorList.size()) {
@@ -375,19 +203,17 @@
bool SensorDevice::sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor) {
bool equivalent = true;
if (prevSensor.handle != newSensor.handle ||
- (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
- (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
- (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
- (prevSensor.version != newSensor.version) ||
- (prevSensor.type != newSensor.type) ||
- (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
- (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
- (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
- (prevSensor.minDelay != newSensor.minDelay) ||
- (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
- (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
- (prevSensor.maxDelay != newSensor.maxDelay) ||
- (prevSensor.flags != newSensor.flags)) {
+ (strcmp(prevSensor.vendor, newSensor.vendor) != 0) ||
+ (strcmp(prevSensor.stringType, newSensor.stringType) != 0) ||
+ (strcmp(prevSensor.requiredPermission, newSensor.requiredPermission) != 0) ||
+ (prevSensor.version != newSensor.version) || (prevSensor.type != newSensor.type) ||
+ (std::abs(prevSensor.maxRange - newSensor.maxRange) > 0.001f) ||
+ (std::abs(prevSensor.resolution - newSensor.resolution) > 0.001f) ||
+ (std::abs(prevSensor.power - newSensor.power) > 0.001f) ||
+ (prevSensor.minDelay != newSensor.minDelay) ||
+ (prevSensor.fifoReservedEventCount != newSensor.fifoReservedEventCount) ||
+ (prevSensor.fifoMaxEventCount != newSensor.fifoMaxEventCount) ||
+ (prevSensor.maxDelay != newSensor.maxDelay) || (prevSensor.flags != newSensor.flags)) {
equivalent = false;
}
return equivalent;
@@ -405,7 +231,7 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& batchParams = info.batchParams[j];
status_t res = batchLocked(info.batchParams.keyAt(j), handle, 0 /* flags */,
- batchParams.mTSample, batchParams.mTBatch);
+ batchParams.mTSample, batchParams.mTBatch);
if (res == NO_ERROR) {
activateLocked(info.batchParams.keyAt(j), handle, true /* enabled */);
@@ -419,21 +245,21 @@
if (connected) {
Info model;
mActivationCount.add(handle, model);
- checkReturn(mSensors->activate(handle, 0 /* enabled */));
+ mHalWrapper->activate(handle, 0 /* enabled */);
} else {
mActivationCount.removeItem(handle);
}
}
std::string SensorDevice::dump() const {
- if (mSensors == nullptr) return "HAL not initialized\n";
+ if (mHalWrapper == nullptr) return "HAL not initialized\n";
String8 result;
result.appendFormat("Total %zu h/w sensors, %zu running %zu disabled clients:\n",
mSensorList.size(), mActivationCount.size(), mDisabledClients.size());
Mutex::Autolock _l(mLock);
- for (const auto & s : mSensorList) {
+ for (const auto& s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
if (info.numActiveClients() == 0) continue;
@@ -444,8 +270,9 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
result.appendFormat("%.1f%s%s", params.mTSample / 1e6f,
- isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
- (j < info.batchParams.size() - 1) ? ", " : "");
+ isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+ : "",
+ (j < info.batchParams.size() - 1) ? ", " : "");
}
result.appendFormat("}, selected = %.2f ms; ", info.bestBatchParams.mTSample / 1e6f);
@@ -453,8 +280,9 @@
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
result.appendFormat("%.1f%s%s", params.mTBatch / 1e6f,
- isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)" : "",
- (j < info.batchParams.size() - 1) ? ", " : "");
+ isClientDisabledLocked(info.batchParams.keyAt(j)) ? "(disabled)"
+ : "",
+ (j < info.batchParams.size() - 1) ? ", " : "");
}
result.appendFormat("}, selected = %.2f ms\n", info.bestBatchParams.mTBatch / 1e6f);
}
@@ -471,30 +299,30 @@
*/
void SensorDevice::dump(ProtoOutputStream* proto) const {
using namespace service::SensorDeviceProto;
- if (mSensors == nullptr) {
- proto->write(INITIALIZED , false);
+ if (mHalWrapper == nullptr) {
+ proto->write(INITIALIZED, false);
return;
}
- proto->write(INITIALIZED , true);
- proto->write(TOTAL_SENSORS , int(mSensorList.size()));
- proto->write(ACTIVE_SENSORS , int(mActivationCount.size()));
+ proto->write(INITIALIZED, true);
+ proto->write(TOTAL_SENSORS, int(mSensorList.size()));
+ proto->write(ACTIVE_SENSORS, int(mActivationCount.size()));
Mutex::Autolock _l(mLock);
- for (const auto & s : mSensorList) {
+ for (const auto& s : mSensorList) {
int32_t handle = s.handle;
const Info& info = mActivationCount.valueFor(handle);
if (info.numActiveClients() == 0) continue;
uint64_t token = proto->start(SENSORS);
- proto->write(SensorProto::HANDLE , handle);
- proto->write(SensorProto::ACTIVE_COUNT , int(info.batchParams.size()));
+ proto->write(SensorProto::HANDLE, handle);
+ proto->write(SensorProto::ACTIVE_COUNT, int(info.batchParams.size()));
for (size_t j = 0; j < info.batchParams.size(); j++) {
const BatchParams& params = info.batchParams[j];
- proto->write(SensorProto::SAMPLING_PERIOD_MS , params.mTSample / 1e6f);
- proto->write(SensorProto::BATCHING_PERIOD_MS , params.mTBatch / 1e6f);
+ proto->write(SensorProto::SAMPLING_PERIOD_MS, params.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_MS, params.mTBatch / 1e6f);
}
- proto->write(SensorProto::SAMPLING_PERIOD_SELECTED , info.bestBatchParams.mTSample / 1e6f);
- proto->write(SensorProto::BATCHING_PERIOD_SELECTED , info.bestBatchParams.mTBatch / 1e6f);
+ proto->write(SensorProto::SAMPLING_PERIOD_SELECTED, info.bestBatchParams.mTSample / 1e6f);
+ proto->write(SensorProto::BATCHING_PERIOD_SELECTED, info.bestBatchParams.mTBatch / 1e6f);
proto->end(token);
}
}
@@ -506,17 +334,17 @@
}
status_t SensorDevice::initCheck() const {
- return mSensors != nullptr ? NO_ERROR : NO_INIT;
+ return mHalWrapper != nullptr ? NO_ERROR : NO_INIT;
}
ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
- if (mSensors == nullptr) return NO_INIT;
+ if (mHalWrapper == nullptr) return NO_INIT;
ssize_t eventsRead = 0;
- if (mSensors->supportsMessageQueues()) {
- eventsRead = pollFmq(buffer, count);
- } else if (mSensors->supportsPolling()) {
- eventsRead = pollHal(buffer, count);
+ if (mHalWrapper->supportsMessageQueues()) {
+ eventsRead = mHalWrapper->pollFmq(buffer, count);
+ } else if (mHalWrapper->supportsPolling()) {
+ eventsRead = mHalWrapper->poll(buffer, count);
} else {
ALOGE("Must support polling or FMQ");
eventsRead = -1;
@@ -524,136 +352,35 @@
return eventsRead;
}
-ssize_t SensorDevice::pollHal(sensors_event_t* buffer, size_t count) {
- ssize_t err;
- int numHidlTransportErrors = 0;
- bool hidlTransportError = false;
-
- do {
- auto ret = mSensors->poll(
- count,
- [&](auto result,
- const auto &events,
- const auto &dynamicSensorsAdded) {
- if (result == Result::OK) {
- convertToSensorEventsAndQuantize(convertToNewEvents(events),
- convertToNewSensorInfos(dynamicSensorsAdded), buffer);
- err = (ssize_t)events.size();
- } else {
- err = statusFromResult(result);
- }
- });
-
- if (ret.isOk()) {
- hidlTransportError = false;
- } else {
- hidlTransportError = true;
- numHidlTransportErrors++;
- if (numHidlTransportErrors > 50) {
- // Log error and bail
- ALOGE("Max Hidl transport errors this cycle : %d", numHidlTransportErrors);
- handleHidlDeath(ret.description());
- } else {
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- }
- }
- } while (hidlTransportError);
-
- if(numHidlTransportErrors > 0) {
- ALOGE("Saw %d Hidl transport failures", numHidlTransportErrors);
- HidlTransportErrorLog errLog(time(nullptr), numHidlTransportErrors);
- mHidlTransportErrors.add(errLog);
- mTotalHidlTransportErrors++;
- }
-
- return err;
-}
-
-ssize_t SensorDevice::pollFmq(sensors_event_t* buffer, size_t maxNumEventsToRead) {
- ssize_t eventsRead = 0;
- size_t availableEvents = mSensors->getEventQueue()->availableToRead();
-
- if (availableEvents == 0) {
- uint32_t eventFlagState = 0;
-
- // Wait for events to become available. This is necessary so that the Event FMQ's read() is
- // able to be called with the correct number of events to read. If the specified number of
- // events is not available, then read() would return no events, possibly introducing
- // additional latency in delivering events to applications.
- mEventQueueFlag->wait(asBaseType(EventQueueFlagBits::READ_AND_PROCESS) |
- asBaseType(INTERNAL_WAKE), &eventFlagState);
- availableEvents = mSensors->getEventQueue()->availableToRead();
-
- if ((eventFlagState & asBaseType(INTERNAL_WAKE)) && mReconnecting) {
- ALOGD("Event FMQ internal wake, returning from poll with no events");
- return DEAD_OBJECT;
- }
- }
-
- size_t eventsToRead = std::min({availableEvents, maxNumEventsToRead, mEventBuffer.size()});
- if (eventsToRead > 0) {
- if (mSensors->getEventQueue()->read(mEventBuffer.data(), eventsToRead)) {
- // Notify the Sensors HAL that sensor events have been read. This is required to support
- // the use of writeBlocking by the Sensors HAL.
- mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::EVENTS_READ));
-
- for (size_t i = 0; i < eventsToRead; i++) {
- convertToSensorEvent(mEventBuffer[i], &buffer[i]);
- android::SensorDeviceUtils::quantizeSensorEventValues(&buffer[i],
- getResolutionForSensor(buffer[i].sensor));
- }
- eventsRead = eventsToRead;
- } else {
- ALOGW("Failed to read %zu events, currently %zu events available",
- eventsToRead, availableEvents);
- }
- }
-
- return eventsRead;
-}
-
-Return<void> SensorDevice::onDynamicSensorsConnected(
- const hidl_vec<SensorInfo> &dynamicSensorsAdded) {
+void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynamicSensorsAdded) {
std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
// Allocate a sensor_t structure for each dynamic sensor added and insert
// it into the dictionary of connected dynamic sensors keyed by handle.
for (size_t i = 0; i < dynamicSensorsAdded.size(); ++i) {
- const SensorInfo &info = dynamicSensorsAdded[i];
+ const sensor_t& sensor = dynamicSensorsAdded[i];
- auto it = mConnectedDynamicSensors.find(info.sensorHandle);
+ auto it = mConnectedDynamicSensors.find(sensor.handle);
CHECK(it == mConnectedDynamicSensors.end());
- sensor_t *sensor = new sensor_t();
- convertToSensor(convertToOldSensorInfo(info), sensor);
-
- mConnectedDynamicSensors.insert(
- std::make_pair(sensor->handle, sensor));
+ mConnectedDynamicSensors.insert(std::make_pair(sensor.handle, sensor));
}
mDynamicSensorsCv.notify_all();
-
- return Return<void>();
}
-Return<void> SensorDevice::onDynamicSensorsDisconnected(
- const hidl_vec<int32_t> &dynamicSensorHandlesRemoved) {
- (void) dynamicSensorHandlesRemoved;
+void SensorDevice::onDynamicSensorsDisconnected(
+ const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) {
// TODO: Currently dynamic sensors do not seem to be removed
- return Return<void>();
}
void SensorDevice::writeWakeLockHandled(uint32_t count) {
- if (mSensors != nullptr && mSensors->supportsMessageQueues()) {
- if (mWakeLockQueue->write(&count)) {
- mWakeLockQueueFlag->wake(asBaseType(WakeLockQueueFlagBits::DATA_WRITTEN));
- } else {
- ALOGW("Failed to write wake lock handled");
- }
+ if (mHalWrapper != nullptr && mHalWrapper->supportsMessageQueues()) {
+ mHalWrapper->writeWakeLockHandled(count);
}
}
-void SensorDevice::autoDisable(void *ident, int handle) {
+void SensorDevice::autoDisable(void* ident, int handle) {
Mutex::Autolock _l(mLock);
ssize_t activationIndex = mActivationCount.indexOfKey(handle);
if (activationIndex < 0) {
@@ -668,7 +395,7 @@
}
status_t SensorDevice::activate(void* ident, int handle, int enabled) {
- if (mSensors == nullptr) return NO_INIT;
+ if (mHalWrapper == nullptr) return NO_INIT;
Mutex::Autolock _l(mLock);
return activateLocked(ident, handle, enabled);
@@ -687,15 +414,15 @@
Info& info(mActivationCount.editValueAt(activationIndex));
ALOGD_IF(DEBUG_CONNECTIONS,
- "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",
- ident, handle, enabled, info.batchParams.size());
+ "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", ident,
+ handle, enabled, info.batchParams.size());
if (enabled) {
ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));
if (isClientDisabledLocked(ident)) {
- ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",
- ident, handle);
+ ALOGW("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d", ident,
+ handle);
return NO_ERROR;
}
@@ -714,7 +441,6 @@
// dictionary.
auto it = mConnectedDynamicSensors.find(handle);
if (it != mConnectedDynamicSensors.end()) {
- delete it->second;
mConnectedDynamicSensors.erase(it);
}
@@ -726,11 +452,10 @@
// Call batch for this sensor with the previously calculated best effort
// batch_rate and timeout. One of the apps has unregistered for sensor
// events, and the best effort batch parameters might have changed.
- ALOGD_IF(DEBUG_CONNECTIONS,
- "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle,
- info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
- checkReturn(mSensors->batch(
- handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64,
+ handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
+ mHalWrapper->batch(handle, info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch);
}
} else {
// sensor wasn't enabled for this ident
@@ -761,19 +486,15 @@
status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,
enabled);
- status_t err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));
+ status_t err = mHalWrapper->activate(handle, enabled);
ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,
strerror(-err));
return err;
}
-status_t SensorDevice::batch(
- void* ident,
- int handle,
- int flags,
- int64_t samplingPeriodNs,
- int64_t maxBatchReportLatencyNs) {
- if (mSensors == nullptr) return NO_INIT;
+status_t SensorDevice::batch(void* ident, int handle, int flags, int64_t samplingPeriodNs,
+ int64_t maxBatchReportLatencyNs) {
+ if (mHalWrapper == nullptr) return NO_INIT;
if (samplingPeriodNs < MINIMUM_EVENTS_PERIOD) {
samplingPeriodNs = MINIMUM_EVENTS_PERIOD;
@@ -783,7 +504,8 @@
}
ALOGD_IF(DEBUG_CONNECTIONS,
- "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64 " timeout=%" PRId64,
+ "SensorDevice::batch: ident=%p, handle=0x%08x, flags=%d, period_ns=%" PRId64
+ " timeout=%" PRId64,
ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs);
Mutex::Autolock _l(mLock);
@@ -807,25 +529,24 @@
info.setBatchParamsForIdent(ident, flags, samplingPeriodNs, maxBatchReportLatencyNs);
}
- status_t err = updateBatchParamsLocked(handle, info);
+ status_t err = updateBatchParamsLocked(handle, info);
if (err != NO_ERROR) {
- ALOGE("sensor batch failed %p 0x%08x %" PRId64 " %" PRId64 " err=%s",
- mSensors.get(), handle, info.bestBatchParams.mTSample,
- info.bestBatchParams.mTBatch, strerror(-err));
+ ALOGE("sensor batch failed 0x%08x %" PRId64 " %" PRId64 " err=%s", handle,
+ info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch, strerror(-err));
info.removeBatchParamsForIdent(ident);
}
return err;
}
-status_t SensorDevice::updateBatchParamsLocked(int handle, Info &info) {
+status_t SensorDevice::updateBatchParamsLocked(int handle, Info& info) {
BatchParams prevBestBatchParams = info.bestBatchParams;
// Find the minimum of all timeouts and batch_rates for this sensor.
info.selectBatchParams();
ALOGD_IF(DEBUG_CONNECTIONS,
- "\t>>> curr_period=%" PRId64 " min_period=%" PRId64
- " curr_timeout=%" PRId64 " min_timeout=%" PRId64,
+ "\t>>> curr_period=%" PRId64 " min_period=%" PRId64 " curr_timeout=%" PRId64
+ " min_timeout=%" PRId64,
prevBestBatchParams.mTSample, info.bestBatchParams.mTSample,
prevBestBatchParams.mTBatch, info.bestBatchParams.mTBatch);
@@ -834,8 +555,8 @@
if (prevBestBatchParams != info.bestBatchParams && info.numActiveClients() > 0) {
ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w BATCH 0x%08x %" PRId64 " %" PRId64, handle,
info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);
- err = checkReturnAndGetStatus(mSensors->batch(
- handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));
+ err = mHalWrapper->batch(handle, info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch);
}
return err;
@@ -846,15 +567,12 @@
}
int SensorDevice::getHalDeviceVersion() const {
- if (mSensors == nullptr) return -1;
+ if (mHalWrapper == nullptr) return -1;
return SENSORS_DEVICE_API_VERSION_1_4;
}
-status_t SensorDevice::flush(void* ident, int handle) {
- if (mSensors == nullptr) return NO_INIT;
- if (isClientDisabled(ident)) return INVALID_OPERATION;
- ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w flush %d", handle);
- return checkReturnAndGetStatus(mSensors->flush(handle));
+status_t SensorDevice::flush(void* /*ident*/, int handle) {
+ return mHalWrapper->flush(handle);
}
bool SensorDevice::isClientDisabled(void* ident) const {
@@ -866,8 +584,8 @@
return mDisabledClients.count(ident) > 0;
}
-std::vector<void *> SensorDevice::getDisabledClientsLocked() const {
- std::vector<void *> vec;
+std::vector<void*> SensorDevice::getDisabledClientsLocked() const {
+ std::vector<void*> vec;
for (const auto& it : mDisabledClients) {
vec.push_back(it.first);
}
@@ -896,7 +614,7 @@
addDisabledReasonForIdentLocked(ident, DisabledReason::DISABLED_REASON_UID_IDLE);
}
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
int handle = mActivationCount.keyAt(i);
Info& info = mActivationCount.editValueAt(i);
@@ -905,8 +623,7 @@
bool disable = info.numActiveClients() == 0 && info.isActive;
bool enable = info.numActiveClients() > 0 && !info.isActive;
- if ((enable || disable) &&
- doActivateHardwareLocked(handle, enable) == NO_ERROR) {
+ if ((enable || disable) && doActivateHardwareLocked(handle, enable) == NO_ERROR) {
info.isActive = enable;
}
}
@@ -938,29 +655,27 @@
}
void SensorDevice::enableAllSensors() {
- if (mSensors == nullptr) return;
+ if (mHalWrapper == nullptr) return;
Mutex::Autolock _l(mLock);
- for (void *client : getDisabledClientsLocked()) {
- removeDisabledReasonForIdentLocked(
- client, DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+ for (void* client : getDisabledClientsLocked()) {
+ removeDisabledReasonForIdentLocked(client,
+ DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
}
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
if (info.batchParams.isEmpty()) continue;
info.selectBatchParams();
const int sensor_handle = mActivationCount.keyAt(i);
ALOGD_IF(DEBUG_CONNECTIONS, "\t>> reenable actuating h/w sensor enable handle=%d ",
- sensor_handle);
- status_t err = checkReturnAndGetStatus(mSensors->batch(
- sensor_handle,
- info.bestBatchParams.mTSample,
- info.bestBatchParams.mTBatch));
+ sensor_handle);
+ status_t err = mHalWrapper->batch(sensor_handle, info.bestBatchParams.mTSample,
+ info.bestBatchParams.mTBatch);
ALOGE_IF(err, "Error calling batch on sensor %d (%s)", sensor_handle, strerror(-err));
if (err == NO_ERROR) {
- err = checkReturnAndGetStatus(mSensors->activate(sensor_handle, 1 /* enabled */));
+ err = mHalWrapper->activate(sensor_handle, 1 /* enabled */);
ALOGE_IF(err, "Error activating sensor %d (%s)", sensor_handle, strerror(-err));
}
@@ -971,138 +686,54 @@
}
void SensorDevice::disableAllSensors() {
- if (mSensors == nullptr) return;
+ if (mHalWrapper == nullptr) return;
Mutex::Autolock _l(mLock);
- for (size_t i = 0; i< mActivationCount.size(); ++i) {
+ for (size_t i = 0; i < mActivationCount.size(); ++i) {
Info& info = mActivationCount.editValueAt(i);
// Check if this sensor has been activated previously and disable it.
if (info.batchParams.size() > 0) {
- const int sensor_handle = mActivationCount.keyAt(i);
- ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
- sensor_handle);
- checkReturn(mSensors->activate(sensor_handle, 0 /* enabled */));
+ const int sensor_handle = mActivationCount.keyAt(i);
+ ALOGD_IF(DEBUG_CONNECTIONS, "\t>> actuating h/w sensor disable handle=%d ",
+ sensor_handle);
+ mHalWrapper->activate(sensor_handle, 0 /* enabled */);
- // Add all the connections that were registered for this sensor to the disabled
- // clients list.
- for (size_t j = 0; j < info.batchParams.size(); ++j) {
- addDisabledReasonForIdentLocked(
- info.batchParams.keyAt(j), DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
- ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
- }
+ // Add all the connections that were registered for this sensor to the disabled
+ // clients list.
+ for (size_t j = 0; j < info.batchParams.size(); ++j) {
+ addDisabledReasonForIdentLocked(info.batchParams.keyAt(j),
+ DisabledReason::DISABLED_REASON_SERVICE_RESTRICTED);
+ ALOGI("added %p to mDisabledClients", info.batchParams.keyAt(j));
+ }
- info.isActive = false;
+ info.isActive = false;
}
}
}
-status_t SensorDevice::injectSensorData(
- const sensors_event_t *injected_sensor_event) {
- if (mSensors == nullptr) return NO_INIT;
- ALOGD_IF(DEBUG_CONNECTIONS,
- "sensor_event handle=%d ts=%" PRId64 " data=%.2f, %.2f, %.2f %.2f %.2f %.2f",
- injected_sensor_event->sensor,
- injected_sensor_event->timestamp, injected_sensor_event->data[0],
- injected_sensor_event->data[1], injected_sensor_event->data[2],
- injected_sensor_event->data[3], injected_sensor_event->data[4],
- injected_sensor_event->data[5]);
-
- Event ev;
- V2_1::implementation::convertFromSensorEvent(*injected_sensor_event, &ev);
-
- return checkReturnAndGetStatus(mSensors->injectSensorData(ev));
+status_t SensorDevice::injectSensorData(const sensors_event_t* injected_sensor_event) {
+ return mHalWrapper->injectSensorData(injected_sensor_event);
}
status_t SensorDevice::setMode(uint32_t mode) {
- if (mSensors == nullptr) return NO_INIT;
- return checkReturnAndGetStatus(mSensors->setOperationMode(
- static_cast<hardware::sensors::V1_0::OperationMode>(mode)));
+ if (mHalWrapper == nullptr) return NO_INIT;
+ return mHalWrapper->setOperationMode(static_cast<SensorService::Mode>(mode));
}
int32_t SensorDevice::registerDirectChannel(const sensors_direct_mem_t* memory) {
- if (mSensors == nullptr) return NO_INIT;
Mutex::Autolock _l(mLock);
- SharedMemType type;
- switch (memory->type) {
- case SENSOR_DIRECT_MEM_TYPE_ASHMEM:
- type = SharedMemType::ASHMEM;
- break;
- case SENSOR_DIRECT_MEM_TYPE_GRALLOC:
- type = SharedMemType::GRALLOC;
- break;
- default:
- return BAD_VALUE;
- }
-
- SharedMemFormat format;
- if (memory->format != SENSOR_DIRECT_FMT_SENSORS_EVENT) {
- return BAD_VALUE;
- }
- format = SharedMemFormat::SENSORS_EVENT;
-
- SharedMemInfo mem = {
- .type = type,
- .format = format,
- .size = static_cast<uint32_t>(memory->size),
- .memoryHandle = memory->handle,
- };
-
- int32_t ret;
- checkReturn(mSensors->registerDirectChannel(mem,
- [&ret](auto result, auto channelHandle) {
- if (result == Result::OK) {
- ret = channelHandle;
- } else {
- ret = statusFromResult(result);
- }
- }));
- return ret;
+ return mHalWrapper->registerDirectChannel(memory, nullptr);
}
void SensorDevice::unregisterDirectChannel(int32_t channelHandle) {
- if (mSensors == nullptr) return;
- Mutex::Autolock _l(mLock);
- checkReturn(mSensors->unregisterDirectChannel(channelHandle));
+ mHalWrapper->unregisterDirectChannel(channelHandle);
}
-int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle,
- int32_t channelHandle, const struct sensors_direct_cfg_t *config) {
- if (mSensors == nullptr) return NO_INIT;
+int32_t SensorDevice::configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config) {
Mutex::Autolock _l(mLock);
- RateLevel rate;
- switch(config->rate_level) {
- case SENSOR_DIRECT_RATE_STOP:
- rate = RateLevel::STOP;
- break;
- case SENSOR_DIRECT_RATE_NORMAL:
- rate = RateLevel::NORMAL;
- break;
- case SENSOR_DIRECT_RATE_FAST:
- rate = RateLevel::FAST;
- break;
- case SENSOR_DIRECT_RATE_VERY_FAST:
- rate = RateLevel::VERY_FAST;
- break;
- default:
- return BAD_VALUE;
- }
-
- int32_t ret;
- checkReturn(mSensors->configDirectReport(sensorHandle, channelHandle, rate,
- [&ret, rate] (auto result, auto token) {
- if (rate == RateLevel::STOP) {
- ret = statusFromResult(result);
- } else {
- if (result == Result::OK) {
- ret = token;
- } else {
- ret = statusFromResult(result);
- }
- }
- }));
-
- return ret;
+ return mHalWrapper->configureDirectChannel(sensorHandle, channelHandle, config);
}
// ---------------------------------------------------------------------------
@@ -1118,13 +749,12 @@
return num;
}
-status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int,
- int64_t samplingPeriodNs,
+status_t SensorDevice::Info::setBatchParamsForIdent(void* ident, int, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs) {
ssize_t index = batchParams.indexOfKey(ident);
if (index < 0) {
- ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64
- " timeout=%" PRId64 ") failed (%s)",
+ ALOGE("Info::setBatchParamsForIdent(ident=%p, period_ns=%" PRId64 " timeout=%" PRId64
+ ") failed (%s)",
ident, samplingPeriodNs, maxBatchReportLatencyNs, strerror(-index));
return BAD_INDEX;
}
@@ -1168,84 +798,20 @@
return mIsDirectReportSupported;
}
-void SensorDevice::convertToSensorEvent(
- const Event &src, sensors_event_t *dst) {
- V2_1::implementation::convertToSensorEvent(src, dst);
-
- if (src.sensorType == V2_1::SensorType::DYNAMIC_SENSOR_META) {
- const DynamicSensorInfo &dyn = src.u.dynamic;
-
- dst->dynamic_sensor_meta.connected = dyn.connected;
- dst->dynamic_sensor_meta.handle = dyn.sensorHandle;
- if (dyn.connected) {
- std::unique_lock<std::mutex> lock(mDynamicSensorsMutex);
- // Give MAX_DYN_SENSOR_WAIT_SEC for onDynamicSensorsConnected to be invoked since it
- // can be received out of order from this event due to a bug in the HIDL spec that
- // marks it as oneway.
- auto it = mConnectedDynamicSensors.find(dyn.sensorHandle);
- if (it == mConnectedDynamicSensors.end()) {
- mDynamicSensorsCv.wait_for(lock, MAX_DYN_SENSOR_WAIT,
- [&, dyn]{
- return mConnectedDynamicSensors.find(dyn.sensorHandle)
- != mConnectedDynamicSensors.end();
- });
- it = mConnectedDynamicSensors.find(dyn.sensorHandle);
- CHECK(it != mConnectedDynamicSensors.end());
- }
-
- dst->dynamic_sensor_meta.sensor = it->second;
-
- memcpy(dst->dynamic_sensor_meta.uuid,
- dyn.uuid.data(),
- sizeof(dst->dynamic_sensor_meta.uuid));
- }
- }
-}
-
-void SensorDevice::convertToSensorEventsAndQuantize(
- const hidl_vec<Event> &src,
- const hidl_vec<SensorInfo> &dynamicSensorsAdded,
- sensors_event_t *dst) {
-
- if (dynamicSensorsAdded.size() > 0) {
- onDynamicSensorsConnected(dynamicSensorsAdded);
- }
-
- for (size_t i = 0; i < src.size(); ++i) {
- V2_1::implementation::convertToSensorEvent(src[i], &dst[i]);
- android::SensorDeviceUtils::quantizeSensorEventValues(&dst[i],
- getResolutionForSensor(dst[i].sensor));
- }
-}
-
float SensorDevice::getResolutionForSensor(int sensorHandle) {
for (size_t i = 0; i < mSensorList.size(); i++) {
- if (sensorHandle == mSensorList[i].handle) {
- return mSensorList[i].resolution;
- }
+ if (sensorHandle == mSensorList[i].handle) {
+ return mSensorList[i].resolution;
+ }
}
auto it = mConnectedDynamicSensors.find(sensorHandle);
if (it != mConnectedDynamicSensors.end()) {
- return it->second->resolution;
+ return it->second.resolution;
}
return 0;
}
-void SensorDevice::handleHidlDeath(const std::string & detail) {
- if (!mSensors->supportsMessageQueues()) {
- // restart is the only option at present.
- LOG_ALWAYS_FATAL("Abort due to ISensors hidl service failure, detail: %s.", detail.c_str());
- } else {
- ALOGD("ISensors HAL died, death recipient will attempt reconnect");
- }
-}
-
-status_t SensorDevice::checkReturnAndGetStatus(const Return<Result>& ret) {
- checkReturn(ret);
- return (!ret.isOk()) ? DEAD_OBJECT : statusFromResult(ret);
-}
-
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/services/sensorservice/SensorDevice.h b/services/sensorservice/SensorDevice.h
index bc8d20f..80e77d9 100644
--- a/services/sensorservice/SensorDevice.h
+++ b/services/sensorservice/SensorDevice.h
@@ -17,14 +17,16 @@
#ifndef ANDROID_SENSOR_DEVICE_H
#define ANDROID_SENSOR_DEVICE_H
+#include "HidlSensorHalWrapper.h"
+#include "ISensorHalWrapper.h"
+
+#include "ISensorsWrapper.h"
#include "SensorDeviceUtils.h"
#include "SensorService.h"
#include "SensorServiceUtils.h"
-#include "ISensorsWrapper.h"
-#include <fmq/MessageQueue.h>
-#include <sensor/SensorEventQueue.h>
#include <sensor/Sensor.h>
+#include <sensor/SensorEventQueue.h>
#include <stdint.h>
#include <sys/types.h>
#include <utils/KeyedVector.h>
@@ -32,9 +34,10 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <algorithm> //std::max std::min
#include <string>
#include <unordered_map>
-#include <algorithm> //std::max std::min
+#include <vector>
#include "RingBuffer.h"
@@ -43,40 +46,11 @@
namespace android {
// ---------------------------------------------------------------------------
-class SensorsHalDeathReceivier : public android::hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<::android::hidl::base::V1_0::IBase>& service) override;
-};
class SensorDevice : public Singleton<SensorDevice>,
- public SensorServiceUtil::Dumpable {
+ public SensorServiceUtil::Dumpable,
+ public ISensorHalWrapper::SensorDeviceCallback {
public:
- class HidlTransportErrorLog {
- public:
-
- HidlTransportErrorLog() {
- mTs = 0;
- mCount = 0;
- }
-
- HidlTransportErrorLog(time_t ts, int count) {
- mTs = ts;
- mCount = count;
- }
-
- String8 toString() const {
- String8 result;
- struct tm *timeInfo = localtime(&mTs);
- result.appendFormat("%02d:%02d:%02d :: %d", timeInfo->tm_hour, timeInfo->tm_min,
- timeInfo->tm_sec, mCount);
- return result;
- }
-
- private:
- time_t mTs; // timestamp of the error
- int mCount; // number of transport errors observed
- };
-
~SensorDevice();
void prepareForReconnect();
void reconnect();
@@ -99,29 +73,27 @@
status_t setMode(uint32_t mode);
bool isDirectReportSupported() const;
- int32_t registerDirectChannel(const sensors_direct_mem_t *memory);
+ int32_t registerDirectChannel(const sensors_direct_mem_t* memory);
void unregisterDirectChannel(int32_t channelHandle);
- int32_t configureDirectChannel(int32_t sensorHandle,
- int32_t channelHandle, const struct sensors_direct_cfg_t *config);
+ int32_t configureDirectChannel(int32_t sensorHandle, int32_t channelHandle,
+ const struct sensors_direct_cfg_t* config);
void disableAllSensors();
void enableAllSensors();
- void autoDisable(void *ident, int handle);
+ void autoDisable(void* ident, int handle);
- status_t injectSensorData(const sensors_event_t *event);
- void notifyConnectionDestroyed(void *ident);
+ status_t injectSensorData(const sensors_event_t* event);
+ void notifyConnectionDestroyed(void* ident);
- using Result = ::android::hardware::sensors::V1_0::Result;
- hardware::Return<void> onDynamicSensorsConnected(
- const hardware::hidl_vec<hardware::sensors::V2_1::SensorInfo> &dynamicSensorsAdded);
- hardware::Return<void> onDynamicSensorsDisconnected(
- const hardware::hidl_vec<int32_t> &dynamicSensorHandlesRemoved);
+ // SensorDeviceCallback
+ virtual void onDynamicSensorsConnected(
+ const std::vector<sensor_t>& dynamicSensorsAdded) override;
+ virtual void onDynamicSensorsDisconnected(
+ const std::vector<int32_t>& dynamicSensorHandlesRemoved) override;
void setUidStateForConnection(void* ident, SensorService::UidState state);
- bool isReconnecting() const {
- return mReconnecting;
- }
+ bool isReconnecting() const { return mHalWrapper->mReconnecting; }
bool isSensorActive(int handle) const;
@@ -132,12 +104,14 @@
// Dumpable
virtual std::string dump() const override;
virtual void dump(util::ProtoOutputStream* proto) const override;
+
private:
friend class Singleton<SensorDevice>;
- sp<::android::hardware::sensors::V2_1::implementation::ISensorsWrapperBase> mSensors;
- Vector<sensor_t> mSensorList;
- std::unordered_map<int32_t, sensor_t*> mConnectedDynamicSensors;
+ std::unique_ptr<ISensorHalWrapper> mHalWrapper;
+
+ std::vector<sensor_t> mSensorList;
+ std::unordered_map<int32_t, sensor_t> mConnectedDynamicSensors;
// A bug in the Sensors HIDL spec which marks onDynamicSensorsConnected as oneway causes dynamic
// meta events and onDynamicSensorsConnected to be received out of order. This mutex + CV are
@@ -145,28 +119,27 @@
// HAL implementations.
std::mutex mDynamicSensorsMutex;
std::condition_variable mDynamicSensorsCv;
- static constexpr std::chrono::seconds MAX_DYN_SENSOR_WAIT{5};
- static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
- mutable Mutex mLock; // protect mActivationCount[].batchParams
+ static const nsecs_t MINIMUM_EVENTS_PERIOD = 1000000; // 1000 Hz
+ mutable Mutex mLock; // protect mActivationCount[].batchParams
// fixed-size array after construction
// Struct to store all the parameters(samplingPeriod, maxBatchReportLatency and flags) from
// batch call. For continous mode clients, maxBatchReportLatency is set to zero.
struct BatchParams {
- nsecs_t mTSample, mTBatch;
- BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
- BatchParams(nsecs_t tSample, nsecs_t tBatch): mTSample(tSample), mTBatch(tBatch) {}
- bool operator != (const BatchParams& other) {
- return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
- }
- // Merge another parameter with this one. The updated mTSample will be the min of the two.
- // The update mTBatch will be the min of original mTBatch and the apparent batch period
- // of the other. the apparent batch is the maximum of mTBatch and mTSample,
- void merge(const BatchParams &other) {
- mTSample = std::min(mTSample, other.mTSample);
- mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
- }
+ nsecs_t mTSample, mTBatch;
+ BatchParams() : mTSample(INT64_MAX), mTBatch(INT64_MAX) {}
+ BatchParams(nsecs_t tSample, nsecs_t tBatch) : mTSample(tSample), mTBatch(tBatch) {}
+ bool operator!=(const BatchParams& other) {
+ return !(mTSample == other.mTSample && mTBatch == other.mTBatch);
+ }
+ // Merge another parameter with this one. The updated mTSample will be the min of the two.
+ // The update mTBatch will be the min of original mTBatch and the apparent batch period
+ // of the other. the apparent batch is the maximum of mTBatch and mTSample,
+ void merge(const BatchParams& other) {
+ mTSample = std::min(mTSample, other.mTSample);
+ mTBatch = std::min(mTBatch, std::max(other.mTBatch, other.mTSample));
+ }
};
// Store batch parameters in the KeyedVector and the optimal batch_rate and timeout in
@@ -205,8 +178,6 @@
};
DefaultKeyedVector<int, Info> mActivationCount;
- // Keep track of any hidl transport failures
- SensorServiceUtil::RingBuffer<HidlTransportErrorLog> mHidlTransportErrors;
int mTotalHidlTransportErrors;
/**
@@ -224,32 +195,19 @@
static_assert(DisabledReason::DISABLED_REASON_MAX < sizeof(uint8_t) * CHAR_BIT);
// Use this map to determine which client is activated or deactivated.
- std::unordered_map<void *, uint8_t> mDisabledClients;
+ std::unordered_map<void*, uint8_t> mDisabledClients;
void addDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
void removeDisabledReasonForIdentLocked(void* ident, DisabledReason reason);
SensorDevice();
- bool connectHidlService();
+ bool connectHalService();
void initializeSensorList();
void reactivateSensors(const DefaultKeyedVector<int, Info>& previousActivations);
- static bool sensorHandlesChanged(const Vector<sensor_t>& oldSensorList,
- const Vector<sensor_t>& newSensorList);
+ static bool sensorHandlesChanged(const std::vector<sensor_t>& oldSensorList,
+ const std::vector<sensor_t>& newSensorList);
static bool sensorIsEquivalent(const sensor_t& prevSensor, const sensor_t& newSensor);
- enum HalConnectionStatus {
- CONNECTED, // Successfully connected to the HAL
- DOES_NOT_EXIST, // Could not find the HAL
- FAILED_TO_CONNECT, // Found the HAL but failed to connect/initialize
- UNKNOWN,
- };
- HalConnectionStatus connectHidlServiceV1_0();
- HalConnectionStatus connectHidlServiceV2_0();
- HalConnectionStatus connectHidlServiceV2_1();
- HalConnectionStatus initializeHidlServiceV2_X();
-
- ssize_t pollHal(sensors_event_t* buffer, size_t count);
- ssize_t pollFmq(sensors_event_t* buffer, size_t count);
status_t activateLocked(void* ident, int handle, int enabled);
status_t batchLocked(void* ident, int handle, int flags, int64_t samplingPeriodNs,
int64_t maxBatchReportLatencyNs);
@@ -257,47 +215,15 @@
status_t updateBatchParamsLocked(int handle, Info& info);
status_t doActivateHardwareLocked(int handle, bool enable);
- void handleHidlDeath(const std::string &detail);
- template<typename T>
- void checkReturn(const Return<T>& ret) {
- if (!ret.isOk()) {
- handleHidlDeath(ret.description());
- }
- }
- status_t checkReturnAndGetStatus(const Return<Result>& ret);
- //TODO(b/67425500): remove waiter after bug is resolved.
- sp<SensorDeviceUtils::HidlServiceRegistrationWaiter> mRestartWaiter;
-
bool isClientDisabled(void* ident) const;
bool isClientDisabledLocked(void* ident) const;
- std::vector<void *> getDisabledClientsLocked() const;
+ std::vector<void*> getDisabledClientsLocked() const;
bool clientHasNoAccessLocked(void* ident) const;
- using Event = hardware::sensors::V2_1::Event;
- using SensorInfo = hardware::sensors::V2_1::SensorInfo;
-
- void convertToSensorEvent(const Event &src, sensors_event_t *dst);
-
- void convertToSensorEventsAndQuantize(
- const hardware::hidl_vec<Event> &src,
- const hardware::hidl_vec<SensorInfo> &dynamicSensorsAdded,
- sensors_event_t *dst);
-
float getResolutionForSensor(int sensorHandle);
bool mIsDirectReportSupported;
-
- typedef hardware::MessageQueue<uint32_t, hardware::kSynchronizedReadWrite> WakeLockQueue;
- std::unique_ptr<WakeLockQueue> mWakeLockQueue;
-
- hardware::EventFlag* mEventQueueFlag;
- hardware::EventFlag* mWakeLockQueueFlag;
-
- std::array<Event, SensorEventQueue::MAX_RECEIVE_BUFFER_EVENT_COUNT> mEventBuffer;
-
- sp<SensorsHalDeathReceivier> mSensorsHalDeathReceiver;
- std::atomic_bool mReconnecting;
};
// ---------------------------------------------------------------------------
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 32a0110..9bc7b8e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -164,7 +164,6 @@
sensor_t const* list;
ssize_t count = dev.getSensorList(&list);
if (count > 0) {
- ssize_t orientationIndex = -1;
bool hasGyro = false, hasAccel = false, hasMag = false;
uint32_t virtualSensorsNeeds =
(1<<SENSOR_TYPE_GRAVITY) |
@@ -183,9 +182,6 @@
case SENSOR_TYPE_MAGNETIC_FIELD:
hasMag = true;
break;
- case SENSOR_TYPE_ORIENTATION:
- orientationIndex = i;
- break;
case SENSOR_TYPE_GYROSCOPE:
case SENSOR_TYPE_GYROSCOPE_UNCALIBRATED:
hasGyro = true;
@@ -201,6 +197,8 @@
virtualSensorsNeeds &= ~(1<<list[i].type);
}
break;
+ default:
+ break;
}
if (useThisSensor) {
if (list[i].type == SENSOR_TYPE_PROXIMITY) {
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 56b8374..5560ed7 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -78,9 +78,11 @@
"libframetimeline",
"libperfetto_client_experimental",
"librenderengine",
+ "libscheduler",
"libserviceutils",
+ "libshaders",
+ "libtonemap",
"libtrace_proto",
- "libaidlcommonsupport",
],
header_libs: [
"android.hardware.graphics.composer@2.1-command-buffer",
@@ -143,6 +145,7 @@
filegroup {
name: "libsurfaceflinger_sources",
srcs: [
+ "BackgroundExecutor.cpp",
"BufferLayer.cpp",
"BufferLayerConsumer.cpp",
"BufferQueueLayer.cpp",
@@ -193,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",
- "SurfaceTracing.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..de8e6b3
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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);
+ android::base::ScopedLockAssertion assumeLock(mMutex);
+ mWorkAvailableCv.wait(lock,
+ [&]() REQUIRES(mMutex) { return mDone || !mTasks.empty(); });
+ tasks = std::move(mTasks);
+ mTasks.clear();
+ done = mDone;
+ } // unlock mMutex
+
+ for (auto& task : tasks) {
+ task();
+ }
+ }
+ });
+}
+
+BackgroundExecutor::~BackgroundExecutor() {
+ {
+ std::scoped_lock lock(mMutex);
+ mDone = true;
+ mWorkAvailableCv.notify_all();
+ }
+ if (mThread.joinable()) {
+ mThread.join();
+ }
+}
+
+void BackgroundExecutor::execute(std::function<void()> task) {
+ std::scoped_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..6db7dda
--- /dev/null
+++ b/services/surfaceflinger/BackgroundExecutor.h
@@ -0,0 +1,43 @@
+/*
+ * 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 <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 GUARDED_BY(mMutex);
+ bool mDone GUARDED_BY(mMutex) = false;
+ std::vector<std::function<void()>> mTasks GUARDED_BY(mMutex);
+ std::thread mThread;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 861d496..e26c763 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -292,14 +292,15 @@
// Sideband layers
auto* compositionState = editCompositionState();
if (compositionState->sidebandStream.get() && !compositionState->sidebandStreamHasFrame) {
- compositionState->compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND;
return;
} else {
// Normal buffer layers
compositionState->hdrMetadata = mBufferInfo.mHdrMetadata;
compositionState->compositionType = mPotentialCursor
- ? Hwc2::IComposerClient::Composition::CURSOR
- : Hwc2::IComposerClient::Composition::DEVICE;
+ ? aidl::android::hardware::graphics::composer3::Composition::CURSOR
+ : aidl::android::hardware::graphics::composer3::Composition::DEVICE;
}
compositionState->buffer = mBufferInfo.mBuffer->getBuffer();
@@ -401,12 +402,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 +419,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);
@@ -566,15 +565,16 @@
// hardware.h, instead of using hard-coded values here.
#define HARDWARE_IS_DEVICE_FORMAT(f) ((f) >= 0x100 && (f) <= 0x1FF)
-bool BufferLayer::getOpacityForFormat(uint32_t format) {
+bool BufferLayer::getOpacityForFormat(PixelFormat format) {
if (HARDWARE_IS_DEVICE_FORMAT(format)) {
return true;
}
switch (format) {
- case HAL_PIXEL_FORMAT_RGBA_8888:
- case HAL_PIXEL_FORMAT_BGRA_8888:
- case HAL_PIXEL_FORMAT_RGBA_FP16:
- case HAL_PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_RGBA_8888:
+ case PIXEL_FORMAT_BGRA_8888:
+ case PIXEL_FORMAT_RGBA_FP16:
+ case PIXEL_FORMAT_RGBA_1010102:
+ case PIXEL_FORMAT_R_8:
return false;
}
// in all other case, we have no blending (also for unknown formats)
@@ -798,7 +798,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/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 8c4c8b7..34d11ac 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -153,7 +153,7 @@
bool onPreComposition(nsecs_t) override;
void preparePerFrameCompositionState() override;
- static bool getOpacityForFormat(uint32_t format);
+ static bool getOpacityForFormat(PixelFormat format);
// from graphics API
const uint32_t mTextureName;
@@ -171,7 +171,7 @@
// the mStateLock.
ui::Transform::RotationFlags mTransformHint = ui::Transform::ROT_0;
- bool getAutoRefresh() const { return mAutoRefresh; }
+ bool getAutoRefresh() const { return mDrawingState.autoRefresh; }
bool getSidebandStreamChanged() const { return mSidebandStreamChanged; }
// Returns true if the next buffer should be presented at the expected present time
@@ -182,7 +182,6 @@
// specific logic
virtual bool isBufferDue(nsecs_t /*expectedPresentTime*/) const = 0;
- std::atomic<bool> mAutoRefresh{false};
std::atomic<bool> mSidebandStreamChanged{false};
private:
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 4e5d2d0..926aa1d 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -118,7 +118,7 @@
bool BufferQueueLayer::fenceHasSignaled() const {
Mutex::Autolock lock(mQueueItemLock);
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -216,7 +216,7 @@
bool autoRefresh;
status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &autoRefresh,
&queuedBuffer, maxFrameNumberToAcquire);
- mAutoRefresh = autoRefresh;
+ mDrawingState.autoRefresh = autoRefresh;
if (updateResult == BufferQueue::PRESENT_LATER) {
// Producer doesn't want buffer to be displayed yet. Signal a
// layer update so we check again at the next opportunity.
@@ -300,7 +300,7 @@
// Decrement the queued-frames count. Signal another event if we
// have more frames pending.
- if ((queuedBuffer && more_frames_pending) || mAutoRefresh) {
+ if ((queuedBuffer && more_frames_pending) || mDrawingState.autoRefresh) {
mFlinger->onLayerUpdate();
}
@@ -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
@@ -523,7 +524,7 @@
}
sp<Layer> BufferQueueLayer::createClone() {
- LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+ LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<BufferQueueLayer> layer = mFlinger->getFactory().createBufferQueueLayer(args);
layer->setInitialValuesForClone(this);
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index dfdb5c0..c6e0727 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -62,6 +62,11 @@
status_t setDefaultBufferProperties(uint32_t w, uint32_t h, PixelFormat format);
sp<IGraphicBufferProducer> getProducer() const;
+ void setSizeForTest(uint32_t w, uint32_t h) {
+ mDrawingState.active_legacy.w = w;
+ mDrawingState.active_legacy.h = h;
+ }
+
protected:
void gatherBufferInfo() override;
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index c0753f9..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);
@@ -630,7 +631,7 @@
// Interface implementation for BufferLayer
// -----------------------------------------------------------------------
bool BufferStateLayer::fenceHasSignaled() const {
- if (SurfaceFlinger::enableLatchUnsignaled) {
+ if (SurfaceFlinger::enableLatchUnsignaledConfig != LatchUnsignaledConfig::Disabled) {
return true;
}
@@ -660,9 +661,7 @@
}
void BufferStateLayer::setAutoRefresh(bool autoRefresh) {
- if (!mAutoRefresh.exchange(autoRefresh)) {
- mFlinger->onLayerUpdate();
- }
+ mDrawingState.autoRefresh = autoRefresh;
}
bool BufferStateLayer::latchSidebandStream(bool& recomputeVisibleRegions) {
@@ -885,7 +884,7 @@
}
sp<Layer> BufferStateLayer::createClone() {
- LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0, LayerMetadata());
+ LayerCreationArgs args(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata());
args.textureName = mTextureName;
sp<BufferStateLayer> layer = mFlinger->getFactory().createBufferStateLayer(args);
layer->mHwcSlotGenerator = mHwcSlotGenerator;
diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp
index 8da2e24..6d7b732 100644
--- a/services/surfaceflinger/Client.cpp
+++ b/services/surfaceflinger/Client.cpp
@@ -72,40 +72,34 @@
return lbc;
}
-status_t Client::createSurface(const String8& name, uint32_t w, uint32_t h, PixelFormat format,
- uint32_t flags, const sp<IBinder>& parentHandle,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
- uint32_t* outTransformHint) {
+status_t Client::createSurface(const String8& name, uint32_t /* w */, uint32_t /* h */,
+ PixelFormat /* format */, uint32_t flags,
+ const sp<IBinder>& parentHandle, LayerMetadata metadata,
+ sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* /* gbp */,
+ int32_t* outLayerId, uint32_t* outTransformHint) {
// We rely on createLayer to check permissions.
- return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- parentHandle, outLayerId, nullptr, outTransformHint);
+ LayerCreationArgs args(mFlinger.get(), this, name.c_str(), flags, std::move(metadata));
+ return mFlinger->createLayer(args, outHandle, parentHandle, outLayerId, nullptr,
+ outTransformHint);
}
-status_t Client::createWithSurfaceParent(const String8& name, uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags,
- const sp<IGraphicBufferProducer>& parent,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp, int32_t* outLayerId,
- uint32_t* outTransformHint) {
- if (mFlinger->authenticateSurfaceTexture(parent) == false) {
- ALOGE("failed to authenticate surface texture");
- return BAD_VALUE;
- }
-
- const auto& layer = (static_cast<MonitoredProducer*>(parent.get()))->getLayer();
- if (layer == nullptr) {
- ALOGE("failed to find parent layer");
- return BAD_VALUE;
- }
-
- return mFlinger->createLayer(name, this, w, h, format, flags, std::move(metadata), handle, gbp,
- nullptr, outLayerId, layer, outTransformHint);
+status_t Client::createWithSurfaceParent(const String8& /* name */, uint32_t /* w */,
+ uint32_t /* h */, PixelFormat /* format */,
+ uint32_t /* flags */,
+ const sp<IGraphicBufferProducer>& /* parent */,
+ LayerMetadata /* metadata */, sp<IBinder>* /* handle */,
+ sp<IGraphicBufferProducer>* /* gbp */,
+ int32_t* /* outLayerId */,
+ uint32_t* /* outTransformHint */) {
+ // This api does not make sense with blast since SF no longer tracks IGBP. This api should be
+ // removed.
+ return BAD_VALUE;
}
status_t Client::mirrorSurface(const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
int32_t* outLayerId) {
- return mFlinger->mirrorLayer(this, mirrorFromHandle, outHandle, outLayerId);
+ LayerCreationArgs args(mFlinger.get(), this, "MirrorRoot", 0 /* flags */, LayerMetadata());
+ return mFlinger->mirrorLayer(args, mirrorFromHandle, outHandle, outLayerId);
}
status_t Client::clearLayerFrameStats(const sp<IBinder>& handle) const {
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 83b4a25..aefc014 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -38,6 +38,7 @@
static_libs: [
"libmath",
"librenderengine",
+ "libtonemap",
"libtrace_proto",
"libaidlcommonsupport",
],
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
index 93586ed..f201751 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionRefreshArgs.h
@@ -85,6 +85,9 @@
// to prevent an early presentation of a frame.
std::shared_ptr<FenceTime> previousPresentFence;
+ // The expected time for the next present
+ nsecs_t expectedPresentTime{0};
+
// If set, a frame has been scheduled for that time.
std::optional<std::chrono::steady_clock::time_point> scheduledFrameTime;
};
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/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index ac243c0..e77e155 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -134,6 +134,9 @@
// Configure layer settings for using blurs
BlurSetting blurSetting;
+
+ // Requested white point of the layer in nits
+ const float whitePointNits;
};
// A superset of LayerSettings required by RenderEngine to compose a layer
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index a000661..8bf7f8f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -39,6 +39,8 @@
#include "DisplayHardware/Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -156,12 +158,13 @@
*/
// The type of composition for this layer
- hal::Composition compositionType{hal::Composition::INVALID};
+ aidl::android::hardware::graphics::composer3::Composition compositionType{
+ aidl::android::hardware::graphics::composer3::Composition::INVALID};
// The buffer and related state
sp<GraphicBuffer> buffer;
int bufferSlot{BufferQueue::INVALID_BUFFER_SLOT};
- sp<Fence> acquireFence;
+ sp<Fence> acquireFence = Fence::NO_FENCE;
Region surfaceDamage;
// The handle to use for a sideband stream for this layer
@@ -200,6 +203,9 @@
// The output-independent frame for the cursor
Rect cursorFrame;
+ // framerate of the layer as measured by LayerHistory
+ float fps;
+
virtual ~LayerFECompositionState();
// Debugging
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
index ead941d..a2824f5 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -33,6 +33,8 @@
#include "LayerFE.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -112,7 +114,8 @@
virtual bool isHardwareCursor() const = 0;
// Applies a HWC device requested composition type change
- virtual void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) = 0;
+ virtual void applyDeviceCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition) = 0;
// Prepares to apply any HWC device layer requests
virtual void prepareForDeviceLayerRequests() = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
index b407267..3571e11 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h
@@ -76,7 +76,7 @@
virtual void applyChangedTypesToLayers(const ChangedTypes&);
virtual void applyDisplayRequests(const DisplayRequests&);
virtual void applyLayerRequestsToLayers(const LayerRequests&);
- virtual void applyClientTargetRequests(const ClientTargetProperty&);
+ virtual void applyClientTargetRequests(const ClientTargetProperty&, float whitePointNits);
// Internal
virtual void setConfiguration(const compositionengine::DisplayCreationArgs&);
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
index 44f754f..c8f177b 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputCompositionState.h
@@ -121,12 +121,18 @@
// to prevent an early presentation of a frame.
std::shared_ptr<FenceTime> previousPresentFence;
+ // The expected time for the next present
+ nsecs_t expectedPresentTime{0};
+
// Current display brightness
float displayBrightnessNits{-1.f};
// SDR white point
float sdrWhitePointNits{-1.f};
+ // White point of the client target
+ float clientTargetWhitePointNits{-1.f};
+
// Debugging
void dump(std::string& result) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
index 244f8ab..0082dac 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h
@@ -27,6 +27,8 @@
#include "DisplayHardware/DisplayIdentification.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android::compositionengine {
struct LayerFECompositionState;
@@ -50,7 +52,8 @@
HWC2::Layer* getHwcLayer() const override;
bool requiresClientComposition() const override;
bool isHardwareCursor() const override;
- void applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition) override;
+ void applyDeviceCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition) override;
void prepareForDeviceLayerRequests() override;
void applyDeviceLayerRequest(Hwc2::IComposerClient::LayerRequest request) override;
bool needsFiltering() const override;
@@ -68,20 +71,24 @@
private:
Rect calculateInitialCrop() const;
- void writeOutputDependentGeometryStateToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
- uint32_t z);
+ void writeOutputDependentGeometryStateToHWC(
+ HWC2::Layer*, aidl::android::hardware::graphics::composer3::Composition, uint32_t z);
void writeOutputIndependentGeometryStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
bool skipLayer);
void writeOutputDependentPerFrameStateToHWC(HWC2::Layer*);
- void writeOutputIndependentPerFrameStateToHWC(HWC2::Layer*, const LayerFECompositionState&,
- bool skipLayer);
+ void writeOutputIndependentPerFrameStateToHWC(
+ HWC2::Layer*, const LayerFECompositionState&,
+ aidl::android::hardware::graphics::composer3::Composition compositionType,
+ bool skipLayer);
void writeSolidColorStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeSidebandStateToHWC(HWC2::Layer*, const LayerFECompositionState&);
void writeBufferStateToHWC(HWC2::Layer*, const LayerFECompositionState&, bool skipLayer);
- void writeCompositionTypeToHWC(HWC2::Layer*, Hwc2::IComposerClient::Composition,
+ void writeCompositionTypeToHWC(HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition,
bool isPeekingThrough, bool skipLayer);
- void detectDisallowedCompositionTypeChange(Hwc2::IComposerClient::Composition from,
- Hwc2::IComposerClient::Composition to) const;
+ void detectDisallowedCompositionTypeChange(
+ aidl::android::hardware::graphics::composer3::Composition from,
+ aidl::android::hardware::graphics::composer3::Composition to) const;
bool isClientCompositionForced(bool isPeekingThrough) const;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 7564c54..49cb912 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -35,6 +35,8 @@
#include "DisplayHardware/ComposerHal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -123,8 +125,8 @@
std::shared_ptr<HWC2::Layer> hwcLayer;
// The most recently set HWC composition type for this layer
- Hwc2::IComposerClient::Composition hwcCompositionType{
- Hwc2::IComposerClient::Composition::INVALID};
+ aidl::android::hardware::graphics::composer3::Composition hwcCompositionType{
+ aidl::android::hardware::graphics::composer3::Composition::INVALID};
// The buffer cache for this layer. This is used to lower the
// cost of sending reused buffers to the HWC.
@@ -146,6 +148,10 @@
// Timestamp for when the layer is queued for client composition
nsecs_t clientCompositionTimestamp{0};
+
+ // White point of the layer, in nits.
+ static constexpr float kDefaultWhitePointNits = 200.f;
+ float whitePointNits = kDefaultWhitePointNits;
};
} // namespace compositionengine::impl
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
index cff6527..92cc484 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h
@@ -87,6 +87,13 @@
const bool mEnableHolePunch;
};
+ // Constants not yet backed by a sysprop
+ // CachedSets that contain no more than this many layers may be considered inactive on the basis
+ // of FPS.
+ static constexpr int kNumLayersFpsConsideration = 1;
+ // Frames/Second threshold below which these CachedSets may be considered inactive.
+ static constexpr float kFpsActiveThreshold = 1.f;
+
Flattener(renderengine::RenderEngine& renderEngine, const Tunables& tunables);
void setDisplaySize(ui::Size size) {
@@ -127,21 +134,22 @@
class Builder {
private:
std::vector<CachedSet>::const_iterator mStart;
- std::vector<size_t> mLengths;
+ int32_t mNumSets = 0;
const CachedSet* mHolePunchCandidate = nullptr;
const CachedSet* mBlurringLayer = nullptr;
+ bool mBuilt = false;
public:
// Initializes a Builder a CachedSet to start from.
// This start iterator must be an iterator for mLayers
void init(const std::vector<CachedSet>::const_iterator& start) {
mStart = start;
- mLengths.push_back(start->getLayerCount());
+ mNumSets = 1;
}
// Appends a new CachedSet to the end of the run
// The provided length must be the size of the next sequential CachedSet in layers
- void append(size_t length) { mLengths.push_back(length); }
+ void increment() { mNumSets++; }
// Sets the hole punch candidate for the Run.
void setHolePunchCandidate(const CachedSet* holePunchCandidate) {
@@ -154,19 +162,36 @@
// Builds a Run instance, if a valid Run may be built.
std::optional<Run> validateAndBuild() {
- if (mLengths.size() == 0) {
- return std::nullopt;
- }
- // Runs of length 1 which are hole punch candidates are allowed if the candidate is
- // going to be used.
- if (mLengths.size() == 1 &&
- (!mHolePunchCandidate || !(mHolePunchCandidate->requiresHolePunch()))) {
+ const bool built = mBuilt;
+ mBuilt = true;
+ if (mNumSets <= 0 || built) {
return std::nullopt;
}
+ const bool requiresHolePunch =
+ mHolePunchCandidate && mHolePunchCandidate->requiresHolePunch();
+
+ if (!requiresHolePunch) {
+ // If we don't require a hole punch, then treat solid color layers at the front
+ // to be "cheap", so remove them from the candidate cached set.
+ while (mNumSets > 1 && mStart->getLayerCount() == 1 &&
+ mStart->getFirstLayer().getBuffer() == nullptr) {
+ mStart++;
+ mNumSets--;
+ }
+
+ // Only allow for single cached sets if a hole punch is required. If we're here,
+ // then we don't require a hole punch, so don't build a run.
+ if (mNumSets <= 1) {
+ return std::nullopt;
+ }
+ }
+
return Run(mStart,
- std::reduce(mLengths.cbegin(), mLengths.cend(), 0u,
- [](size_t left, size_t right) { return left + right; }),
+ std::reduce(mStart, mStart + mNumSets, 0u,
+ [](size_t length, const CachedSet& set) {
+ return length + set.getLayerCount();
+ }),
mHolePunchCandidate, mBlurringLayer);
}
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
index 5237527..14324de 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/LayerState.h
@@ -28,6 +28,8 @@
#include "DisplayHardware/Hal.h"
#include "math/HashCombine.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace std {
template <typename T>
struct hash<android::sp<T>> {
@@ -232,7 +234,7 @@
return mBackgroundBlurRadius.get() > 0 || !mBlurRegions.get().empty();
}
int32_t getBackgroundBlurRadius() const { return mBackgroundBlurRadius.get(); }
- hardware::graphics::composer::hal::Composition getCompositionType() const {
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType() const {
return mCompositionType.get();
}
@@ -245,6 +247,7 @@
bool isProtected() const {
return getOutputLayer()->getLayerFE().getCompositionState()->hasProtectedContent;
}
+ float getFps() const { return getOutputLayer()->getLayerFE().getCompositionState()->fps; }
void dump(std::string& result) const;
std::optional<std::string> compare(const LayerState& other) const;
@@ -369,17 +372,18 @@
OutputLayerState<mat4, LayerStateField::ColorTransform> mColorTransform;
- using CompositionTypeState = OutputLayerState<hardware::graphics::composer::hal::Composition,
- LayerStateField::CompositionType>;
- CompositionTypeState
- mCompositionType{[](auto layer) {
- return layer->getState().forceClientComposition
- ? hardware::graphics::composer::hal::Composition::CLIENT
- : layer->getLayerFE()
- .getCompositionState()
- ->compositionType;
- },
- CompositionTypeState::getHalToStrings()};
+ using CompositionTypeState =
+ OutputLayerState<aidl::android::hardware::graphics::composer3::Composition,
+ LayerStateField::CompositionType>;
+ CompositionTypeState mCompositionType{[](auto layer) {
+ return layer->getState().forceClientComposition
+ ? aidl::android::hardware::graphics::
+ composer3::Composition::CLIENT
+ : layer->getLayerFE()
+ .getCompositionState()
+ ->compositionType;
+ },
+ CompositionTypeState::getHalToStrings()};
OutputLayerState<void*, LayerStateField::SidebandStream>
mSidebandStream{[](auto layer) {
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
index fe486d3..ef1560e 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Predictor.h
@@ -109,7 +109,7 @@
static std::optional<Plan> fromString(const std::string&);
void reset() { mLayerTypes.clear(); }
- void addLayerType(hardware::graphics::composer::hal::Composition type) {
+ void addLayerType(aidl::android::hardware::graphics::composer3::Composition type) {
mLayerTypes.emplace_back(type);
}
@@ -125,7 +125,7 @@
}
private:
- std::vector<hardware::graphics::composer::hal::Composition> mLayerTypes;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition> mLayerTypes;
};
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
index 358ed5a..a6cb811 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -46,7 +46,8 @@
MOCK_CONST_METHOD0(getHwcLayer, HWC2::Layer*());
MOCK_CONST_METHOD0(requiresClientComposition, bool());
MOCK_CONST_METHOD0(isHardwareCursor, bool());
- MOCK_METHOD1(applyDeviceCompositionTypeChange, void(Hwc2::IComposerClient::Composition));
+ MOCK_METHOD1(applyDeviceCompositionTypeChange,
+ void(aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD0(prepareForDeviceLayerRequests, void());
MOCK_METHOD1(applyDeviceLayerRequest, void(Hwc2::IComposerClient::LayerRequest request));
MOCK_CONST_METHOD0(needsFiltering, bool());
diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp
index 02fa49f..5543ad5 100644
--- a/services/surfaceflinger/CompositionEngine/src/Display.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp
@@ -227,7 +227,8 @@
if (status_t result =
hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(),
getState().earliestPresentTime,
- getState().previousPresentFence, &changes);
+ getState().previousPresentFence,
+ getState().expectedPresentTime, &changes);
result != NO_ERROR) {
ALOGE("chooseCompositionStrategy failed for %s: %d (%s)", getName().c_str(), result,
strerror(-result));
@@ -237,7 +238,8 @@
applyChangedTypesToLayers(changes->changedTypes);
applyDisplayRequests(changes->displayRequests);
applyLayerRequestsToLayers(changes->layerRequests);
- applyClientTargetRequests(changes->clientTargetProperty);
+ applyClientTargetRequests(changes->clientTargetProperty,
+ changes->clientTargetWhitePointNits);
}
// Determine what type of composition we are doing from the final state
@@ -281,7 +283,8 @@
if (auto it = changedTypes.find(hwcLayer); it != changedTypes.end()) {
layer->applyDeviceCompositionTypeChange(
- static_cast<Hwc2::IComposerClient::Composition>(it->second));
+ static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+ it->second));
}
}
}
@@ -309,12 +312,14 @@
}
}
-void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty) {
+void Display::applyClientTargetRequests(const ClientTargetProperty& clientTargetProperty,
+ float whitePointNits) {
if (clientTargetProperty.dataspace == ui::Dataspace::UNKNOWN) {
return;
}
editState().dataspace = clientTargetProperty.dataspace;
+ editState().clientTargetWhitePointNits = whitePointNits;
getRenderSurface()->setBufferDataspace(clientTargetProperty.dataspace);
getRenderSurface()->setBufferPixelFormat(clientTargetProperty.pixelFormat);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6800004..6833584 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -728,6 +728,7 @@
editState().earliestPresentTime = refreshArgs.earliestPresentTime;
editState().previousPresentFence = refreshArgs.previousPresentFence;
+ editState().expectedPresentTime = refreshArgs.expectedPresentTime;
compositionengine::OutputLayer* peekThroughLayer = nullptr;
sp<GraphicBuffer> previousOverride = nullptr;
@@ -1057,7 +1058,7 @@
clientCompositionDisplay.maxLuminance = outputState.displayBrightnessNits > 0.f
? outputState.displayBrightnessNits
: mDisplayColorProfile->getHdrCapabilities().getDesiredMaxLuminance();
- clientCompositionDisplay.sdrWhitePointNits = outputState.sdrWhitePointNits;
+ clientCompositionDisplay.targetLuminanceNits = outputState.clientTargetWhitePointNits;
// Compute the global color transform matrix.
if (!outputState.usesDeviceComposition && !getSkipColorTransform()) {
@@ -1217,7 +1218,8 @@
.dataspace = outputDataspace,
.realContentIsVisible = realContentIsVisible,
.clearContent = !clientComposition,
- .blurSetting = blurSetting};
+ .blurSetting = blurSetting,
+ .whitePointNits = layerState.whitePointNits};
results = layerFE.prepareClientCompositionList(targetSettings);
if (realContentIsVisible && !results.empty()) {
layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index e958549..94ad6c3 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -24,6 +24,9 @@
#include <compositionengine/impl/OutputLayer.h>
#include <compositionengine/impl/OutputLayerCompositionState.h>
#include <cstdint>
+#include "system/graphics-base-v1.0.h"
+
+#include <ui/DataspaceUtils.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic push
@@ -34,6 +37,8 @@
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion"
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
OutputLayer::~OutputLayer() = default;
@@ -317,6 +322,14 @@
? outputState.targetDataspace
: layerFEState->dataspace;
+ // For hdr content, treat the white point as the display brightness - HDR content should not be
+ // boosted or dimmed.
+ if (isHdrDataspace(state.dataspace)) {
+ state.whitePointNits = getOutput().getState().displayBrightnessNits;
+ } else {
+ state.whitePointNits = getOutput().getState().sdrWhitePointNits;
+ }
+
// These are evaluated every frame as they can potentially change at any
// time.
if (layerFEState->forceClientComposition || !profile.isDataspaceSupported(state.dataspace) ||
@@ -347,6 +360,10 @@
auto requestedCompositionType = outputIndependentState->compositionType;
+ if (requestedCompositionType == Composition::SOLID_COLOR && state.overrideInfo.buffer) {
+ requestedCompositionType = Composition::DEVICE;
+ }
+
// TODO(b/181172795): We now update geometry for all flattened layers. We should update it
// only when the geometry actually changes
const bool isOverridden =
@@ -359,20 +376,22 @@
}
writeOutputDependentPerFrameStateToHWC(hwcLayer.get());
- writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState, skipLayer);
+ writeOutputIndependentPerFrameStateToHWC(hwcLayer.get(), *outputIndependentState,
+ requestedCompositionType, skipLayer);
writeCompositionTypeToHWC(hwcLayer.get(), requestedCompositionType, isPeekingThrough,
skipLayer);
- // Always set the layer color after setting the composition type.
- writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+ if (requestedCompositionType == Composition::SOLID_COLOR) {
+ writeSolidColorStateToHWC(hwcLayer.get(), *outputIndependentState);
+ }
editState().hwc->stateOverridden = isOverridden;
editState().hwc->layerSkipped = skipLayer;
}
void OutputLayer::writeOutputDependentGeometryStateToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType,
+ Composition requestedCompositionType,
uint32_t z) {
const auto& outputDependentState = getState();
@@ -406,7 +425,7 @@
}
// Solid-color layers and overridden buffers should always use an identity transform.
- const auto bufferTransform = (requestedCompositionType != hal::Composition::SOLID_COLOR &&
+ const auto bufferTransform = (requestedCompositionType != Composition::SOLID_COLOR &&
getState().overrideInfo.buffer == nullptr)
? outputDependentState.bufferTransform
: static_cast<hal::Transform>(0);
@@ -473,11 +492,21 @@
ALOGE("[%s] Failed to set dataspace %d: %s (%d)", getLayerFE().getDebugName(), dataspace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
+
+ // Don't dim cached layers
+ const auto whitePointNits = outputDependentState.overrideInfo.buffer
+ ? getOutput().getState().displayBrightnessNits
+ : outputDependentState.whitePointNits;
+
+ if (auto error = hwcLayer->setWhitePointNits(whitePointNits); error != hal::Error::NONE) {
+ ALOGE("[%s] Failed to set white point %f: %s (%d)", getLayerFE().getDebugName(),
+ whitePointNits, to_string(error).c_str(), static_cast<int32_t>(error));
+ }
}
void OutputLayer::writeOutputIndependentPerFrameStateToHWC(
HWC2::Layer* hwcLayer, const LayerFECompositionState& outputIndependentState,
- bool skipLayer) {
+ Composition compositionType, bool skipLayer) {
switch (auto error = hwcLayer->setColorTransform(outputIndependentState.colorTransform)) {
case hal::Error::NONE:
break;
@@ -501,19 +530,20 @@
}
// Content-specific per-frame state
- switch (outputIndependentState.compositionType) {
- case hal::Composition::SOLID_COLOR:
+ switch (compositionType) {
+ case Composition::SOLID_COLOR:
// For compatibility, should be written AFTER the composition type.
break;
- case hal::Composition::SIDEBAND:
+ case Composition::SIDEBAND:
writeSidebandStateToHWC(hwcLayer, outputIndependentState);
break;
- case hal::Composition::CURSOR:
- case hal::Composition::DEVICE:
+ case Composition::CURSOR:
+ case Composition::DEVICE:
+ case Composition::DISPLAY_DECORATION:
writeBufferStateToHWC(hwcLayer, outputIndependentState, skipLayer);
break;
- case hal::Composition::INVALID:
- case hal::Composition::CLIENT:
+ case Composition::INVALID:
+ case Composition::CLIENT:
// Ignored
break;
}
@@ -521,10 +551,6 @@
void OutputLayer::writeSolidColorStateToHWC(HWC2::Layer* hwcLayer,
const LayerFECompositionState& outputIndependentState) {
- if (outputIndependentState.compositionType != hal::Composition::SOLID_COLOR) {
- return;
- }
-
hal::Color color = {static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.r)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.g)),
static_cast<uint8_t>(std::round(255.0f * outputIndependentState.color.b)),
@@ -583,13 +609,13 @@
}
void OutputLayer::writeCompositionTypeToHWC(HWC2::Layer* hwcLayer,
- hal::Composition requestedCompositionType,
+ Composition requestedCompositionType,
bool isPeekingThrough, bool skipLayer) {
auto& outputDependentState = editState();
if (isClientCompositionForced(isPeekingThrough)) {
// If we are forcing client composition, we need to tell the HWC
- requestedCompositionType = hal::Composition::CLIENT;
+ requestedCompositionType = Composition::CLIENT;
}
// Set the requested composition type with the HWC whenever it changes
@@ -602,7 +628,7 @@
if (auto error = hwcLayer->setCompositionType(requestedCompositionType);
error != hal::Error::NONE) {
ALOGE("[%s] Failed to set composition type %s: %s (%d)", getLayerFE().getDebugName(),
- toString(requestedCompositionType).c_str(), to_string(error).c_str(),
+ to_string(requestedCompositionType).c_str(), to_string(error).c_str(),
static_cast<int32_t>(error));
}
}
@@ -641,38 +667,38 @@
bool OutputLayer::requiresClientComposition() const {
const auto& state = getState();
- return !state.hwc || state.hwc->hwcCompositionType == hal::Composition::CLIENT;
+ return !state.hwc || state.hwc->hwcCompositionType == Composition::CLIENT;
}
bool OutputLayer::isHardwareCursor() const {
const auto& state = getState();
- return state.hwc && state.hwc->hwcCompositionType == hal::Composition::CURSOR;
+ return state.hwc && state.hwc->hwcCompositionType == Composition::CURSOR;
}
-void OutputLayer::detectDisallowedCompositionTypeChange(hal::Composition from,
- hal::Composition to) const {
+void OutputLayer::detectDisallowedCompositionTypeChange(Composition from, Composition to) const {
bool result = false;
switch (from) {
- case hal::Composition::INVALID:
- case hal::Composition::CLIENT:
+ case Composition::INVALID:
+ case Composition::CLIENT:
result = false;
break;
- case hal::Composition::DEVICE:
- case hal::Composition::SOLID_COLOR:
- result = (to == hal::Composition::CLIENT);
+ case Composition::DEVICE:
+ case Composition::SOLID_COLOR:
+ case Composition::DISPLAY_DECORATION:
+ result = (to == Composition::CLIENT);
break;
- case hal::Composition::CURSOR:
- case hal::Composition::SIDEBAND:
- result = (to == hal::Composition::CLIENT || to == hal::Composition::DEVICE);
+ case Composition::CURSOR:
+ case Composition::SIDEBAND:
+ result = (to == Composition::CLIENT || to == Composition::DEVICE);
break;
}
if (!result) {
ALOGE("[%s] Invalid device requested composition type change: %s (%d) --> %s (%d)",
- getLayerFE().getDebugName(), toString(from).c_str(), static_cast<int>(from),
- toString(to).c_str(), static_cast<int>(to));
+ getLayerFE().getDebugName(), to_string(from).c_str(), static_cast<int>(from),
+ to_string(to).c_str(), static_cast<int>(to));
}
}
@@ -681,7 +707,7 @@
(!isPeekingThrough && getLayerFE().hasRoundedCorners());
}
-void OutputLayer::applyDeviceCompositionTypeChange(hal::Composition compositionType) {
+void OutputLayer::applyDeviceCompositionTypeChange(Composition compositionType) {
auto& state = editState();
LOG_FATAL_IF(!state.hwc);
auto& hwcState = *state.hwc;
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/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
index ec52e59..2203f22 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp
@@ -25,6 +25,7 @@
#include <math/HashCombine.h>
#include <renderengine/DisplaySettings.h>
#include <renderengine/RenderEngine.h>
+#include <ui/DebugUtils.h>
#include <utils/Trace.h>
#include <utils/Trace.h>
@@ -169,6 +170,7 @@
.clip = viewport,
.outputDataspace = outputDataspace,
.orientation = orientation,
+ .targetLuminanceNits = outputState.displayBrightnessNits,
};
LayerFE::ClientCompositionTargetSettings targetSettings{
@@ -181,6 +183,7 @@
.realContentIsVisible = true,
.clearContent = false,
.blurSetting = LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ .whitePointNits = outputState.displayBrightnessNits,
};
std::vector<renderengine::LayerSettings> layerSettings;
@@ -391,20 +394,29 @@
if (mLayers.size() == 1) {
base::StringAppendF(&result, " Layer [%s]\n", mLayers[0].getName().c_str());
- base::StringAppendF(&result, " Buffer %p", mLayers[0].getBuffer().get());
- base::StringAppendF(&result, " Protected [%s]",
+ if (auto* buffer = mLayers[0].getBuffer().get()) {
+ base::StringAppendF(&result, " Buffer %p", buffer);
+ base::StringAppendF(&result, " Format %s",
+ decodePixelFormat(buffer->getPixelFormat()).c_str());
+ }
+ base::StringAppendF(&result, " Protected [%s]\n",
mLayers[0].getState()->isProtected() ? "true" : "false");
} else {
- result.append(" Cached set of:");
+ result.append(" Cached set of:\n");
for (const Layer& layer : mLayers) {
- base::StringAppendF(&result, "\n Layer [%s]", layer.getName().c_str());
- base::StringAppendF(&result, "\n Protected [%s]",
+ base::StringAppendF(&result, " Layer [%s]\n", layer.getName().c_str());
+ if (auto* buffer = layer.getBuffer().get()) {
+ base::StringAppendF(&result, " Buffer %p", buffer);
+ base::StringAppendF(&result, " Format[%s]",
+ decodePixelFormat(buffer->getPixelFormat()).c_str());
+ }
+ base::StringAppendF(&result, " Protected [%s]\n",
layer.getState()->isProtected() ? "true" : "false");
}
}
- base::StringAppendF(&result, "\n Creation cost: %zd", getCreationCost());
- base::StringAppendF(&result, "\n Display cost: %zd\n", getDisplayCost());
+ base::StringAppendF(&result, " Creation cost: %zd\n", getCreationCost());
+ base::StringAppendF(&result, " Display cost: %zd\n", getDisplayCost());
}
} // namespace android::compositionengine::impl::planner
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
index 2272099..0918510 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp
@@ -211,7 +211,8 @@
displayCost += static_cast<size_t>(layer->getDisplayFrame().width() *
layer->getDisplayFrame().height());
- hasClientComposition |= layer->getCompositionType() == hal::Composition::CLIENT;
+ hasClientComposition |= layer->getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT;
}
if (hasClientComposition) {
@@ -409,25 +410,29 @@
bool runHasFirstLayer = false;
for (auto currentSet = mLayers.cbegin(); currentSet != mLayers.cend(); ++currentSet) {
- const bool layerIsInactive =
- now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
+ bool layerIsInactive = now - currentSet->getLastUpdate() > mTunables.mActiveLayerTimeout;
const bool layerHasBlur = currentSet->hasBlurBehind();
+ // Layers should also be considered inactive whenever their framerate is lower than 1fps.
+ if (!layerIsInactive && currentSet->getLayerCount() == kNumLayersFpsConsideration) {
+ auto layerFps = currentSet->getFirstLayer().getState()->getFps();
+ if (layerFps > 0 && layerFps <= kFpsActiveThreshold) {
+ ATRACE_FORMAT("layer is considered inactive due to low FPS [%s] %f",
+ currentSet->getFirstLayer().getName().c_str(), layerFps);
+ layerIsInactive = true;
+ }
+ }
+
if (layerIsInactive && (firstLayer || runHasFirstLayer || !layerHasBlur) &&
!currentSet->hasUnsupportedDataspace()) {
if (isPartOfRun) {
- builder.append(currentSet->getLayerCount());
+ builder.increment();
} else {
- // Runs can't start with a non-buffer layer
- if (currentSet->getFirstLayer().getBuffer() == nullptr) {
- ALOGV("[%s] Skipping initial non-buffer layer", __func__);
- } else {
- builder.init(currentSet);
- if (firstLayer) {
- runHasFirstLayer = true;
- }
- isPartOfRun = true;
+ builder.init(currentSet);
+ if (firstLayer) {
+ runHasFirstLayer = true;
}
+ isPartOfRun = true;
}
} else if (isPartOfRun) {
builder.setHolePunchCandidate(&(*currentSet));
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
index 2532e3d..c79ca0d 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp
@@ -160,7 +160,8 @@
lhs.mColorTransform == rhs.mColorTransform &&
lhs.mCompositionType == rhs.mCompositionType &&
lhs.mSidebandStream == rhs.mSidebandStream && lhs.mBuffer == rhs.mBuffer &&
- (lhs.mCompositionType.get() != hal::Composition::SOLID_COLOR ||
+ (lhs.mCompositionType.get() !=
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR ||
lhs.mSolidColor == rhs.mSolidColor);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
index f5b1cee..74d2701 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp
@@ -193,7 +193,7 @@
finalPlan.addLayerType(
forcedOrRequestedClient
- ? hardware::graphics::composer::hal::Composition::CLIENT
+ ? aidl::android::hardware::graphics::composer3::Composition::CLIENT
: layer->getLayerFE().getCompositionState()->compositionType);
}
diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
index 8226ef7..2d53583 100644
--- a/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/planner/Predictor.cpp
@@ -39,8 +39,10 @@
// Skip layers where both are client-composited, since that doesn't change the
// composition plan
- if (mLayers[i].getCompositionType() == hal::Composition::CLIENT &&
- other[i]->getCompositionType() == hal::Composition::CLIENT) {
+ if (mLayers[i].getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT &&
+ other[i]->getCompositionType() ==
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT) {
continue;
}
@@ -89,22 +91,32 @@
for (char c : string) {
switch (c) {
case 'C':
- plan.addLayerType(hal::Composition::CLIENT);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT);
continue;
case 'U':
- plan.addLayerType(hal::Composition::CURSOR);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR);
continue;
case 'D':
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE);
continue;
case 'I':
- plan.addLayerType(hal::Composition::INVALID);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::INVALID);
continue;
case 'B':
- plan.addLayerType(hal::Composition::SIDEBAND);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND);
continue;
case 'S':
- plan.addLayerType(hal::Composition::SOLID_COLOR);
+ plan.addLayerType(
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR);
+ continue;
+ case 'A':
+ plan.addLayerType(aidl::android::hardware::graphics::composer3::Composition::
+ DISPLAY_DECORATION);
continue;
default:
return std::nullopt;
@@ -117,24 +129,28 @@
std::string result;
for (auto type : plan.mLayerTypes) {
switch (type) {
- case hal::Composition::CLIENT:
+ case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
result.append("C");
break;
- case hal::Composition::CURSOR:
+ case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
result.append("U");
break;
- case hal::Composition::DEVICE:
+ case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
result.append("D");
break;
- case hal::Composition::INVALID:
+ case aidl::android::hardware::graphics::composer3::Composition::INVALID:
result.append("I");
break;
- case hal::Composition::SIDEBAND:
+ case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
result.append("B");
break;
- case hal::Composition::SOLID_COLOR:
+ case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
result.append("S");
break;
+ case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+ // A for "Alpha", since the decoration is an alpha layer.
+ result.append("A");
+ break;
}
}
return result;
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index ed235b8..88f00a5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -38,6 +38,10 @@
#include "MockHWComposer.h"
#include "MockPowerAdvisor.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
namespace {
@@ -556,7 +560,7 @@
TEST_F(DisplayChooseCompositionStrategyTest, takesEarlyOutOnHwcError) {
EXPECT_CALL(*mDisplay, anyLayersRequireClientComposition()).WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _))
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), false, _, _, _, _))
.WillOnce(Return(INVALID_OPERATION));
mDisplay->chooseCompositionStrategy();
@@ -579,7 +583,7 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
.WillOnce(Return(NO_ERROR));
EXPECT_CALL(*mDisplay, allLayersRequireClientComposition()).WillOnce(Return(false));
@@ -592,10 +596,11 @@
TEST_F(DisplayChooseCompositionStrategyTest, normalOperationWithChanges) {
android::HWComposer::DeviceRequestedChanges changes{
- {{nullptr, hal::Composition::CLIENT}},
+ {{nullptr, Composition::CLIENT}},
hal::DisplayRequest::FLIP_CLIENT_TARGET,
{{nullptr, hal::LayerRequest::CLEAR_CLIENT_TARGET}},
{hal::PixelFormat::RGBA_8888, hal::Dataspace::UNKNOWN},
+ -1.f,
};
// Since two calls are made to anyLayersRequireClientComposition with different return
@@ -610,8 +615,8 @@
.WillOnce(Return(false));
EXPECT_CALL(mHwComposer,
- getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _))
- .WillOnce(DoAll(SetArgPointee<4>(changes), Return(NO_ERROR)));
+ getDeviceCompositionChanges(HalDisplayId(DEFAULT_DISPLAY_ID), true, _, _, _, _))
+ .WillOnce(DoAll(SetArgPointee<5>(changes), Return(NO_ERROR)));
EXPECT_CALL(*mDisplay, applyChangedTypesToLayers(changes.changedTypes)).Times(1);
EXPECT_CALL(*mDisplay, applyDisplayRequests(changes.displayRequests)).Times(1);
EXPECT_CALL(*mDisplay, applyLayerRequestsToLayers(changes.layerRequests)).Times(1);
@@ -699,17 +704,15 @@
}
TEST_F(DisplayApplyChangedTypesToLayersTest, appliesChanges) {
- EXPECT_CALL(*mLayer1.outputLayer,
- applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT))
+ EXPECT_CALL(*mLayer1.outputLayer, applyDeviceCompositionTypeChange(Composition::CLIENT))
.Times(1);
- EXPECT_CALL(*mLayer2.outputLayer,
- applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::DEVICE))
+ EXPECT_CALL(*mLayer2.outputLayer, applyDeviceCompositionTypeChange(Composition::DEVICE))
.Times(1);
mDisplay->applyChangedTypesToLayers(impl::Display::ChangedTypes{
- {&mLayer1.hwc2Layer, hal::Composition::CLIENT},
- {&mLayer2.hwc2Layer, hal::Composition::DEVICE},
- {&hwc2LayerUnknown, hal::Composition::SOLID_COLOR},
+ {&mLayer1.hwc2Layer, Composition::CLIENT},
+ {&mLayer2.hwc2Layer, Composition::DEVICE},
+ {&hwc2LayerUnknown, Composition::SOLID_COLOR},
});
}
@@ -788,15 +791,18 @@
.dataspace = hal::Dataspace::STANDARD_BT470M,
};
+ static constexpr float kWhitePointNits = 800.f;
+
mock::RenderSurface* renderSurface = new StrictMock<mock::RenderSurface>();
mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(renderSurface));
EXPECT_CALL(*renderSurface, setBufferPixelFormat(clientTargetProperty.pixelFormat));
EXPECT_CALL(*renderSurface, setBufferDataspace(clientTargetProperty.dataspace));
- mDisplay->applyClientTargetRequests(clientTargetProperty);
+ mDisplay->applyClientTargetRequests(clientTargetProperty, kWhitePointNits);
auto& state = mDisplay->getState();
EXPECT_EQ(clientTargetProperty.dataspace, state.dataspace);
+ EXPECT_EQ(kWhitePointNits, state.clientTargetWhitePointNits);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
index 9518659..ff68053 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h
@@ -32,6 +32,8 @@
#include <ui/GraphicTypes.h>
#include "DisplayHardware/HWC2.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -57,7 +59,8 @@
MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&));
MOCK_METHOD1(setBlendMode, Error(hal::BlendMode));
MOCK_METHOD1(setColor, Error(hal::Color));
- MOCK_METHOD1(setCompositionType, Error(hal::Composition));
+ MOCK_METHOD1(setCompositionType,
+ Error(aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace));
MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&));
MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&));
@@ -71,6 +74,7 @@
MOCK_METHOD1(setColorTransform, Error(const android::mat4&));
MOCK_METHOD3(setLayerGenericMetadata,
Error(const std::string&, bool, const std::vector<uint8_t>&));
+ MOCK_METHOD1(setWhitePointNits, Error(float));
};
} // namespace mock
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 224dad1..0f174db 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -51,9 +51,9 @@
MOCK_METHOD2(allocatePhysicalDisplay, void(hal::HWDisplayId, PhysicalDisplayId));
MOCK_METHOD1(createLayer, std::shared_ptr<HWC2::Layer>(HalDisplayId));
- MOCK_METHOD5(getDeviceCompositionChanges,
+ MOCK_METHOD6(getDeviceCompositionChanges,
status_t(HalDisplayId, bool, std::chrono::steady_clock::time_point,
- const std::shared_ptr<FenceTime>&,
+ const std::shared_ptr<FenceTime>&, nsecs_t,
std::optional<android::HWComposer::DeviceRequestedChanges>*));
MOCK_METHOD5(setClientTarget,
status_t(HalDisplayId, uint32_t, const sp<Fence>&, const sp<GraphicBuffer>&,
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index fbc2089..ad7976f 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -30,6 +30,10 @@
#include "MockHWComposer.h"
#include "RegionMatcher.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine {
namespace {
@@ -652,6 +656,22 @@
EXPECT_EQ(ui::Dataspace::V0_SCRGB, mOutputLayer.getState().dataspace);
}
+TEST_F(OutputLayerUpdateCompositionStateTest, setsWhitePointNitsCorrectly) {
+ mOutputState.sdrWhitePointNits = 200.f;
+ mOutputState.displayBrightnessNits = 800.f;
+
+ mLayerFEState.dataspace = ui::Dataspace::DISPLAY_P3;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+ EXPECT_EQ(mOutputState.sdrWhitePointNits, mOutputLayer.getState().whitePointNits);
+
+ mLayerFEState.dataspace = ui::Dataspace::BT2020_ITU_PQ;
+ mLayerFEState.isColorspaceAgnostic = false;
+ mOutputLayer.updateCompositionState(false, false, ui::Transform::RotationFlags::ROT_0);
+
+ EXPECT_EQ(mOutputState.displayBrightnessNits, mOutputLayer.getState().whitePointNits);
+}
+
TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) {
mOutputLayer.editState().forceClientComposition = false;
@@ -728,6 +748,8 @@
static constexpr int kOverrideHwcSlot = impl::HwcBufferCache::FLATTENER_CACHING_SLOT;
static constexpr bool kLayerGenericMetadata1Mandatory = true;
static constexpr bool kLayerGenericMetadata2Mandatory = true;
+ static constexpr float kWhitePointNits = 200.f;
+ static constexpr float kDisplayBrightnessNits = 400.f;
static const half4 kColor;
static const Rect kDisplayFrame;
@@ -758,6 +780,7 @@
outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform);
outputLayerState.outputSpaceVisibleRegion = kOutputSpaceVisibleRegion;
outputLayerState.dataspace = kDataspace;
+ outputLayerState.whitePointNits = kWhitePointNits;
mLayerFEState.blendMode = kBlendMode;
mLayerFEState.alpha = kAlpha;
@@ -770,6 +793,8 @@
mLayerFEState.bufferSlot = BufferQueue::INVALID_BUFFER_SLOT;
mLayerFEState.acquireFence = kFence;
+ mOutputState.displayBrightnessNits = kDisplayBrightnessNits;
+
EXPECT_CALL(mOutput, getDisplayColorProfile())
.WillRepeatedly(Return(&mDisplayColorProfile));
EXPECT_CALL(mDisplayColorProfile, getSupportedPerFrameMetadata())
@@ -818,9 +843,11 @@
void expectPerFrameCommonCalls(SimulateUnsupported unsupported = SimulateUnsupported::None,
ui::Dataspace dataspace = kDataspace,
const Region& visibleRegion = kOutputSpaceVisibleRegion,
- const Region& surfaceDamage = kSurfaceDamage) {
+ const Region& surfaceDamage = kSurfaceDamage,
+ float whitePointNits = kWhitePointNits) {
EXPECT_CALL(*mHwcLayer, setVisibleRegion(RegionEq(visibleRegion))).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setDataspace(dataspace)).WillOnce(Return(kError));
+ EXPECT_CALL(*mHwcLayer, setWhitePointNits(whitePointNits)).WillOnce(Return(kError));
EXPECT_CALL(*mHwcLayer, setColorTransform(kColorTransform))
.WillOnce(Return(unsupported == SimulateUnsupported::ColorTransform
? hal::Error::UNSUPPORTED
@@ -828,7 +855,7 @@
EXPECT_CALL(*mHwcLayer, setSurfaceDamage(RegionEq(surfaceDamage))).WillOnce(Return(kError));
}
- void expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition compositionType) {
+ void expectSetCompositionTypeCall(Composition compositionType) {
EXPECT_CALL(*mHwcLayer, setCompositionType(compositionType)).WillOnce(Return(kError));
}
@@ -952,7 +979,7 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -961,7 +988,7 @@
// check this in this test only by setting up an testing::InSeqeuence
// instance before setting up the two expectations.
InSequence s;
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SOLID_COLOR);
+ expectSetCompositionTypeCall(Composition::SOLID_COLOR);
expectSetColorCall();
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
@@ -969,11 +996,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSideband) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SIDEBAND;
+ mLayerFEState.compositionType = Composition::SIDEBAND;
expectPerFrameCommonCalls();
expectSetSidebandHandleCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::SIDEBAND);
+ expectSetCompositionTypeCall(Composition::SIDEBAND);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -982,11 +1009,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForCursor) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::CURSOR;
+ mLayerFEState.compositionType = Composition::CURSOR;
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CURSOR);
+ expectSetCompositionTypeCall(Composition::CURSOR);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -995,11 +1022,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForDevice) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1008,10 +1035,9 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsNotSetIfUnchanged) {
- (*mOutputLayer.editState().hwc).hwcCompositionType =
- Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ (*mOutputLayer.editState().hwc).hwcCompositionType = Composition::SOLID_COLOR;
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
expectSetColorCall();
@@ -1024,11 +1050,11 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfColorTransformNotSupported) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls(SimulateUnsupported::ColorTransform);
expectSetColorCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
@@ -1037,25 +1063,25 @@
TEST_F(OutputLayerWriteStateToHWCTest, compositionTypeIsSetToClientIfClientCompositionForced) {
mOutputLayer.editState().forceClientComposition = true;
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
expectPerFrameCommonCalls();
expectSetColorCall();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ false, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/ false);
}
TEST_F(OutputLayerWriteStateToHWCTest, allStateIncludesMetadataIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeGenericLayerMetadataInState();
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
expectGenericLayerMetadataCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1064,12 +1090,12 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, perFrameStateDoesNotIncludeMetadataIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeGenericLayerMetadataInState();
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
@@ -1078,15 +1104,31 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerDoesNotSendBuffer) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeOverrideInfo();
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kSkipAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, overriddenSkipLayerForSolidColorDoesNotSendBuffer) {
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kSkipAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
+ expectSetHdrMetadataAndBufferCalls();
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ true, 0,
@@ -1094,15 +1136,31 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoIfPresent) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
includeOverrideInfo();
expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
kOverrideBlendMode, kOverrideAlpha);
expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
- kOverrideSurfaceDamage);
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
+ EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
+
+ mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
+ /*zIsOverridden*/ false, /*isPeekingThrough*/ false);
+}
+
+TEST_F(OutputLayerWriteStateToHWCTest, includesOverrideInfoForSolidColorIfPresent) {
+ mLayerFEState.compositionType = Composition::SOLID_COLOR;
+ includeOverrideInfo();
+
+ expectGeometryCommonCalls(kOverrideDisplayFrame, kOverrideSourceCrop, kOverrideBufferTransform,
+ kOverrideBlendMode, kOverrideAlpha);
+ expectPerFrameCommonCalls(SimulateUnsupported::None, kOverrideDataspace, kOverrideVisibleRegion,
+ kOverrideSurfaceDamage, kDisplayBrightnessNits);
+ expectSetHdrMetadataAndBufferCalls(kOverrideHwcSlot, kOverrideBuffer, kOverrideFence);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1110,14 +1168,14 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousOverriddenLayerSendsSurfaceDamage) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().hwc->stateOverridden = true;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1125,16 +1183,16 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedDeviceCompositionInfo) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().hwc->stateOverridden = true;
mOutputLayer.editState().hwc->layerSkipped = true;
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1142,17 +1200,17 @@
}
TEST_F(OutputLayerWriteStateToHWCTest, previousSkipLayerSendsUpdatedClientCompositionInfo) {
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.editState().forceClientComposition = true;
mOutputLayer.editState().hwc->stateOverridden = true;
mOutputLayer.editState().hwc->layerSkipped = true;
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
expectGeometryCommonCalls();
expectPerFrameCommonCalls(SimulateUnsupported::None, kDataspace, kOutputSpaceVisibleRegion,
Region::INVALID_REGION);
expectSetHdrMetadataAndBufferCalls();
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(false));
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
@@ -1198,7 +1256,7 @@
expectGeometryCommonCalls();
expectPerFrameCommonCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillOnce(Return(true));
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::CLIENT);
+ expectSetCompositionTypeCall(Composition::CLIENT);
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
@@ -1210,14 +1268,13 @@
expectPerFrameCommonCalls();
expectSetHdrMetadataAndBufferCalls();
EXPECT_CALL(*mLayerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
- expectSetCompositionTypeCall(Hwc2::IComposerClient::Composition::DEVICE);
+ expectSetCompositionTypeCall(Composition::DEVICE);
- mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mLayerFEState.compositionType = Composition::DEVICE;
mOutputLayer.writeStateToHWC(/*includeGeometry*/ true, /*skipLayer*/ false, 0,
/*zIsOverridden*/ false, /*isPeekingThrough*/
true);
- EXPECT_EQ(Hwc2::IComposerClient::Composition::DEVICE,
- mOutputLayer.getState().hwc->hwcCompositionType);
+ EXPECT_EQ(Composition::DEVICE, mOutputLayer.getState().hwc->hwcCompositionType);
}
/*
@@ -1316,14 +1373,14 @@
TEST_F(OutputLayerTest, requiresClientCompositionReturnsTrueIfSetToClientComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CLIENT;
EXPECT_TRUE(mOutputLayer.requiresClientComposition());
}
TEST_F(OutputLayerTest, requiresClientCompositionReturnsFalseIfSetToDeviceComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
EXPECT_FALSE(mOutputLayer.requiresClientComposition());
}
@@ -1340,14 +1397,14 @@
TEST_F(OutputLayerTest, isHardwareCursorReturnsTrueIfSetToCursorComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::CURSOR;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::CURSOR;
EXPECT_TRUE(mOutputLayer.isHardwareCursor());
}
TEST_F(OutputLayerTest, isHardwareCursorReturnsFalseIfSetToDeviceComposition) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
EXPECT_FALSE(mOutputLayer.isHardwareCursor());
}
@@ -1358,13 +1415,12 @@
TEST_F(OutputLayerTest, applyDeviceCompositionTypeChangeSetsNewType) {
mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc{nullptr};
- mOutputLayer.editState().hwc->hwcCompositionType = Hwc2::IComposerClient::Composition::DEVICE;
+ mOutputLayer.editState().hwc->hwcCompositionType = Composition::DEVICE;
- mOutputLayer.applyDeviceCompositionTypeChange(Hwc2::IComposerClient::Composition::CLIENT);
+ mOutputLayer.applyDeviceCompositionTypeChange(Composition::CLIENT);
ASSERT_TRUE(mOutputLayer.getState().hwc);
- EXPECT_EQ(Hwc2::IComposerClient::Composition::CLIENT,
- mOutputLayer.getState().hwc->hwcCompositionType);
+ EXPECT_EQ(Composition::CLIENT, mOutputLayer.getState().hwc->hwcCompositionType);
}
/*
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index cf63ef5..6d96260 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3023,6 +3023,7 @@
mOutput.mState.usesDeviceComposition = false;
mOutput.mState.reusedClientComposition = false;
mOutput.mState.flipClientTarget = false;
+ mOutput.mState.clientTargetWhitePointNits = kClientTargetLuminanceNits;
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
@@ -3057,6 +3058,7 @@
static constexpr float kDefaultMaxLuminance = 0.9f;
static constexpr float kDefaultAvgLuminance = 0.7f;
static constexpr float kDefaultMinLuminance = 0.1f;
+ static constexpr float kClientTargetLuminanceNits = 200.f;
static const Rect kDefaultOutputFrame;
static const Rect kDefaultOutputViewport;
@@ -3424,7 +3426,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3435,7 +3438,8 @@
.andIfSkipColorTransform(false)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3444,10 +3448,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(true)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3456,10 +3460,10 @@
verify().ifMixedCompositionIs(false)
.andIfUsesHdr(false)
.andIfSkipColorTransform(false)
- .thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
- kDefaultMaxLuminance, kDefaultOutputDataspace,
- kDefaultColorTransformMat,
- kDefaultOutputOrientationFlags})
+ .thenExpectDisplaySettingsUsed(
+ {kDefaultOutputDestinationClip, kDefaultOutputViewport, kDefaultMaxLuminance,
+ kDefaultOutputDataspace, kDefaultColorTransformMat,
+ kDefaultOutputOrientationFlags, kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3471,7 +3475,8 @@
.andIfSkipColorTransform(true)
.thenExpectDisplaySettingsUsed({kDefaultOutputDestinationClip, kDefaultOutputViewport,
kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
- kDefaultOutputOrientationFlags})
+ kDefaultOutputOrientationFlags,
+ kClientTargetLuminanceNits})
.execute()
.expectAFenceWasReturned();
}
@@ -3729,6 +3734,8 @@
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
}
+ static constexpr float kLayerWhitePointNits = 200.f;
+
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
StrictMock<OutputPartialMock> mOutput;
@@ -3768,6 +3775,7 @@
static constexpr ui::Rotation kDisplayOrientation = ui::ROTATION_0;
static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
+ static constexpr float kLayerWhitePointNits = 200.f;
static const Rect kDisplayFrame;
static const Rect kDisplayViewport;
@@ -3930,14 +3938,15 @@
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
- false, /* needs filtering */
- false, /* secure */
- false, /* supports protected content */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
kDisplayViewport,
kDisplayDataspace,
false /* realContentIsVisible */,
true /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -3949,6 +3958,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mBlackoutSettings = mLayers[1].mLayerSettings;
@@ -3988,6 +3998,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(Rect(0, 0, 30, 30)),
@@ -3999,6 +4010,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(Rect(0, 0, 40, 201)),
@@ -4010,6 +4022,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4039,6 +4052,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4050,6 +4064,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4061,6 +4076,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4090,7 +4106,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
-
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4102,6 +4118,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4113,6 +4130,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4141,6 +4159,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4152,6 +4171,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4163,6 +4183,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4179,7 +4200,6 @@
TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
protectedContentSupportUsedToGenerateRequests) {
-
compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
Region(kDisplayFrame),
false, /* needs filtering */
@@ -4190,6 +4210,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
Region(kDisplayFrame),
@@ -4201,6 +4222,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
Region(kDisplayFrame),
@@ -4212,6 +4234,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(*mLayers[0].mLayerFE, prepareClientCompositionList(Eq(ByRef(layer0TargetSettings))))
@@ -4379,6 +4402,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4396,6 +4420,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
@@ -4428,6 +4453,7 @@
false /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
LayerFE::LayerSettings mShadowSettings;
@@ -4472,6 +4498,7 @@
true /* realContentIsVisible */,
false /* clearContent */,
compositionengine::LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled,
+ kLayerWhitePointNits,
};
EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
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/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
index 42b3d97..d5a117a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp
@@ -55,13 +55,21 @@
}
MATCHER_P(ClientCompositionTargetSettingsSecureEq, expectedSecureSetting, "") {
- *result_listener << "ClientCompositionTargetSettings' SecureSettings aren't equal \n";
+ *result_listener << "ClientCompositionTargetSettings' isSecure bits aren't equal \n";
*result_listener << "expected " << expectedSecureSetting << "\n";
*result_listener << "actual " << arg.isSecure << "\n";
return expectedSecureSetting == arg.isSecure;
}
+MATCHER_P(ClientCompositionTargetSettingsWhitePointEq, expectedWhitePoint, "") {
+ *result_listener << "ClientCompositionTargetSettings' white points aren't equal \n";
+ *result_listener << "expected " << expectedWhitePoint << "\n";
+ *result_listener << "actual " << arg.whitePointNits << "\n";
+
+ return expectedWhitePoint == arg.whitePointNits;
+}
+
static const ui::Size kOutputSize = ui::Size(1, 1);
class CachedSetTest : public testing::Test {
@@ -431,6 +439,54 @@
cachedSet.append(CachedSet(layer3));
}
+TEST_F(CachedSetTest, renderWhitePoint) {
+ // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
+ CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE;
+ CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get();
+ sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE;
+
+ CachedSet cachedSet(layer1);
+ cachedSet.append(CachedSet(layer2));
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1;
+ clientCompList1.push_back({});
+
+ std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2;
+ clientCompList2.push_back({});
+
+ mOutputState.displayBrightnessNits = 400.f;
+
+ const auto drawLayers =
+ [&](const renderengine::DisplaySettings& displaySettings,
+ const std::vector<renderengine::LayerSettings>&,
+ const std::shared_ptr<renderengine::ExternalTexture>&, const bool,
+ base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> {
+ EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits);
+ return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()});
+ };
+
+ EXPECT_CALL(*layerFE1,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList1));
+ EXPECT_CALL(*layerFE2,
+ prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq(
+ mOutputState.displayBrightnessNits)))
+ .WillOnce(Return(clientCompList2));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers));
+ mOutputState.isSecure = true;
+ cachedSet.render(mRenderEngine, mTexturePool, mOutputState);
+ expectReadyBuffer(cachedSet);
+
+ EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace());
+ EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds());
+
+ // Now check that appending a new cached set properly cleans up RenderEngine resources.
+ CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get();
+ cachedSet.append(CachedSet(layer3));
+}
+
TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) {
// Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0)
CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get();
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
index 9b0a75f..35d051e 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp
@@ -224,6 +224,22 @@
mFlattener->renderCachedSets(mOutputState, std::nullopt);
}
+TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) {
+ mTestLayers[0]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold / 2;
+ mTestLayers[1]->layerFECompositionState.fps = Flattener::kFpsActiveThreshold;
+
+ auto& layerState1 = mTestLayers[0]->layerState;
+ auto& layerState2 = mTestLayers[1]->layerState;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ };
+
+ initializeFlattener(layers);
+ expectAllLayersFlattened(layers);
+}
+
TEST_F(FlattenerTest, flattenLayers_basicFlatten) {
auto& layerState1 = mTestLayers[0]->layerState;
auto& layerState2 = mTestLayers[1]->layerState;
@@ -746,6 +762,76 @@
EXPECT_EQ(nullptr, peekThroughLayer1);
}
+TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) {
+ mTestLayers[0]->outputLayerCompositionState.displayFrame = Rect(0, 0, 5, 5);
+ mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+ mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+
+ // An opaque static background
+ auto& layerState0 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer0 = layerState0->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // a rounded updating layer
+ auto& layerState1 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+
+ EXPECT_CALL(*mTestLayers[1]->layerFE, hasRoundedCorners()).WillRepeatedly(Return(true));
+
+ std::vector<LayerFE::LayerSettings> clientCompositionList = {
+ LayerFE::LayerSettings{},
+ };
+ clientCompositionList[0].source.buffer.buffer = std::make_shared<
+ renderengine::ExternalTexture>(mTestLayers[1]->layerFECompositionState.buffer,
+ mRenderEngine,
+ renderengine::ExternalTexture::Usage::READABLE);
+ EXPECT_CALL(*mTestLayers[1]->layerFE, prepareClientCompositionList(_))
+ .WillOnce(Return(clientCompositionList));
+
+ const std::vector<const LayerState*> layers = {
+ layerState0.get(),
+ layerState1.get(),
+ };
+
+ initializeFlattener(layers);
+
+ // layer 1 satisfies every condition in CachedSet::requiresHolePunch()
+ mTime += 200ms;
+ layerState1->resetFramesSinceBufferUpdate(); // it is updating
+
+ initializeOverrideBuffer(layers);
+ // Expect no cache invalidation the first time (there's no cache yet)
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet of layer 0. Though it is just one layer, it satisfies the
+ // exception that there would be a hole punch above it.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer0);
+
+ // This time we merge the CachedSet in and we should still have only two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_NE(nullptr, overrideBuffer0); // got overridden
+ EXPECT_EQ(nullptr, overrideBuffer1); // did not
+
+ // expect 0's peek though layer to be 1's output layer
+ const auto* peekThroughLayer0 =
+ layerState0->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ const auto* peekThroughLayer1 =
+ layerState1->getOutputLayer()->getState().overrideInfo.peekThroughLayer;
+ EXPECT_EQ(&mTestLayers[1]->outputLayer, peekThroughLayer0);
+ EXPECT_EQ(nullptr, peekThroughLayer1);
+}
+
TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) {
auto& layerState1 = mTestLayers[0]->layerState;
@@ -1191,5 +1277,61 @@
EXPECT_EQ(nullptr, overrideBuffer3);
}
+TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) {
+ auto& layerState1 = mTestLayers[0]->layerState;
+ const auto& overrideBuffer1 = layerState1->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState2 = mTestLayers[1]->layerState;
+ const auto& overrideBuffer2 = layerState2->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState3 = mTestLayers[2]->layerState;
+ const auto& overrideBuffer3 = layerState3->getOutputLayer()->getState().overrideInfo.buffer;
+ auto& layerState4 = mTestLayers[3]->layerState;
+ const auto& overrideBuffer4 = layerState4->getOutputLayer()->getState().overrideInfo.buffer;
+
+ // Rewrite the first two layers to just be a solid color.
+ mTestLayers[0]->layerFECompositionState.color = half4(255.f, 0.f, 0.f, 255.f);
+ mTestLayers[0]->layerFECompositionState.buffer = nullptr;
+ mTestLayers[1]->layerFECompositionState.color = half4(0.f, 255.f, 0.f, 255.f);
+ mTestLayers[1]->layerFECompositionState.buffer = nullptr;
+
+ const std::vector<const LayerState*> layers = {
+ layerState1.get(),
+ layerState2.get(),
+ layerState3.get(),
+ layerState4.get(),
+ };
+
+ initializeFlattener(layers);
+
+ mTime += 200ms;
+ initializeOverrideBuffer(layers);
+ EXPECT_EQ(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+
+ // This will render a CachedSet.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _))
+ .WillOnce(Return(ByMove(
+ futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}))));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ // We've rendered a CachedSet, but we haven't merged it in.
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(nullptr, overrideBuffer3);
+ EXPECT_EQ(nullptr, overrideBuffer4);
+
+ // This time we merge the CachedSet in, so we have a new hash, and we should
+ // only have two sets.
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0);
+ initializeOverrideBuffer(layers);
+ EXPECT_NE(getNonBufferHash(layers),
+ mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime));
+ mFlattener->renderCachedSets(mOutputState, std::nullopt);
+
+ EXPECT_EQ(nullptr, overrideBuffer1);
+ EXPECT_EQ(nullptr, overrideBuffer2);
+ EXPECT_EQ(overrideBuffer3, overrideBuffer4);
+ EXPECT_NE(nullptr, overrideBuffer4);
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
index 9ad3ab4..84b3fc5 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/LayerStateTest.cpp
@@ -27,6 +27,10 @@
#include "android/hardware_buffer.h"
#include "compositionengine/LayerFECompositionState.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine::impl::planner {
namespace {
@@ -277,33 +281,28 @@
TEST_F(LayerStateTest, getCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::DEVICE,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::DEVICE, mLayerState->getCompositionType());
}
TEST_F(LayerStateTest, getCompositionType_forcedClient) {
OutputLayerCompositionState outputLayerCompositionState;
outputLayerCompositionState.forceClientComposition = true;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::CLIENT,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::CLIENT, mLayerState->getCompositionType());
}
TEST_F(LayerStateTest, updateCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
@@ -311,29 +310,25 @@
mock::OutputLayer newOutputLayer;
mock::LayerFE newLayerFE;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType =
- hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
layerFECompositionStateTwo);
Flags<LayerStateField> updates = mLayerState->update(&newOutputLayer);
- EXPECT_EQ(hardware::graphics::composer::hal::Composition::SOLID_COLOR,
- mLayerState->getCompositionType());
+ EXPECT_EQ(Composition::SOLID_COLOR, mLayerState->getCompositionType());
EXPECT_EQ(Flags<LayerStateField>(LayerStateField::CompositionType), updates);
}
TEST_F(LayerStateTest, compareCompositionType) {
OutputLayerCompositionState outputLayerCompositionState;
LayerFECompositionState layerFECompositionState;
- layerFECompositionState.compositionType =
- hardware::graphics::composer::hal::Composition::DEVICE;
+ layerFECompositionState.compositionType = Composition::DEVICE;
setupMocksForLayer(mOutputLayer, mLayerFE, outputLayerCompositionState,
layerFECompositionState);
mLayerState = std::make_unique<LayerState>(&mOutputLayer);
mock::OutputLayer newOutputLayer;
mock::LayerFE newLayerFE;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType =
- hardware::graphics::composer::hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(newOutputLayer, newLayerFE, outputLayerCompositionState,
layerFECompositionStateTwo);
auto otherLayerState = std::make_unique<LayerState>(&newOutputLayer);
diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
index 1492707..6038268 100644
--- a/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/planner/PredictorTest.cpp
@@ -24,6 +24,10 @@
#include <gtest/gtest.h>
#include <log/log.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android::compositionengine::impl::planner {
namespace {
@@ -103,7 +107,7 @@
mock::LayerFE layerFEOne;
OutputLayerCompositionState outputLayerCompositionStateOne;
LayerFECompositionState layerFECompositionStateOne;
- layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ layerFECompositionStateOne.compositionType = Composition::DEVICE;
setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
layerFECompositionStateOne);
LayerState layerStateOne(&outputLayerOne);
@@ -112,7 +116,7 @@
mock::LayerFE layerFETwo;
OutputLayerCompositionState outputLayerCompositionStateTwo;
LayerFECompositionState layerFECompositionStateTwo;
- layerFECompositionStateTwo.compositionType = hal::Composition::SOLID_COLOR;
+ layerFECompositionStateTwo.compositionType = Composition::SOLID_COLOR;
setupMocksForLayer(outputLayerTwo, layerFETwo, outputLayerCompositionStateTwo,
layerFECompositionStateTwo);
LayerState layerStateTwo(&outputLayerTwo);
@@ -370,7 +374,7 @@
TEST_F(PredictionTest, constructPrediction) {
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Prediction prediction({}, plan);
@@ -442,13 +446,13 @@
mock::LayerFE layerFEOne;
OutputLayerCompositionState outputLayerCompositionStateOne;
LayerFECompositionState layerFECompositionStateOne;
- layerFECompositionStateOne.compositionType = hal::Composition::DEVICE;
+ layerFECompositionStateOne.compositionType = Composition::DEVICE;
setupMocksForLayer(outputLayerOne, layerFEOne, outputLayerCompositionStateOne,
layerFECompositionStateOne);
LayerState layerStateOne(&outputLayerOne);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -484,7 +488,7 @@
LayerState layerStateTwo(&outputLayerTwo);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -521,7 +525,7 @@
LayerState layerStateTwo(&outputLayerTwo);
Plan plan;
- plan.addLayerType(hal::Composition::DEVICE);
+ plan.addLayerType(Composition::DEVICE);
Predictor predictor;
@@ -535,7 +539,7 @@
EXPECT_EQ(Prediction::Type::Approximate, predictedPlan->type);
Plan planTwo;
- planTwo.addLayerType(hal::Composition::CLIENT);
+ planTwo.addLayerType(Composition::CLIENT);
predictor.recordResult(predictedPlan, hashTwo, {&layerStateTwo}, false, planTwo);
// Now trying to retrieve the predicted plan again returns a nullopt instead.
// TODO(b/158790260): Even though this is enforced in this test, we might want to reassess this.
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 841e79f..3ccc229 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -36,8 +36,7 @@
sp<Layer> ContainerLayer::createClone() {
sp<ContainerLayer> layer = mFlinger->getFactory().createContainerLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
- LayerMetadata()));
+ LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
layer->setInitialValuesForClone(this);
return layer;
}
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/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
index ba57be5..d97a399 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp
@@ -41,6 +41,8 @@
using aidl::android::hardware::graphics::composer3::PowerMode;
using aidl::android::hardware::graphics::composer3::VirtualDisplay;
+using aidl::android::hardware::graphics::composer3::CommandResultPayload;
+
using AidlColorMode = aidl::android::hardware::graphics::composer3::ColorMode;
using AidlContentType = aidl::android::hardware::graphics::composer3::ContentType;
using AidlDisplayIdentification =
@@ -48,8 +50,6 @@
using AidlDisplayContentSample = aidl::android::hardware::graphics::composer3::DisplayContentSample;
using AidlDisplayAttribute = aidl::android::hardware::graphics::composer3::DisplayAttribute;
using AidlDisplayCapability = aidl::android::hardware::graphics::composer3::DisplayCapability;
-using AidlExecuteCommandsStatus =
- aidl::android::hardware::graphics::composer3::ExecuteCommandsStatus;
using AidlHdrCapabilities = aidl::android::hardware::graphics::composer3::HdrCapabilities;
using AidlPerFrameMetadata = aidl::android::hardware::graphics::composer3::PerFrameMetadata;
using AidlPerFrameMetadataKey = aidl::android::hardware::graphics::composer3::PerFrameMetadataKey;
@@ -59,14 +59,11 @@
aidl::android::hardware::graphics::composer3::VsyncPeriodChangeConstraints;
using AidlVsyncPeriodChangeTimeline =
aidl::android::hardware::graphics::composer3::VsyncPeriodChangeTimeline;
-using AidlLayerGenericMetadataKey =
- aidl::android::hardware::graphics::composer3::LayerGenericMetadataKey;
using AidlDisplayContentSamplingAttributes =
aidl::android::hardware::graphics::composer3::DisplayContentSamplingAttributes;
using AidlFormatColorComponent = aidl::android::hardware::graphics::composer3::FormatColorComponent;
using AidlDisplayConnectionType =
aidl::android::hardware::graphics::composer3::DisplayConnectionType;
-using AidlIComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient;
using AidlColorTransform = aidl::android::hardware::graphics::common::ColorTransform;
using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
@@ -167,10 +164,10 @@
}
template <>
-IComposerClient::LayerGenericMetadataKey translate(AidlLayerGenericMetadataKey x) {
- return IComposerClient::LayerGenericMetadataKey{
- .name = x.name,
- .mandatory = x.mandatory,
+IComposerClient::ClientTargetProperty translate(ClientTargetProperty x) {
+ return IComposerClient::ClientTargetProperty{
+ .pixelFormat = translate<PixelFormat>(x.pixelFormat),
+ .dataspace = translate<Dataspace>(x.dataspace),
};
}
@@ -226,7 +223,7 @@
return AServiceManager_isDeclared(instance(serviceName).c_str());
}
-AidlComposer::AidlComposer(const std::string& serviceName) : mWriter(kWriterInitialSize) {
+AidlComposer::AidlComposer(const std::string& serviceName) {
// This only waits if the service is actually declared
mAidlComposer = AidlIComposer::fromBinder(
ndk::SpAIBinder(AServiceManager_waitForService(instance(serviceName).c_str())));
@@ -245,6 +242,14 @@
AidlComposer::~AidlComposer() = default;
+bool AidlComposer::isSupported(OptionalFeature feature) const {
+ switch (feature) {
+ case OptionalFeature::RefreshRateSwitching:
+ case OptionalFeature::ExpectedPresentTime:
+ return true;
+ }
+}
+
std::vector<IComposer::Capability> AidlComposer::getCapabilities() {
std::vector<Capability> capabilities;
const auto status = mAidlComposer->getCapabilities(&capabilities);
@@ -327,8 +332,7 @@
}
Error AidlComposer::acceptDisplayChanges(Display display) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.acceptDisplayChanges();
+ mWriter.acceptDisplayChanges(translate<int64_t>(display));
return Error::NONE;
}
@@ -368,8 +372,15 @@
Error AidlComposer::getChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) {
- mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
+ const auto changedLayers = mReader.takeChangedCompositionTypes(translate<int64_t>(display));
+ outLayers->reserve(changedLayers.size());
+ outTypes->reserve(changedLayers.size());
+
+ for (const auto& layer : changedLayers) {
+ outLayers->emplace_back(translate<Layer>(layer.layer));
+ outTypes->emplace_back(layer.composition);
+ }
return Error::NONE;
}
@@ -422,17 +433,28 @@
Error AidlComposer::getDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
std::vector<Layer>* outLayers,
std::vector<uint32_t>* outLayerRequestMasks) {
- mReader.takeDisplayRequests(display, outDisplayRequestMask, outLayers, outLayerRequestMasks);
+ const auto displayRequests = mReader.takeDisplayRequests(translate<int64_t>(display));
+ *outDisplayRequestMask = translate<uint32_t>(displayRequests.mask);
+ outLayers->reserve(displayRequests.layerRequests.size());
+ outLayerRequestMasks->reserve(displayRequests.layerRequests.size());
+
+ for (const auto& layer : displayRequests.layerRequests) {
+ outLayers->emplace_back(translate<Layer>(layer.layer));
+ outLayerRequestMasks->emplace_back(translate<uint32_t>(layer.mask));
+ }
return Error::NONE;
}
Error AidlComposer::getDozeSupport(Display display, bool* outSupport) {
+ std::vector<AidlDisplayCapability> capabilities;
const auto status =
- mAidlComposerClient->getDozeSupport(translate<int64_t>(display), outSupport);
+ mAidlComposerClient->getDisplayCapabilities(translate<int64_t>(display), &capabilities);
if (!status.isOk()) {
- ALOGE("getDozeSupport failed %s", status.getDescription().c_str());
+ ALOGE("getDisplayCapabilities failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
}
+ *outSupport = std::find(capabilities.begin(), capabilities.end(),
+ AidlDisplayCapability::DOZE) != capabilities.end();
return Error::NONE;
}
@@ -456,22 +478,33 @@
Error AidlComposer::getReleaseFences(Display display, std::vector<Layer>* outLayers,
std::vector<int>* outReleaseFences) {
- mReader.takeReleaseFences(display, outLayers, outReleaseFences);
+ auto fences = mReader.takeReleaseFences(translate<int64_t>(display));
+ outLayers->reserve(fences.size());
+ outReleaseFences->reserve(fences.size());
+
+ for (auto& fence : fences) {
+ outLayers->emplace_back(translate<Layer>(fence.layer));
+ // take ownership
+ const int fenceOwner = fence.fence.get();
+ *fence.fence.getR() = -1;
+ outReleaseFences->emplace_back(fenceOwner);
+ }
return Error::NONE;
}
Error AidlComposer::presentDisplay(Display display, int* outPresentFence) {
ATRACE_NAME("HwcPresentDisplay");
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.presentDisplay();
+ mWriter.presentDisplay(translate<int64_t>(display));
Error error = execute();
if (error != Error::NONE) {
return error;
}
- mReader.takePresentFence(display, outPresentFence);
-
+ auto fence = mReader.takePresentFence(translate<int64_t>(display));
+ // take ownership
+ *outPresentFence = fence.get();
+ *fence.getR() = -1;
return Error::NONE;
}
@@ -488,14 +521,12 @@
Error AidlComposer::setClientTarget(Display display, uint32_t slot, const sp<GraphicBuffer>& target,
int acquireFence, Dataspace dataspace,
const std::vector<IComposerClient::Rect>& damage) {
- mWriter.selectDisplay(translate<int64_t>(display));
-
const native_handle_t* handle = nullptr;
if (target.get()) {
handle = target->getNativeBuffer()->handle;
}
- mWriter.setClientTarget(slot, handle, acquireFence,
+ mWriter.setClientTarget(translate<int64_t>(display), slot, handle, acquireFence,
translate<aidl::android::hardware::graphics::common::Dataspace>(
dataspace),
translate<AidlRect>(damage));
@@ -514,16 +545,14 @@
return Error::NONE;
}
-Error AidlComposer::setColorTransform(Display display, const float* matrix, ColorTransform hint) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.setColorTransform(matrix, translate<AidlColorTransform>(hint));
+Error AidlComposer::setColorTransform(Display display, const float* matrix) {
+ mWriter.setColorTransform(translate<int64_t>(display), matrix);
return Error::NONE;
}
Error AidlComposer::setOutputBuffer(Display display, const native_handle_t* buffer,
int releaseFence) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.setOutputBuffer(0, buffer, dup(releaseFence));
+ mWriter.setOutputBuffer(translate<int64_t>(display), 0, buffer, dup(releaseFence));
return Error::NONE;
}
@@ -559,228 +588,182 @@
return Error::NONE;
}
-Error AidlComposer::validateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests) {
+Error AidlComposer::validateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) {
ATRACE_NAME("HwcValidateDisplay");
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.validateDisplay();
+ mWriter.validateDisplay(translate<int64_t>(display),
+ ClockMonotonicTimestamp{expectedPresentTime});
Error error = execute();
if (error != Error::NONE) {
return error;
}
- mReader.hasChanges(display, outNumTypes, outNumRequests);
+ mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
return Error::NONE;
}
-Error AidlComposer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests, int* outPresentFence,
- uint32_t* state) {
+Error AidlComposer::presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ int* outPresentFence, uint32_t* state) {
ATRACE_NAME("HwcPresentOrValidateDisplay");
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.presentOrvalidateDisplay();
+ mWriter.presentOrvalidateDisplay(translate<int64_t>(display),
+ ClockMonotonicTimestamp{expectedPresentTime});
Error error = execute();
if (error != Error::NONE) {
return error;
}
- mReader.takePresentOrValidateStage(display, state);
-
- if (*state == 1) { // Present succeeded
- mReader.takePresentFence(display, outPresentFence);
+ const auto result = mReader.takePresentOrValidateStage(translate<int64_t>(display));
+ if (!result.has_value()) {
+ *state = translate<uint32_t>(-1);
+ return Error::NO_RESOURCES;
}
- if (*state == 0) { // Validate succeeded.
- mReader.hasChanges(display, outNumTypes, outNumRequests);
+ *state = translate<uint32_t>(*result);
+
+ if (*result == PresentOrValidate::Result::Presented) {
+ auto fence = mReader.takePresentFence(translate<int64_t>(display));
+ // take ownership
+ *outPresentFence = fence.get();
+ *fence.getR() = -1;
+ }
+
+ if (*result == PresentOrValidate::Result::Validated) {
+ mReader.hasChanges(translate<int64_t>(display), outNumTypes, outNumRequests);
}
return Error::NONE;
}
Error AidlComposer::setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerCursorPosition(x, y);
+ mWriter.setLayerCursorPosition(translate<int64_t>(display), translate<int64_t>(layer), x, y);
return Error::NONE;
}
Error AidlComposer::setLayerBuffer(Display display, Layer layer, uint32_t slot,
const sp<GraphicBuffer>& buffer, int acquireFence) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
-
const native_handle_t* handle = nullptr;
if (buffer.get()) {
handle = buffer->getNativeBuffer()->handle;
}
- mWriter.setLayerBuffer(slot, handle, acquireFence);
+ mWriter.setLayerBuffer(translate<int64_t>(display), translate<int64_t>(layer), slot, handle,
+ acquireFence);
return Error::NONE;
}
Error AidlComposer::setLayerSurfaceDamage(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& damage) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerSurfaceDamage(translate<AidlRect>(damage));
+ mWriter.setLayerSurfaceDamage(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(damage));
return Error::NONE;
}
Error AidlComposer::setLayerBlendMode(Display display, Layer layer,
IComposerClient::BlendMode mode) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerBlendMode(translate<BlendMode>(mode));
+ mWriter.setLayerBlendMode(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<BlendMode>(mode));
return Error::NONE;
}
Error AidlComposer::setLayerColor(Display display, Layer layer,
const IComposerClient::Color& color) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerColor(translate<Color>(color));
+ mWriter.setLayerColor(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<Color>(color));
return Error::NONE;
}
-Error AidlComposer::setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerCompositionType(translate<Composition>(type));
+Error AidlComposer::setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ mWriter.setLayerCompositionType(translate<int64_t>(display), translate<int64_t>(layer), type);
return Error::NONE;
}
Error AidlComposer::setLayerDataspace(Display display, Layer layer, Dataspace dataspace) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerDataspace(translate<AidlDataspace>(dataspace));
+ mWriter.setLayerDataspace(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlDataspace>(dataspace));
return Error::NONE;
}
Error AidlComposer::setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerDisplayFrame(translate<AidlRect>(frame));
+ mWriter.setLayerDisplayFrame(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(frame));
return Error::NONE;
}
Error AidlComposer::setLayerPlaneAlpha(Display display, Layer layer, float alpha) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerPlaneAlpha(alpha);
+ mWriter.setLayerPlaneAlpha(translate<int64_t>(display), translate<int64_t>(layer), alpha);
return Error::NONE;
}
Error AidlComposer::setLayerSidebandStream(Display display, Layer layer,
const native_handle_t* stream) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerSidebandStream(stream);
+ mWriter.setLayerSidebandStream(translate<int64_t>(display), translate<int64_t>(layer), stream);
return Error::NONE;
}
Error AidlComposer::setLayerSourceCrop(Display display, Layer layer,
const IComposerClient::FRect& crop) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerSourceCrop(translate<AidlFRect>(crop));
+ mWriter.setLayerSourceCrop(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlFRect>(crop));
return Error::NONE;
}
Error AidlComposer::setLayerTransform(Display display, Layer layer, Transform transform) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerTransform(translate<AidlTransform>(transform));
+ mWriter.setLayerTransform(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlTransform>(transform));
return Error::NONE;
}
Error AidlComposer::setLayerVisibleRegion(Display display, Layer layer,
const std::vector<IComposerClient::Rect>& visible) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerVisibleRegion(translate<AidlRect>(visible));
+ mWriter.setLayerVisibleRegion(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlRect>(visible));
return Error::NONE;
}
Error AidlComposer::setLayerZOrder(Display display, Layer layer, uint32_t z) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerZOrder(z);
+ mWriter.setLayerZOrder(translate<int64_t>(display), translate<int64_t>(layer), z);
return Error::NONE;
}
Error AidlComposer::execute() {
- // prepare input command queue
- bool queueChanged = false;
- int32_t commandLength = 0;
- std::vector<aidl::android::hardware::common::NativeHandle> commandHandles;
- if (!mWriter.writeQueue(&queueChanged, &commandLength, &commandHandles)) {
- mWriter.reset();
- return Error::NO_RESOURCES;
- }
-
- // set up new input command queue if necessary
- if (queueChanged) {
- const auto status = mAidlComposerClient->setInputCommandQueue(mWriter.getMQDescriptor());
- if (!status.isOk()) {
- ALOGE("setInputCommandQueue failed %s", status.getDescription().c_str());
- mWriter.reset();
- return static_cast<Error>(status.getServiceSpecificError());
- }
- }
-
- if (commandLength == 0) {
+ const auto& commands = mWriter.getPendingCommands();
+ if (commands.empty()) {
mWriter.reset();
return Error::NONE;
}
- AidlExecuteCommandsStatus commandStatus;
- auto status =
- mAidlComposerClient->executeCommands(commandLength, commandHandles, &commandStatus);
- // executeCommands can fail because of out-of-fd and we do not want to
- // abort() in that case
- if (!status.isOk()) {
- ALOGE("executeCommands failed %s", status.getDescription().c_str());
- return static_cast<Error>(status.getServiceSpecificError());
- }
-
- // set up new output command queue if necessary
- if (commandStatus.queueChanged) {
- ::aidl::android::hardware::common::fmq::MQDescriptor<
- int32_t, ::aidl::android::hardware::common::fmq::SynchronizedReadWrite>
- outputCommandQueue;
- status = mAidlComposerClient->getOutputCommandQueue(&outputCommandQueue);
+ { // scope for results
+ std::vector<CommandResultPayload> results;
+ auto status = mAidlComposerClient->executeCommands(commands, &results);
if (!status.isOk()) {
- ALOGE("getOutputCommandQueue failed %s", status.getDescription().c_str());
+ ALOGE("executeCommands failed %s", status.getDescription().c_str());
return static_cast<Error>(status.getServiceSpecificError());
}
- mReader.setMQDescriptor(outputCommandQueue);
+ mReader.parse(std::move(results));
}
+ const auto commandErrors = mReader.takeErrors();
+ Error error = Error::NONE;
+ for (const auto& cmdErr : commandErrors) {
+ const auto index = static_cast<size_t>(cmdErr.commandIndex);
+ if (index < 0 || index >= commands.size()) {
+ ALOGE("invalid command index %zu", index);
+ return Error::BAD_PARAMETER;
+ }
- Error error;
- if (mReader.readQueue(commandStatus.length, std::move(commandStatus.handles))) {
- error = static_cast<Error>(mReader.parse());
- mReader.reset();
- } else {
- error = Error::NO_RESOURCES;
- }
-
- if (error == Error::NONE) {
- std::vector<AidlCommandReader::CommandError> commandErrors = mReader.takeErrors();
-
- for (const auto& cmdErr : commandErrors) {
- auto command = mWriter.getCommand(cmdErr.location);
- if (command == Command::VALIDATE_DISPLAY || command == Command::PRESENT_DISPLAY ||
- command == Command::PRESENT_OR_VALIDATE_DISPLAY) {
- error = cmdErr.error;
- } else {
- ALOGW("command 0x%x generated error %d", command, cmdErr.error);
- }
+ const auto& command = commands[index];
+ if (command.validateDisplay || command.presentDisplay || command.presentOrValidateDisplay) {
+ error = translate<Error>(cmdErr.errorCode);
+ } else {
+ ALOGW("command '%s' generated error %" PRId32, command.toString().c_str(),
+ cmdErr.errorCode);
}
}
@@ -792,9 +775,8 @@
Error AidlComposer::setLayerPerFrameMetadata(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadata>& perFrameMetadatas) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerPerFrameMetadata(translate<AidlPerFrameMetadata>(perFrameMetadatas));
+ mWriter.setLayerPerFrameMetadata(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlPerFrameMetadata>(perFrameMetadatas));
return Error::NONE;
}
@@ -855,9 +837,7 @@
}
Error AidlComposer::setLayerColorTransform(Display display, Layer layer, const float* matrix) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerColorTransform(matrix);
+ mWriter.setLayerColorTransform(translate<int64_t>(display), translate<int64_t>(layer), matrix);
return Error::NONE;
}
@@ -921,9 +901,8 @@
Error AidlComposer::setLayerPerFrameMetadataBlobs(
Display display, Layer layer,
const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerPerFrameMetadataBlobs(translate<AidlPerFrameMetadataBlob>(metadata));
+ mWriter.setLayerPerFrameMetadataBlobs(translate<int64_t>(display), translate<int64_t>(layer),
+ translate<AidlPerFrameMetadataBlob>(metadata));
return Error::NONE;
}
@@ -1029,342 +1008,32 @@
return V2_4::Error::NONE;
}
-V2_4::Error AidlComposer::setLayerGenericMetadata(Display display, Layer layer,
- const std::string& key, bool mandatory,
- const std::vector<uint8_t>& value) {
- mWriter.selectDisplay(translate<int64_t>(display));
- mWriter.selectLayer(translate<int64_t>(layer));
- mWriter.setLayerGenericMetadata(key, mandatory, value);
- return V2_4::Error::NONE;
+V2_4::Error AidlComposer::setLayerGenericMetadata(Display, Layer, const std::string&, bool,
+ const std::vector<uint8_t>&) {
+ // There are no users for this API. See b/209691612.
+ return V2_4::Error::UNSUPPORTED;
}
V2_4::Error AidlComposer::getLayerGenericMetadataKeys(
- std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) {
- std::vector<AidlLayerGenericMetadataKey> keys;
- const auto status = mAidlComposerClient->getLayerGenericMetadataKeys(&keys);
- if (!status.isOk()) {
- ALOGE("getLayerGenericMetadataKeys failed %s", status.getDescription().c_str());
- return static_cast<V2_4::Error>(status.getServiceSpecificError());
- }
- *outKeys = translate<IComposerClient::LayerGenericMetadataKey>(keys);
- return V2_4::Error::NONE;
+ std::vector<IComposerClient::LayerGenericMetadataKey>*) {
+ // There are no users for this API. See b/209691612.
+ return V2_4::Error::UNSUPPORTED;
}
Error AidlComposer::getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
- mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* whitePointNits) {
+ const auto property = mReader.takeClientTargetProperty(translate<int64_t>(display));
+ *outClientTargetProperty =
+ translate<IComposerClient::ClientTargetProperty>(property.clientTargetProperty);
+ *whitePointNits = property.whitePointNits;
return Error::NONE;
}
-AidlCommandReader::~AidlCommandReader() {
- resetData();
-}
-
-int AidlCommandReader::parse() {
- resetData();
-
- Command command;
- uint16_t length = 0;
-
- while (!isEmpty()) {
- if (!beginCommand(&command, &length)) {
- break;
- }
-
- bool parsed = false;
- switch (command) {
- case Command::SELECT_DISPLAY:
- parsed = parseSelectDisplay(length);
- break;
- case Command::SET_ERROR:
- parsed = parseSetError(length);
- break;
- case Command::SET_CHANGED_COMPOSITION_TYPES:
- parsed = parseSetChangedCompositionTypes(length);
- break;
- case Command::SET_DISPLAY_REQUESTS:
- parsed = parseSetDisplayRequests(length);
- break;
- case Command::SET_PRESENT_FENCE:
- parsed = parseSetPresentFence(length);
- break;
- case Command::SET_RELEASE_FENCES:
- parsed = parseSetReleaseFences(length);
- break;
- case Command ::SET_PRESENT_OR_VALIDATE_DISPLAY_RESULT:
- parsed = parseSetPresentOrValidateDisplayResult(length);
- break;
- case Command::SET_CLIENT_TARGET_PROPERTY:
- parsed = parseSetClientTargetProperty(length);
- break;
- default:
- parsed = false;
- break;
- }
-
- endCommand();
-
- if (!parsed) {
- ALOGE("failed to parse command 0x%x length %" PRIu16, command, length);
- break;
- }
- }
-
- return isEmpty() ? 0 : AidlIComposerClient::EX_NO_RESOURCES;
-}
-
-bool AidlCommandReader::parseSelectDisplay(uint16_t length) {
- if (length != CommandWriterBase::kSelectDisplayLength) {
- return false;
- }
-
- mCurrentReturnData = &mReturnData[read64()];
-
- return true;
-}
-
-bool AidlCommandReader::parseSetError(uint16_t length) {
- if (length != CommandWriterBase::kSetErrorLength) {
- return false;
- }
-
- auto location = read();
- auto error = static_cast<Error>(readSigned());
-
- mErrors.emplace_back(CommandError{location, error});
-
- return true;
-}
-
-bool AidlCommandReader::parseSetChangedCompositionTypes(uint16_t length) {
- // (layer id [64bit], composition type [32bit]) pairs
- static constexpr int kCommandWords = 3;
-
- if (length % kCommandWords != 0 || !mCurrentReturnData) {
- return false;
- }
-
- uint32_t count = length / kCommandWords;
- mCurrentReturnData->changedLayers.reserve(count);
- mCurrentReturnData->compositionTypes.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto type = static_cast<IComposerClient::Composition>(readSigned());
-
- mCurrentReturnData->changedLayers.push_back(layer);
- mCurrentReturnData->compositionTypes.push_back(type);
-
- count--;
- }
-
- return true;
-}
-
-bool AidlCommandReader::parseSetDisplayRequests(uint16_t length) {
- // display requests [32 bit] followed by
- // (layer id [64bit], layer requests [32bit]) pairs
- static constexpr int kDisplayRequestsWords = 1;
- static constexpr int kCommandWords = 3;
- if (length % kCommandWords != kDisplayRequestsWords || !mCurrentReturnData) {
- return false;
- }
-
- mCurrentReturnData->displayRequests = read();
-
- uint32_t count = (length - kDisplayRequestsWords) / kCommandWords;
- mCurrentReturnData->requestedLayers.reserve(count);
- mCurrentReturnData->requestMasks.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto layerRequestMask = read();
-
- mCurrentReturnData->requestedLayers.push_back(layer);
- mCurrentReturnData->requestMasks.push_back(layerRequestMask);
-
- count--;
- }
-
- return true;
-}
-
-bool AidlCommandReader::parseSetPresentFence(uint16_t length) {
- if (length != CommandWriterBase::kSetPresentFenceLength || !mCurrentReturnData) {
- return false;
- }
-
- if (mCurrentReturnData->presentFence >= 0) {
- close(mCurrentReturnData->presentFence);
- }
- mCurrentReturnData->presentFence = readFence();
-
- return true;
-}
-
-bool AidlCommandReader::parseSetReleaseFences(uint16_t length) {
- // (layer id [64bit], release fence index [32bit]) pairs
- static constexpr int kCommandWords = 3;
-
- if (length % kCommandWords != 0 || !mCurrentReturnData) {
- return false;
- }
-
- uint32_t count = length / kCommandWords;
- mCurrentReturnData->releasedLayers.reserve(count);
- mCurrentReturnData->releaseFences.reserve(count);
- while (count > 0) {
- auto layer = read64();
- auto fence = readFence();
-
- mCurrentReturnData->releasedLayers.push_back(layer);
- mCurrentReturnData->releaseFences.push_back(fence);
-
- count--;
- }
-
- return true;
-}
-
-bool AidlCommandReader::parseSetPresentOrValidateDisplayResult(uint16_t length) {
- if (length != CommandWriterBase::kPresentOrValidateDisplayResultLength || !mCurrentReturnData) {
- return false;
- }
- mCurrentReturnData->presentOrValidateState = read();
- return true;
-}
-
-bool AidlCommandReader::parseSetClientTargetProperty(uint16_t length) {
- if (length != CommandWriterBase::kSetClientTargetPropertyLength || !mCurrentReturnData) {
- return false;
- }
- mCurrentReturnData->clientTargetProperty.pixelFormat = static_cast<PixelFormat>(readSigned());
- mCurrentReturnData->clientTargetProperty.dataspace = static_cast<Dataspace>(readSigned());
- return true;
-}
-
-void AidlCommandReader::resetData() {
- mErrors.clear();
-
- for (auto& data : mReturnData) {
- if (data.second.presentFence >= 0) {
- close(data.second.presentFence);
- }
- for (auto fence : data.second.releaseFences) {
- if (fence >= 0) {
- close(fence);
- }
- }
- }
-
- mReturnData.clear();
- mCurrentReturnData = nullptr;
-}
-
-std::vector<AidlCommandReader::CommandError> AidlCommandReader::takeErrors() {
- return std::move(mErrors);
-}
-
-bool AidlCommandReader::hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
- uint32_t* outNumLayerRequestMasks) const {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outNumChangedCompositionTypes = 0;
- *outNumLayerRequestMasks = 0;
- return false;
- }
-
- const ReturnData& data = found->second;
-
- *outNumChangedCompositionTypes = static_cast<uint32_t>(data.compositionTypes.size());
- *outNumLayerRequestMasks = static_cast<uint32_t>(data.requestMasks.size());
-
- return !(data.compositionTypes.empty() && data.requestMasks.empty());
-}
-
-void AidlCommandReader::takeChangedCompositionTypes(
- Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- outLayers->clear();
- outTypes->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outLayers = std::move(data.changedLayers);
- *outTypes = std::move(data.compositionTypes);
-}
-
-void AidlCommandReader::takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
- std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outDisplayRequestMask = 0;
- outLayers->clear();
- outLayerRequestMasks->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outDisplayRequestMask = data.displayRequests;
- *outLayers = std::move(data.requestedLayers);
- *outLayerRequestMasks = std::move(data.requestMasks);
-}
-
-void AidlCommandReader::takeReleaseFences(Display display, std::vector<Layer>* outLayers,
- std::vector<int>* outReleaseFences) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- outLayers->clear();
- outReleaseFences->clear();
- return;
- }
-
- ReturnData& data = found->second;
-
- *outLayers = std::move(data.releasedLayers);
- *outReleaseFences = std::move(data.releaseFences);
-}
-
-void AidlCommandReader::takePresentFence(Display display, int* outPresentFence) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *outPresentFence = -1;
- return;
- }
-
- ReturnData& data = found->second;
-
- *outPresentFence = data.presentFence;
- data.presentFence = -1;
-}
-
-void AidlCommandReader::takePresentOrValidateStage(Display display, uint32_t* state) {
- auto found = mReturnData.find(display);
- if (found == mReturnData.end()) {
- *state = static_cast<uint32_t>(-1);
- return;
- }
- ReturnData& data = found->second;
- *state = data.presentOrValidateState;
-}
-
-void AidlCommandReader::takeClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
- auto found = mReturnData.find(display);
-
- // If not found, return the default values.
- if (found == mReturnData.end()) {
- outClientTargetProperty->pixelFormat = PixelFormat::RGBA_8888;
- outClientTargetProperty->dataspace = Dataspace::UNKNOWN;
- return;
- }
-
- ReturnData& data = found->second;
- *outClientTargetProperty = data.clientTargetProperty;
+Error AidlComposer::setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) {
+ mWriter.setLayerWhitePointNits(translate<int64_t>(display), translate<int64_t>(layer),
+ whitePointNits);
+ return Error::NONE;
}
} // namespace Hwc2
diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
index a6d9500..777b295 100644
--- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.h
@@ -34,104 +34,21 @@
#include <aidl/android/hardware/graphics/composer3/IComposer.h>
#include <aidl/android/hardware/graphics/composer3/IComposerClient.h>
-#include <android/hardware/graphics/composer3/command-buffer.h>
+#include <android/hardware/graphics/composer3/ComposerClientReader.h>
+#include <android/hardware/graphics/composer3/ComposerClientWriter.h>
+
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
namespace android::Hwc2 {
-using AidlCommandWriterBase = aidl::android::hardware::graphics::composer3::CommandWriterBase;
-using AidlCommandReaderBase = aidl::android::hardware::graphics::composer3::CommandReaderBase;
+using aidl::android::hardware::graphics::composer3::ComposerClientReader;
+using aidl::android::hardware::graphics::composer3::ComposerClientWriter;
class AidlIComposerCallbackWrapper;
-class AidlCommandReader : public AidlCommandReaderBase {
-public:
- ~AidlCommandReader();
-
- // Parse and execute commands from the command queue. The commands are
- // actually return values from the server and will be saved in ReturnData.
- int parse();
-
- // Get and clear saved errors.
- struct CommandError {
- uint32_t location;
- Error error;
- };
- std::vector<CommandError> takeErrors();
-
- bool hasChanges(Display display, uint32_t* outNumChangedCompositionTypes,
- uint32_t* outNumLayerRequestMasks) const;
-
- // Get and clear saved changed composition types.
- void takeChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes);
-
- // Get and clear saved display requests.
- void takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
- std::vector<Layer>* outLayers,
- std::vector<uint32_t>* outLayerRequestMasks);
-
- // Get and clear saved release fences.
- void takeReleaseFences(Display display, std::vector<Layer>* outLayers,
- std::vector<int>* outReleaseFences);
-
- // Get and clear saved present fence.
- void takePresentFence(Display display, int* outPresentFence);
-
- // Get what stage succeeded during PresentOrValidate: Present or Validate
- void takePresentOrValidateStage(Display display, uint32_t* state);
-
- // Get the client target properties requested by hardware composer.
- void takeClientTargetProperty(Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty);
-
-private:
- void resetData();
-
- bool parseSelectDisplay(uint16_t length);
- bool parseSetError(uint16_t length);
- bool parseSetChangedCompositionTypes(uint16_t length);
- bool parseSetDisplayRequests(uint16_t length);
- bool parseSetPresentFence(uint16_t length);
- bool parseSetReleaseFences(uint16_t length);
- bool parseSetPresentOrValidateDisplayResult(uint16_t length);
- bool parseSetClientTargetProperty(uint16_t length);
-
- struct ReturnData {
- uint32_t displayRequests = 0;
-
- std::vector<Layer> changedLayers;
- std::vector<IComposerClient::Composition> compositionTypes;
-
- std::vector<Layer> requestedLayers;
- std::vector<uint32_t> requestMasks;
-
- int presentFence = -1;
-
- std::vector<Layer> releasedLayers;
- std::vector<int> releaseFences;
-
- uint32_t presentOrValidateState;
-
- // Composer 2.4 implementation can return a client target property
- // structure to indicate the client target properties that hardware
- // composer requests. The composer client must change the client target
- // properties to match this request.
- IComposerClient::ClientTargetProperty clientTargetProperty{PixelFormat::RGBA_8888,
- Dataspace::UNKNOWN};
- };
-
- std::vector<CommandError> mErrors;
- std::unordered_map<Display, ReturnData> mReturnData;
-
- // When SELECT_DISPLAY is parsed, this is updated to point to the
- // display's return data in mReturnData. We use it to avoid repeated
- // map lookups.
- ReturnData* mCurrentReturnData;
-};
-
// Composer is a wrapper to IComposer, a proxy to server-side composer.
class AidlComposer final : public Hwc2::Composer {
public:
@@ -140,6 +57,8 @@
explicit AidlComposer(const std::string& serviceName);
~AidlComposer() override;
+ bool isSupported(OptionalFeature) const;
+
std::vector<IComposer::Capability> getCapabilities() override;
std::string dumpDebugInfo() override;
@@ -163,8 +82,10 @@
Error destroyLayer(Display display, Layer layer) override;
Error getActiveConfig(Display display, Config* outConfig) override;
- Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) override;
+ Error getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
@@ -195,7 +116,7 @@
int acquireFence, Dataspace dataspace,
const std::vector<IComposerClient::Rect>& damage) override;
Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
- Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
+ Error setColorTransform(Display display, const float* matrix) override;
Error setOutputBuffer(Display display, const native_handle_t* buffer,
int releaseFence) override;
Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
@@ -203,10 +124,11 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, uint32_t* outNumTypes,
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
uint32_t* outNumRequests) override;
- Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
+ Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
int* outPresentFence, uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
@@ -217,8 +139,9 @@
const std::vector<IComposerClient::Rect>& damage) override;
Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
- Error setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) override;
+ Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) override;
Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
Error setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) override;
@@ -259,7 +182,6 @@
Error setDisplayBrightness(Display display, float brightness) override;
// Composer HAL 2.4
- bool isVsyncPeriodSwitchSupported() override { return true; }
Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) override;
V2_4::Error getDisplayConnectionType(Display display,
@@ -279,18 +201,12 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
- Error getClientTargetProperty(
- Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outClientTargetWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
private:
- class AidlCommandWriter : public AidlCommandWriterBase {
- public:
- explicit AidlCommandWriter(uint32_t initialMaxSize)
- : AidlCommandWriterBase(initialMaxSize) {}
- ~AidlCommandWriter() override {}
- };
-
// Many public functions above simply write a command into the command
// queue to batch the calls. validateDisplay and presentDisplay will call
// this function to execute the command queue.
@@ -306,8 +222,8 @@
// 1. Tightly coupling this cache to the max size of BufferQueue
// 2. Adding an additional slot for the layer caching feature in SurfaceFlinger (see: Planner.h)
static const constexpr uint32_t kMaxLayerBufferCount = BufferQueue::NUM_BUFFER_SLOTS + 1;
- AidlCommandWriter mWriter;
- AidlCommandReader mReader;
+ ComposerClientWriter mWriter;
+ ComposerClientReader mReader;
// Aidl interface
using AidlIComposer = aidl::android::hardware::graphics::composer3::IComposer;
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 3bbce7b..a6a1e6f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -31,6 +31,8 @@
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -73,6 +75,13 @@
virtual ~Composer() = 0;
+ enum class OptionalFeature {
+ RefreshRateSwitching,
+ ExpectedPresentTime,
+ };
+
+ virtual bool isSupported(OptionalFeature) const = 0;
+
virtual std::vector<IComposer::Capability> getCapabilities() = 0;
virtual std::string dumpDebugInfo() = 0;
@@ -98,7 +107,7 @@
virtual Error getActiveConfig(Display display, Config* outConfig) = 0;
virtual Error getChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) = 0;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) = 0;
virtual Error getColorModes(Display display, std::vector<ColorMode>* outModes) = 0;
virtual Error getDisplayAttribute(Display display, Config config,
IComposerClient::Attribute attribute, int32_t* outValue) = 0;
@@ -130,7 +139,7 @@
int acquireFence, Dataspace dataspace,
const std::vector<IComposerClient::Rect>& damage) = 0;
virtual Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) = 0;
- virtual Error setColorTransform(Display display, const float* matrix, ColorTransform hint) = 0;
+ virtual Error setColorTransform(Display display, const float* matrix) = 0;
virtual Error setOutputBuffer(Display display, const native_handle_t* buffer,
int releaseFence) = 0;
virtual Error setPowerMode(Display display, IComposerClient::PowerMode mode) = 0;
@@ -138,12 +147,12 @@
virtual Error setClientTargetSlotCount(Display display) = 0;
- virtual Error validateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests) = 0;
+ virtual Error validateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) = 0;
- virtual Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests, int* outPresentFence,
- uint32_t* state) = 0;
+ virtual Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ int* outPresentFence, uint32_t* state) = 0;
virtual Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) = 0;
/* see setClientTarget for the purpose of slot */
@@ -155,8 +164,9 @@
IComposerClient::BlendMode mode) = 0;
virtual Error setLayerColor(Display display, Layer layer,
const IComposerClient::Color& color) = 0;
- virtual Error setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) = 0;
+ virtual Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) = 0;
virtual Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) = 0;
virtual Error setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) = 0;
@@ -197,7 +207,6 @@
virtual Error setDisplayBrightness(Display display, float brightness) = 0;
// Composer HAL 2.4
- virtual bool isVsyncPeriodSwitchSupported() = 0;
virtual Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) = 0;
virtual V2_4::Error getDisplayConnectionType(
@@ -221,7 +230,11 @@
virtual V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) = 0;
virtual Error getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) = 0;
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) = 0;
+
+ // AIDL Composer
+ virtual Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) = 0;
};
} // namespace android::Hwc2
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/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index e21b0da..3dcea61 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -39,6 +39,8 @@
#include "ComposerHal.h"
+using aidl::android::hardware::graphics::composer3::Composition;
+
namespace android {
using android::Fence;
@@ -142,12 +144,12 @@
bool Display::isVsyncPeriodSwitchSupported() const {
ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
- return mComposer.isVsyncPeriodSwitchSupported();
+ return mComposer.isSupported(android::Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
}
Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) {
std::vector<Hwc2::Layer> layerIds;
- std::vector<Hwc2::IComposerClient::Composition> types;
+ std::vector<Composition> types;
auto intError = mComposer.getChangedCompositionTypes(
mId, &layerIds, &types);
uint32_t numElements = layerIds.size();
@@ -434,8 +436,8 @@
return static_cast<Error>(intError);
}
-Error Display::setColorTransform(const android::mat4& matrix, ColorTransform hint) {
- auto intError = mComposer.setColorTransform(mId, matrix.asArray(), hint);
+Error Display::setColorTransform(const android::mat4& matrix) {
+ auto intError = mComposer.setColorTransform(mId, matrix.asArray());
return static_cast<Error>(intError);
}
@@ -490,11 +492,11 @@
return static_cast<Error>(intError);
}
-Error Display::validate(uint32_t* outNumTypes, uint32_t* outNumRequests)
-{
+Error Display::validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
- auto intError = mComposer.validateDisplay(mId, &numTypes, &numRequests);
+ auto intError = mComposer.validateDisplay(mId, expectedPresentTime, &numTypes, &numRequests);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
@@ -505,14 +507,14 @@
return error;
}
-Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
- sp<android::Fence>* outPresentFence, uint32_t* state) {
-
+Error Display::presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ uint32_t* outNumRequests, sp<android::Fence>* outPresentFence,
+ uint32_t* state) {
uint32_t numTypes = 0;
uint32_t numRequests = 0;
int32_t presentFenceFd = -1;
- auto intError = mComposer.presentOrValidateDisplay(
- mId, &numTypes, &numRequests, &presentFenceFd, state);
+ auto intError = mComposer.presentOrValidateDisplay(mId, expectedPresentTime, &numTypes,
+ &numRequests, &presentFenceFd, state);
auto error = static_cast<Error>(intError);
if (error != Error::NONE && !hasChangesError(error)) {
return error;
@@ -555,8 +557,10 @@
return static_cast<Error>(intError);
}
-Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty) {
- const auto error = mComposer.getClientTargetProperty(mId, outClientTargetProperty);
+Error Display::getClientTargetProperty(ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
+ const auto error =
+ mComposer.getClientTargetProperty(mId, outClientTargetProperty, outWhitePointNits);
return static_cast<Error>(error);
}
@@ -919,6 +923,16 @@
return static_cast<Error>(intError);
}
+// AIDL HAL
+Error Layer::setWhitePointNits(float whitePointNits) {
+ if (CC_UNLIKELY(!mDisplay)) {
+ return Error::BAD_DISPLAY;
+ }
+
+ auto intError = mComposer.setLayerWhitePointNits(mDisplay->getId(), mId, whitePointNits);
+ return static_cast<Error>(intError);
+}
+
} // namespace impl
} // namespace HWC2
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index a65efb2..c2ebd45 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -36,6 +36,8 @@
#include "Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android {
class Fence;
@@ -88,7 +90,8 @@
[[clang::warn_unused_result]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
createLayer() = 0;
[[clang::warn_unused_result]] virtual hal::Error getChangedCompositionTypes(
- std::unordered_map<Layer*, hal::Composition>* outTypes) = 0;
+ std::unordered_map<Layer*, aidl::android::hardware::graphics::composer3::Composition>*
+ outTypes) = 0;
[[clang::warn_unused_result]] virtual hal::Error getColorModes(
std::vector<hal::ColorMode>* outModes) const = 0;
// Returns a bitmask which contains HdrMetadata::Type::*.
@@ -125,16 +128,17 @@
[[clang::warn_unused_result]] virtual hal::Error setColorMode(
hal::ColorMode mode, hal::RenderIntent renderIntent) = 0;
[[clang::warn_unused_result]] virtual hal::Error setColorTransform(
- const android::mat4& matrix, hal::ColorTransform hint) = 0;
+ const android::mat4& matrix) = 0;
[[clang::warn_unused_result]] virtual hal::Error setOutputBuffer(
const android::sp<android::GraphicBuffer>& buffer,
const android::sp<android::Fence>& releaseFence) = 0;
[[clang::warn_unused_result]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
[[clang::warn_unused_result]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
- [[clang::warn_unused_result]] virtual hal::Error validate(uint32_t* outNumTypes,
+ [[clang::warn_unused_result]] virtual hal::Error validate(nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes,
uint32_t* outNumRequests) = 0;
[[clang::warn_unused_result]] virtual hal::Error presentOrValidate(
- uint32_t* outNumTypes, uint32_t* outNumRequests,
+ nsecs_t expectedPresentTime, uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
[[clang::warn_unused_result]] virtual std::future<hal::Error> setDisplayBrightness(
float brightness) = 0;
@@ -146,7 +150,7 @@
std::vector<hal::ContentType>*) const = 0;
[[clang::warn_unused_result]] virtual hal::Error setContentType(hal::ContentType) = 0;
[[clang::warn_unused_result]] virtual hal::Error getClientTargetProperty(
- hal::ClientTargetProperty* outClientTargetProperty) = 0;
+ hal::ClientTargetProperty* outClientTargetProperty, float* outWhitePointNits) = 0;
};
namespace impl {
@@ -163,7 +167,9 @@
hal::Error acceptChanges() override;
base::expected<std::shared_ptr<HWC2::Layer>, hal::Error> createLayer() override;
hal::Error getChangedCompositionTypes(
- std::unordered_map<HWC2::Layer*, hal::Composition>* outTypes) override;
+ std::unordered_map<HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const override;
// Returns a bitmask which contains HdrMetadata::Type::*.
int32_t getSupportedPerFrameMetadata() const override;
@@ -192,13 +198,15 @@
const android::sp<android::Fence>& acquireFence,
hal::Dataspace dataspace) override;
hal::Error setColorMode(hal::ColorMode, hal::RenderIntent) override;
- hal::Error setColorTransform(const android::mat4& matrix, hal::ColorTransform hint) override;
+ hal::Error setColorTransform(const android::mat4& matrix) override;
hal::Error setOutputBuffer(const android::sp<android::GraphicBuffer>&,
const android::sp<android::Fence>& releaseFence) override;
hal::Error setPowerMode(hal::PowerMode) override;
hal::Error setVsyncEnabled(hal::Vsync enabled) override;
- hal::Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override;
- hal::Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
+ hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ uint32_t* outNumRequests) override;
+ hal::Error presentOrValidate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
+ uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence,
uint32_t* state) override;
std::future<hal::Error> setDisplayBrightness(float brightness) override;
@@ -209,7 +217,8 @@
hal::Error getSupportedContentTypes(
std::vector<hal::ContentType>* outSupportedContentTypes) const override;
hal::Error setContentType(hal::ContentType) override;
- hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty) override;
+ hal::Error getClientTargetProperty(hal::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
// Other Display methods
hal::HWDisplayId getId() const override { return mId; }
@@ -265,7 +274,8 @@
[[clang::warn_unused_result]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
[[clang::warn_unused_result]] virtual hal::Error setColor(hal::Color color) = 0;
- [[clang::warn_unused_result]] virtual hal::Error setCompositionType(hal::Composition type) = 0;
+ [[clang::warn_unused_result]] virtual hal::Error setCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition type) = 0;
[[clang::warn_unused_result]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
[[clang::warn_unused_result]] virtual hal::Error setPerFrameMetadata(
const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0;
@@ -288,6 +298,9 @@
// Composer HAL 2.4
[[clang::warn_unused_result]] virtual hal::Error setLayerGenericMetadata(
const std::string& name, bool mandatory, const std::vector<uint8_t>& value) = 0;
+
+ // AIDL HAL
+ [[clang::warn_unused_result]] virtual hal::Error setWhitePointNits(float whitePointNits) = 0;
};
namespace impl {
@@ -312,7 +325,8 @@
hal::Error setBlendMode(hal::BlendMode mode) override;
hal::Error setColor(hal::Color color) override;
- hal::Error setCompositionType(hal::Composition type) override;
+ hal::Error setCompositionType(
+ aidl::android::hardware::graphics::composer3::Composition type) override;
hal::Error setDataspace(hal::Dataspace dataspace) override;
hal::Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
const android::HdrMetadata& metadata) override;
@@ -331,6 +345,9 @@
hal::Error setLayerGenericMetadata(const std::string& name, bool mandatory,
const std::vector<uint8_t>& value) override;
+ // AIDL HAL
+ hal::Error setWhitePointNits(float whitePointNits) override;
+
private:
// These are references to data owned by HWC2::Device, which will outlive
// this HWC2::Layer, so these references are guaranteed to be valid for
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index 06f5df5..3b4fff0 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -159,8 +159,10 @@
}
mRegisteredCallback = true;
+ const bool vsyncSwitchingSupported =
+ mComposer->isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching);
mComposer->registerCallback(
- sp<ComposerCallbackBridge>::make(callback, mComposer->isVsyncPeriodSwitchSupported()));
+ sp<ComposerCallbackBridge>::make(callback, vsyncSwitchingSupported));
}
bool HWComposer::getDisplayIdentificationData(hal::HWDisplayId hwcDisplayId, uint8_t* outPort,
@@ -457,7 +459,7 @@
status_t HWComposer::getDeviceCompositionChanges(
HalDisplayId displayId, bool frameUsesClientComposition,
std::chrono::steady_clock::time_point earliestPresentTime,
- const std::shared_ptr<FenceTime>& previousPresentFence,
+ const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) {
ATRACE_CALL();
@@ -479,16 +481,30 @@
// earliest time to present. Otherwise, we may present a frame too early.
// 2. There is no client composition. Otherwise, we first need to render the
// client target buffer.
- const bool prevFencePending =
- previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
- const bool canPresentEarly =
- !prevFencePending && std::chrono::steady_clock::now() < earliestPresentTime;
- const bool canSkipValidate = !canPresentEarly && !frameUsesClientComposition;
+ const bool canSkipValidate = [&] {
+ // We must call validate if we have client composition
+ if (frameUsesClientComposition) {
+ return false;
+ }
+
+ // If composer supports getting the expected present time, we can skip
+ // as composer will make sure to prevent early presentation
+ if (mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime)) {
+ return true;
+ }
+
+ // composer doesn't support getting the expected present time. We can only
+ // skip validate if we know that we are not going to present early.
+ return std::chrono::steady_clock::now() >= earliestPresentTime ||
+ previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
+ }();
+
displayData.validateWasSkipped = false;
if (canSkipValidate) {
sp<Fence> outPresentFence;
uint32_t state = UINT32_MAX;
- error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state);
+ error = hwcDisplay->presentOrValidate(expectedPresentTime, &numTypes, &numRequests,
+ &outPresentFence, &state);
if (!hasChangesError(error)) {
RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR);
}
@@ -503,7 +519,7 @@
}
// Present failed but Validate ran.
} else {
- error = hwcDisplay->validate(&numTypes, &numRequests);
+ error = hwcDisplay->validate(expectedPresentTime, &numTypes, &numRequests);
}
ALOGV("SkipValidate failed, Falling back to SLOW validate/present");
if (!hasChangesError(error)) {
@@ -522,11 +538,13 @@
RETURN_IF_HWC_ERROR_FOR("getRequests", error, displayId, BAD_INDEX);
DeviceRequestedChanges::ClientTargetProperty clientTargetProperty;
- error = hwcDisplay->getClientTargetProperty(&clientTargetProperty);
+ float clientTargetWhitePointNits;
+ error = hwcDisplay->getClientTargetProperty(&clientTargetProperty, &clientTargetWhitePointNits);
outChanges->emplace(DeviceRequestedChanges{std::move(changedTypes), std::move(displayRequests),
std::move(layerRequests),
- std::move(clientTargetProperty)});
+ std::move(clientTargetProperty),
+ clientTargetWhitePointNits});
error = hwcDisplay->acceptChanges();
RETURN_IF_HWC_ERROR_FOR("acceptChanges", error, displayId, BAD_INDEX);
@@ -567,9 +585,11 @@
return NO_ERROR;
}
- const bool previousFramePending =
- previousPresentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING;
- if (!previousFramePending) {
+ const bool waitForEarliestPresent =
+ !mComposer->isSupported(Hwc2::Composer::OptionalFeature::ExpectedPresentTime) &&
+ previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING;
+
+ if (waitForEarliestPresent) {
ATRACE_NAME("wait for earliest present time");
std::this_thread::sleep_until(earliestPresentTime);
}
@@ -653,11 +673,7 @@
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
- bool isIdentity = transform == mat4();
- auto error = displayData.hwcDisplay
- ->setColorTransform(transform,
- isIdentity ? hal::ColorTransform::IDENTITY
- : hal::ColorTransform::ARBITRARY_MATRIX);
+ auto error = displayData.hwcDisplay->setColorTransform(transform);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -669,6 +685,13 @@
mPhysicalDisplayIdMap.erase(hwcDisplayId);
mDisplayData.erase(displayId);
+
+ // Reset the primary display ID if we're disconnecting it.
+ // This way isHeadless() will return false, which is necessary
+ // because getPrimaryDisplayId() will crash.
+ if (mPrimaryHwcDisplayId == hwcDisplayId) {
+ mPrimaryHwcDisplayId.reset();
+ }
}
status_t HWComposer::setOutputBuffer(HalVirtualDisplayId displayId, const sp<Fence>& acquireFence,
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index 0a090da..b7f1064 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -43,6 +43,8 @@
#include "HWC2.h"
#include "Hal.h"
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
namespace android {
namespace hal = hardware::graphics::composer::hal;
@@ -70,7 +72,9 @@
class HWComposer {
public:
struct DeviceRequestedChanges {
- using ChangedTypes = std::unordered_map<HWC2::Layer*, hal::Composition>;
+ using ChangedTypes =
+ std::unordered_map<HWC2::Layer*,
+ aidl::android::hardware::graphics::composer3::Composition>;
using ClientTargetProperty = hal::ClientTargetProperty;
using DisplayRequests = hal::DisplayRequest;
using LayerRequests = std::unordered_map<HWC2::Layer*, hal::LayerRequest>;
@@ -79,6 +83,7 @@
DisplayRequests displayRequests;
LayerRequests layerRequests;
ClientTargetProperty clientTargetProperty;
+ float clientTargetWhitePointNits;
};
struct HWCDisplayMode {
@@ -129,7 +134,7 @@
virtual status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::chrono::steady_clock::time_point earliestPresentTime,
- const std::shared_ptr<FenceTime>& previousPresentFence,
+ const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) = 0;
virtual status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
@@ -231,9 +236,12 @@
virtual Hwc2::Composer* getComposer() const = 0;
- // Returns the first display connected at boot. It cannot be disconnected, which implies an
- // internal connection type. Its connection via HWComposer::onHotplug, which in practice is
- // immediately after HWComposer construction, must occur before any call to this function.
+ // Returns the first display connected at boot. Its connection via HWComposer::onHotplug,
+ // which in practice is immediately after HWComposer construction, must occur before any
+ // call to this function.
+ // The primary display can be temporarily disconnected from the perspective
+ // of this class. Callers must not call getPrimaryHwcDisplayId() or getPrimaryDisplayId()
+ // if isHeadless().
//
// TODO(b/182939859): Remove special cases for primary display.
virtual hal::HWDisplayId getPrimaryHwcDisplayId() const = 0;
@@ -275,7 +283,7 @@
status_t getDeviceCompositionChanges(
HalDisplayId, bool frameUsesClientComposition,
std::chrono::steady_clock::time_point earliestPresentTime,
- const std::shared_ptr<FenceTime>& previousPresentFence,
+ const std::shared_ptr<FenceTime>& previousPresentFence, nsecs_t expectedPresentTime,
std::optional<DeviceRequestedChanges>* outChanges) override;
status_t setClientTarget(HalDisplayId, uint32_t slot, const sp<Fence>& acquireFence,
diff --git a/services/surfaceflinger/DisplayHardware/Hal.h b/services/surfaceflinger/DisplayHardware/Hal.h
index 02d0658..7236868 100644
--- a/services/surfaceflinger/DisplayHardware/Hal.h
+++ b/services/surfaceflinger/DisplayHardware/Hal.h
@@ -20,6 +20,8 @@
#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <android/hardware/graphics/composer/2.4/IComposerClient.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
#define ERROR_HAS_CHANGES 5
namespace android {
@@ -49,7 +51,6 @@
using Attribute = IComposerClient::Attribute;
using BlendMode = IComposerClient::BlendMode;
using Color = IComposerClient::Color;
-using Composition = IComposerClient::Composition;
using Connection = IComposerCallback::Connection;
using ContentType = IComposerClient::ContentType;
using Capability = IComposer::Capability;
@@ -95,20 +96,23 @@
}
}
-inline std::string to_string(hardware::graphics::composer::hal::Composition composition) {
+inline std::string to_string(
+ aidl::android::hardware::graphics::composer3::Composition composition) {
switch (composition) {
- case hardware::graphics::composer::hal::Composition::INVALID:
+ case aidl::android::hardware::graphics::composer3::Composition::INVALID:
return "Invalid";
- case hardware::graphics::composer::hal::Composition::CLIENT:
+ case aidl::android::hardware::graphics::composer3::Composition::CLIENT:
return "Client";
- case hardware::graphics::composer::hal::Composition::DEVICE:
+ case aidl::android::hardware::graphics::composer3::Composition::DEVICE:
return "Device";
- case hardware::graphics::composer::hal::Composition::SOLID_COLOR:
+ case aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR:
return "SolidColor";
- case hardware::graphics::composer::hal::Composition::CURSOR:
+ case aidl::android::hardware::graphics::composer3::Composition::CURSOR:
return "Cursor";
- case hardware::graphics::composer::hal::Composition::SIDEBAND:
+ case aidl::android::hardware::graphics::composer3::Composition::SIDEBAND:
return "Sideband";
+ case aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION:
+ return "DisplayDecoration";
default:
return "Unknown";
}
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
index 6c40598..96f4496 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp
@@ -30,6 +30,7 @@
#include <hidl/HidlTransportUtils.h>
#include <log/log.h>
#include <utils/Trace.h>
+#include "Hal.h"
#include <algorithm>
#include <cinttypes>
@@ -153,6 +154,15 @@
}
}
+bool HidlComposer::isSupported(OptionalFeature feature) const {
+ switch (feature) {
+ case OptionalFeature::RefreshRateSwitching:
+ return mClient_2_4 != nullptr;
+ case OptionalFeature::ExpectedPresentTime:
+ return false;
+ }
+}
+
std::vector<IComposer::Capability> HidlComposer::getCapabilities() {
std::vector<IComposer::Capability> capabilities;
mComposer->getCapabilities(
@@ -278,7 +288,7 @@
Error HidlComposer::getChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) {
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
mReader.takeChangedCompositionTypes(display, outLayers, outTypes);
return Error::NONE;
}
@@ -497,9 +507,12 @@
return unwrapRet(ret);
}
-Error HidlComposer::setColorTransform(Display display, const float* matrix, ColorTransform hint) {
+Error HidlComposer::setColorTransform(Display display, const float* matrix) {
mWriter.selectDisplay(display);
- mWriter.setColorTransform(matrix, hint);
+ const bool isIdentity = (mat4(matrix) == mat4());
+ mWriter.setColorTransform(matrix,
+ isIdentity ? ColorTransform::IDENTITY
+ : ColorTransform::ARBITRARY_MATRIX);
return Error::NONE;
}
@@ -532,8 +545,8 @@
return unwrapRet(ret);
}
-Error HidlComposer::validateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests) {
+Error HidlComposer::validateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
+ uint32_t* outNumTypes, uint32_t* outNumRequests) {
ATRACE_NAME("HwcValidateDisplay");
mWriter.selectDisplay(display);
mWriter.validateDisplay();
@@ -548,9 +561,9 @@
return Error::NONE;
}
-Error HidlComposer::presentOrValidateDisplay(Display display, uint32_t* outNumTypes,
- uint32_t* outNumRequests, int* outPresentFence,
- uint32_t* state) {
+Error HidlComposer::presentOrValidateDisplay(Display display, nsecs_t /*expectedPresentTime*/,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
+ int* outPresentFence, uint32_t* state) {
ATRACE_NAME("HwcPresentOrValidateDisplay");
mWriter.selectDisplay(display);
mWriter.presentOrvalidateDisplay();
@@ -618,11 +631,22 @@
return Error::NONE;
}
-Error HidlComposer::setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) {
+static IComposerClient::Composition to_hidl_type(
+ aidl::android::hardware::graphics::composer3::Composition type) {
+ LOG_ALWAYS_FATAL_IF(static_cast<int32_t>(type) >
+ static_cast<int32_t>(IComposerClient::Composition::SIDEBAND),
+ "Trying to use %s, which is not supported by HidlComposer!",
+ android::to_string(type).c_str());
+
+ return static_cast<IComposerClient::Composition>(type);
+}
+
+Error HidlComposer::setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
mWriter.selectDisplay(display);
mWriter.selectLayer(layer);
- mWriter.setLayerCompositionType(type);
+ mWriter.setLayerCompositionType(to_hidl_type(type));
return Error::NONE;
}
@@ -1164,8 +1188,14 @@
}
Error HidlComposer::getClientTargetProperty(
- Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty) {
+ Display display, IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) {
mReader.takeClientTargetProperty(display, outClientTargetProperty);
+ *outWhitePointNits = -1.f;
+ return Error::NONE;
+}
+
+Error HidlComposer::setLayerWhitePointNits(Display, Layer, float) {
return Error::NONE;
}
@@ -1260,7 +1290,8 @@
mCurrentReturnData->compositionTypes.reserve(count);
while (count > 0) {
auto layer = read64();
- auto type = static_cast<IComposerClient::Composition>(readSigned());
+ auto type = static_cast<aidl::android::hardware::graphics::composer3::Composition>(
+ readSigned());
mCurrentReturnData->changedLayers.push_back(layer);
mCurrentReturnData->compositionTypes.push_back(type);
@@ -1388,7 +1419,7 @@
void CommandReader::takeChangedCompositionTypes(
Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) {
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes) {
auto found = mReturnData.find(display);
if (found == mReturnData.end()) {
outLayers->clear();
diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
index ad253a2..8190c17 100644
--- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.h
@@ -37,6 +37,8 @@
#include <ui/GraphicBuffer.h>
#include <utils/StrongPointer.h>
+#include <aidl/android/hardware/graphics/composer3/Composition.h>
+
// TODO(b/129481165): remove the #pragma below and fix conversion issues
#pragma clang diagnostic pop // ignored "-Wconversion -Wextra"
@@ -92,8 +94,9 @@
uint32_t* outNumLayerRequestMasks) const;
// Get and clear saved changed composition types.
- void takeChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes);
+ void takeChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes);
// Get and clear saved display requests.
void takeDisplayRequests(Display display, uint32_t* outDisplayRequestMask,
@@ -130,7 +133,7 @@
uint32_t displayRequests = 0;
std::vector<Layer> changedLayers;
- std::vector<IComposerClient::Composition> compositionTypes;
+ std::vector<aidl::android::hardware::graphics::composer3::Composition> compositionTypes;
std::vector<Layer> requestedLayers;
std::vector<uint32_t> requestMasks;
@@ -165,6 +168,8 @@
explicit HidlComposer(const std::string& serviceName);
~HidlComposer() override;
+ bool isSupported(OptionalFeature) const;
+
std::vector<IComposer::Capability> getCapabilities() override;
std::string dumpDebugInfo() override;
@@ -188,8 +193,10 @@
Error destroyLayer(Display display, Layer layer) override;
Error getActiveConfig(Display display, Config* outConfig) override;
- Error getChangedCompositionTypes(Display display, std::vector<Layer>* outLayers,
- std::vector<IComposerClient::Composition>* outTypes) override;
+ Error getChangedCompositionTypes(
+ Display display, std::vector<Layer>* outLayers,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>* outTypes)
+ override;
Error getColorModes(Display display, std::vector<ColorMode>* outModes) override;
Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
int32_t* outValue) override;
@@ -220,7 +227,7 @@
int acquireFence, Dataspace dataspace,
const std::vector<IComposerClient::Rect>& damage) override;
Error setColorMode(Display display, ColorMode mode, RenderIntent renderIntent) override;
- Error setColorTransform(Display display, const float* matrix, ColorTransform hint) override;
+ Error setColorTransform(Display display, const float* matrix) override;
Error setOutputBuffer(Display display, const native_handle_t* buffer,
int releaseFence) override;
Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
@@ -228,10 +235,11 @@
Error setClientTargetSlotCount(Display display) override;
- Error validateDisplay(Display display, uint32_t* outNumTypes,
+ Error validateDisplay(Display display, nsecs_t expectedPresentTime, uint32_t* outNumTypes,
uint32_t* outNumRequests) override;
- Error presentOrValidateDisplay(Display display, uint32_t* outNumTypes, uint32_t* outNumRequests,
+ Error presentOrValidateDisplay(Display display, nsecs_t expectedPresentTime,
+ uint32_t* outNumTypes, uint32_t* outNumRequests,
int* outPresentFence, uint32_t* state) override;
Error setCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
@@ -242,8 +250,9 @@
const std::vector<IComposerClient::Rect>& damage) override;
Error setLayerBlendMode(Display display, Layer layer, IComposerClient::BlendMode mode) override;
Error setLayerColor(Display display, Layer layer, const IComposerClient::Color& color) override;
- Error setLayerCompositionType(Display display, Layer layer,
- IComposerClient::Composition type) override;
+ Error setLayerCompositionType(
+ Display display, Layer layer,
+ aidl::android::hardware::graphics::composer3::Composition type) override;
Error setLayerDataspace(Display display, Layer layer, Dataspace dataspace) override;
Error setLayerDisplayFrame(Display display, Layer layer,
const IComposerClient::Rect& frame) override;
@@ -284,7 +293,6 @@
Error setDisplayBrightness(Display display, float brightness) override;
// Composer HAL 2.4
- bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) override;
V2_4::Error getDisplayConnectionType(Display display,
@@ -304,9 +312,10 @@
bool mandatory, const std::vector<uint8_t>& value) override;
V2_4::Error getLayerGenericMetadataKeys(
std::vector<IComposerClient::LayerGenericMetadataKey>* outKeys) override;
- Error getClientTargetProperty(
- Display display,
- IComposerClient::ClientTargetProperty* outClientTargetProperty) override;
+ Error getClientTargetProperty(Display display,
+ IComposerClient::ClientTargetProperty* outClientTargetProperty,
+ float* outWhitePointNits) override;
+ Error setLayerWhitePointNits(Display display, Layer layer, float whitePointNits) override;
private:
class CommandWriter : public CommandWriterBase {
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index e26ab11..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;
@@ -326,7 +310,7 @@
status_t VirtualDisplaySurface::dequeueBuffer(Source source,
PixelFormat format, uint64_t usage, int* sslot, sp<Fence>* fence) {
- LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
+ LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
status_t result =
mSource[source]->dequeueBuffer(sslot, fence, mSinkBufferWidth, mSinkBufferHeight,
@@ -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;
@@ -641,7 +610,7 @@
}
status_t VirtualDisplaySurface::refreshOutputBuffer() {
- LOG_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId));
+ LOG_ALWAYS_FATAL_IF(GpuVirtualDisplayId::tryCast(mDisplayId).has_value());
if (mOutputProducerSlot >= 0) {
mSource[SOURCE_SINK]->cancelBuffer(
@@ -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/EffectLayer.cpp b/services/surfaceflinger/EffectLayer.cpp
index 86c6b21..cc85352 100644
--- a/services/surfaceflinger/EffectLayer.cpp
+++ b/services/surfaceflinger/EffectLayer.cpp
@@ -109,7 +109,8 @@
auto* compositionState = editCompositionState();
compositionState->color = getColor();
- compositionState->compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
+ compositionState->compositionType =
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR;
}
sp<compositionengine::LayerFE> EffectLayer::getCompositionEngineLayerFE() const {
@@ -136,8 +137,7 @@
sp<Layer> EffectLayer::createClone() {
sp<EffectLayer> layer = mFlinger->getFactory().createEffectLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, 0, 0,
- LayerMetadata()));
+ LayerCreationArgs(mFlinger.get(), nullptr, mName + " (Mirror)", 0, LayerMetadata()));
layer->setInitialValuesForClone(this);
return layer;
}
diff --git a/services/surfaceflinger/FpsReporter.h b/services/surfaceflinger/FpsReporter.h
index bd7b9a5..438b1aa 100644
--- a/services/surfaceflinger/FpsReporter.h
+++ b/services/surfaceflinger/FpsReporter.h
@@ -24,6 +24,7 @@
#include "Clock.h"
#include "FrameTimeline/FrameTimeline.h"
+#include "WpHash.h"
namespace android {
@@ -50,11 +51,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
struct TrackedListener {
sp<gui::IFpsListener> listener;
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..86e96d7 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),
@@ -667,7 +667,8 @@
packet->set_timestamp(
static_cast<uint64_t>(endTime - kPredictionExpiredStartTimeDelta));
} else {
- packet->set_timestamp(static_cast<uint64_t>(mPredictions.startTime));
+ packet->set_timestamp(static_cast<uint64_t>(
+ mActuals.startTime == 0 ? mPredictions.startTime : mActuals.startTime));
}
auto* event = packet->set_frame_timeline_event();
@@ -778,7 +779,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/HdrLayerInfoReporter.h b/services/surfaceflinger/HdrLayerInfoReporter.h
index 671395f..4ada2b6 100644
--- a/services/surfaceflinger/HdrLayerInfoReporter.h
+++ b/services/surfaceflinger/HdrLayerInfoReporter.h
@@ -22,6 +22,8 @@
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class HdrLayerInfoReporter final : public IBinder::DeathRecipient {
@@ -63,11 +65,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
struct TrackedListener {
sp<gui::IHdrLayerInfoListener> listener;
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 388181c..5948a78 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>
@@ -45,6 +46,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
@@ -87,11 +89,12 @@
std::atomic<int32_t> Layer::sSequence{1};
Layer::Layer(const LayerCreationArgs& args)
- : mFlinger(args.flinger),
+ : sequence(args.sequence.value_or(sSequence++)),
+ mFlinger(args.flinger),
mName(base::StringPrintf("%s#%d", args.name.c_str(), sequence)),
mClientRef(args.client),
- mWindowType(
- static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))) {
+ mWindowType(static_cast<WindowInfo::Type>(args.metadata.getInt32(METADATA_WINDOW_TYPE, 0))),
+ mLayerCreationFlags(args.flags) {
uint32_t layerFlags = 0;
if (args.flags & ISurfaceComposerClient::eHidden) layerFlags |= layer_state_t::eLayerHidden;
if (args.flags & ISurfaceComposerClient::eOpaque) layerFlags |= layer_state_t::eLayerOpaque;
@@ -99,8 +102,6 @@
if (args.flags & ISurfaceComposerClient::eSkipScreenshot)
layerFlags |= layer_state_t::eLayerSkipScreenshot;
- mDrawingState.active_legacy.w = args.w;
- mDrawingState.active_legacy.h = args.h;
mDrawingState.flags = layerFlags;
mDrawingState.active_legacy.transform.set(0, 0);
mDrawingState.crop.makeInvalid();
@@ -185,12 +186,10 @@
}
LayerCreationArgs::LayerCreationArgs(SurfaceFlinger* flinger, sp<Client> client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata)
+ uint32_t flags, LayerMetadata metadata)
: flinger(flinger),
client(std::move(client)),
name(std::move(name)),
- w(w),
- h(h),
flags(flags),
metadata(std::move(metadata)) {
IPCThreadState* ipc = IPCThreadState::self();
@@ -483,6 +482,14 @@
compositionState->stretchEffect.hasEffect()) {
compositionState->forceClientComposition = true;
}
+ // If there are no visible region changes, we still need to update blur parameters.
+ compositionState->blurRegions = drawingState.blurRegions;
+ compositionState->backgroundBlurRadius = drawingState.backgroundBlurRadius;
+
+ // Layer framerate is used in caching decisions.
+ // Retrieve it from the scheduler which maintains an instance of LayerHistory, and store it in
+ // LayerFECompositionState where it would be visible to Flattener.
+ compositionState->fps = mFlinger->getLayerFramerate(systemTime(), getSequence());
}
void Layer::prepareCursorCompositionState() {
@@ -579,6 +586,8 @@
layerSettings.alpha = alpha;
layerSettings.sourceDataspace = getDataSpace();
+
+ layerSettings.whitePointNits = targetSettings.whitePointNits;
switch (targetSettings.blurSetting) {
case LayerFE::ClientCompositionTargetSettings::BlurSetting::Enabled:
layerSettings.backgroundBlurRadius = getBackgroundBlurRadius();
@@ -641,15 +650,16 @@
return {*layerSettings};
}
-Hwc2::IComposerClient::Composition Layer::getCompositionType(const DisplayDevice& display) const {
+aidl::android::hardware::graphics::composer3::Composition Layer::getCompositionType(
+ const DisplayDevice& display) const {
const auto outputLayer = findOutputLayerForDisplay(&display);
if (outputLayer == nullptr) {
- return Hwc2::IComposerClient::Composition::INVALID;
+ return aidl::android::hardware::graphics::composer3::Composition::INVALID;
}
if (outputLayer->getState().hwc) {
return (*outputLayer->getState().hwc).hwcCompositionType;
} else {
- return Hwc2::IComposerClient::Composition::CLIENT;
+ return aidl::android::hardware::graphics::composer3::Composition::CLIENT;
}
}
@@ -886,7 +896,7 @@
uint32_t flags = ISurfaceComposerClient::eFXSurfaceEffect;
std::string name = mName + "BackgroundColorLayer";
mDrawingState.bgColorLayer = mFlinger->getFactory().createEffectLayer(
- LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), 0, 0, flags,
+ LayerCreationArgs(mFlinger.get(), nullptr, std::move(name), flags,
LayerMetadata()));
// add to child list
@@ -923,14 +933,16 @@
bool Layer::setBackgroundBlurRadius(int backgroundBlurRadius) {
if (mDrawingState.backgroundBlurRadius == backgroundBlurRadius) return false;
-
- mDrawingState.sequence++;
+ // If we start or stop drawing blur then the layer's visibility state may change so increment
+ // the magic sequence number.
+ if (mDrawingState.backgroundBlurRadius == 0 || backgroundBlurRadius == 0) {
+ mDrawingState.sequence++;
+ }
mDrawingState.backgroundBlurRadius = backgroundBlurRadius;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
return true;
}
-
bool Layer::setMatrix(const layer_state_t::matrix22_t& matrix,
bool allowNonRectPreservingTransforms) {
ui::Transform t;
@@ -957,6 +969,11 @@
}
bool Layer::setBlurRegions(const std::vector<BlurRegion>& blurRegions) {
+ // If we start or stop drawing blur then the layer's visibility state may change so increment
+ // the magic sequence number.
+ if (mDrawingState.blurRegions.size() == 0 || blurRegions.size() == 0) {
+ mDrawingState.sequence++;
+ }
mDrawingState.blurRegions = blurRegions;
mDrawingState.modified = true;
setTransactionFlags(eTransactionNeeded);
@@ -1157,9 +1174,6 @@
}
bool Layer::setFrameRate(FrameRate frameRate) {
- if (!mFlinger->useFrameRateApi) {
- return false;
- }
if (mDrawingState.frameRate == frameRate) {
return false;
}
@@ -1249,6 +1263,7 @@
getSequence(), mName,
mTransactionName,
/*isBuffer*/ false, getGameMode());
+ surfaceFrame->setActualStartTime(info.startTimeNanos);
// For Transactions, the post time is considered to be both queue and acquire fence time.
surfaceFrame->setActualQueueTime(postTime);
surfaceFrame->setAcquireFenceTime(postTime);
@@ -1266,6 +1281,7 @@
mFlinger->mFrameTimeline->createSurfaceFrameForToken(info, mOwnerPid, mOwnerUid,
getSequence(), mName, debugName,
/*isBuffer*/ true, getGameMode());
+ surfaceFrame->setActualStartTime(info.startTimeNanos);
// For buffers, acquire fence time will set during latch.
surfaceFrame->setActualQueueTime(queueTime);
const auto fps = mFlinger->mScheduler->getFrameRateOverride(getOwnerUid());
@@ -1290,8 +1306,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;
}
@@ -1408,19 +1424,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) {
@@ -1459,8 +1462,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, ' ');
}
@@ -1543,11 +1546,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) {
@@ -1573,7 +1575,7 @@
const auto removeResult = mCurrentChildren.remove(layer);
updateTreeHasFrameRateVote();
- layer->setGameModeForTree(0);
+ layer->setGameModeForTree(GameMode::Unsupported);
layer->updateTreeHasFrameRateVote();
return removeResult;
@@ -2002,10 +2004,10 @@
writeToProtoDrawingState(layerProto, traceFlags, display);
writeToProtoCommonState(layerProto, LayerVector::StateSet::Drawing, traceFlags);
- if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
// Only populate for the primary display.
if (display) {
- const Hwc2::IComposerClient::Composition compositionType = getCompositionType(*display);
+ const auto compositionType = getCompositionType(*display);
layerProto->set_hwc_composition_type(static_cast<HwcCompositionType>(compositionType));
}
}
@@ -2020,43 +2022,38 @@
void Layer::writeToProtoDrawingState(LayerProto* layerInfo, uint32_t traceFlags,
const DisplayDevice* display) {
const ui::Transform transform = getTransform();
+ auto buffer = getBuffer();
+ if (buffer != nullptr) {
+ LayerProtoHelper::writeToProto(buffer,
+ [&]() { return layerInfo->mutable_active_buffer(); });
+ LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
+ layerInfo->mutable_buffer_transform());
+ }
+ layerInfo->set_invalidate(contentDirty);
+ layerInfo->set_is_protected(isProtected());
+ layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
+ layerInfo->set_queued_frames(getQueuedFrameCount());
+ layerInfo->set_refresh_pending(isBufferLatched());
+ layerInfo->set_curr_frame(mCurrentFrameNumber);
+ layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
- if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
+ layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
+ layerInfo->set_corner_radius(getRoundedCornerState().radius);
+ layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
+ layerInfo->set_is_trusted_overlay(isTrustedOverlay());
+ LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
+ LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
+ [&]() { return layerInfo->mutable_position(); });
+ LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
+ if (traceFlags & LayerTracing::TRACE_COMPOSITION) {
+ LayerProtoHelper::writeToProto(getVisibleRegion(display),
+ [&]() { return layerInfo->mutable_visible_region(); });
+ }
+ LayerProtoHelper::writeToProto(surfaceDamageRegion,
+ [&]() { return layerInfo->mutable_damage_region(); });
- auto buffer = getBuffer();
- if (buffer != nullptr) {
- LayerProtoHelper::writeToProto(buffer,
- [&]() { return layerInfo->mutable_active_buffer(); });
- LayerProtoHelper::writeToProtoDeprecated(ui::Transform(getBufferTransform()),
- layerInfo->mutable_buffer_transform());
- }
- layerInfo->set_invalidate(contentDirty);
- layerInfo->set_is_protected(isProtected());
- layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(getDataSpace())));
- layerInfo->set_queued_frames(getQueuedFrameCount());
- layerInfo->set_refresh_pending(isBufferLatched());
- layerInfo->set_curr_frame(mCurrentFrameNumber);
- layerInfo->set_effective_scaling_mode(getEffectiveScalingMode());
-
- layerInfo->set_requested_corner_radius(getDrawingState().cornerRadius);
- layerInfo->set_corner_radius(getRoundedCornerState().radius);
- layerInfo->set_background_blur_radius(getBackgroundBlurRadius());
- layerInfo->set_is_trusted_overlay(isTrustedOverlay());
- LayerProtoHelper::writeToProtoDeprecated(transform, layerInfo->mutable_transform());
- LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(),
- [&]() { return layerInfo->mutable_position(); });
- LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); });
- if (traceFlags & SurfaceTracing::TRACE_COMPOSITION) {
- LayerProtoHelper::writeToProto(getVisibleRegion(display),
- [&]() { return layerInfo->mutable_visible_region(); });
- }
- LayerProtoHelper::writeToProto(surfaceDamageRegion,
- [&]() { return layerInfo->mutable_damage_region(); });
-
- if (hasColorTransform()) {
- LayerProtoHelper::writeToProto(getColorTransform(),
- layerInfo->mutable_color_transform());
- }
+ if (hasColorTransform()) {
+ LayerProtoHelper::writeToProto(getColorTransform(), layerInfo->mutable_color_transform());
}
LayerProtoHelper::writeToProto(mSourceBounds,
@@ -2076,70 +2073,66 @@
ui::Transform requestedTransform = state.transform;
- if (traceFlags & SurfaceTracing::TRACE_CRITICAL) {
- layerInfo->set_id(sequence);
- layerInfo->set_name(getName().c_str());
- layerInfo->set_type(getType());
+ layerInfo->set_id(sequence);
+ layerInfo->set_name(getName().c_str());
+ layerInfo->set_type(getType());
- for (const auto& child : children) {
- layerInfo->add_children(child->sequence);
- }
-
- for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
- sp<Layer> strongRelative = weakRelative.promote();
- if (strongRelative != nullptr) {
- layerInfo->add_relatives(strongRelative->sequence);
- }
- }
-
- LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
- [&]() { return layerInfo->mutable_transparent_region(); });
-
- layerInfo->set_layer_stack(getLayerStack().id);
- layerInfo->set_z(state.z);
-
- LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(),
- [&]() {
- return layerInfo->mutable_requested_position();
- });
-
- LayerProtoHelper::writeSizeToProto(state.width, state.height,
- [&]() { return layerInfo->mutable_size(); });
-
- LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
-
- layerInfo->set_is_opaque(isOpaque(state));
-
-
- layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
- LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
- LayerProtoHelper::writeToProto(state.color,
- [&]() { return layerInfo->mutable_requested_color(); });
- layerInfo->set_flags(state.flags);
-
- LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
- layerInfo->mutable_requested_transform());
-
- auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
- if (parent != nullptr) {
- layerInfo->set_parent(parent->sequence);
- } else {
- layerInfo->set_parent(-1);
- }
-
- auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
- if (zOrderRelativeOf != nullptr) {
- layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
- } else {
- layerInfo->set_z_order_relative_of(-1);
- }
-
- layerInfo->set_is_relative_of(state.isRelativeOf);
-
- layerInfo->set_owner_uid(mOwnerUid);
+ for (const auto& child : children) {
+ layerInfo->add_children(child->sequence);
}
- if (traceFlags & SurfaceTracing::TRACE_INPUT) {
+ for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+ sp<Layer> strongRelative = weakRelative.promote();
+ if (strongRelative != nullptr) {
+ layerInfo->add_relatives(strongRelative->sequence);
+ }
+ }
+
+ LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy,
+ [&]() { return layerInfo->mutable_transparent_region(); });
+
+ layerInfo->set_layer_stack(getLayerStack().id);
+ layerInfo->set_z(state.z);
+
+ LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() {
+ return layerInfo->mutable_requested_position();
+ });
+
+ LayerProtoHelper::writeSizeToProto(state.width, state.height,
+ [&]() { return layerInfo->mutable_size(); });
+
+ LayerProtoHelper::writeToProto(state.crop, [&]() { return layerInfo->mutable_crop(); });
+
+ layerInfo->set_is_opaque(isOpaque(state));
+
+ layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+ LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); });
+ LayerProtoHelper::writeToProto(state.color,
+ [&]() { return layerInfo->mutable_requested_color(); });
+ layerInfo->set_flags(state.flags);
+
+ LayerProtoHelper::writeToProtoDeprecated(requestedTransform,
+ layerInfo->mutable_requested_transform());
+
+ auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote();
+ if (parent != nullptr) {
+ layerInfo->set_parent(parent->sequence);
+ } else {
+ layerInfo->set_parent(-1);
+ }
+
+ auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
+ if (zOrderRelativeOf != nullptr) {
+ layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+ } else {
+ layerInfo->set_z_order_relative_of(-1);
+ }
+
+ layerInfo->set_is_relative_of(state.isRelativeOf);
+
+ layerInfo->set_owner_uid(mOwnerUid);
+
+ if (traceFlags & LayerTracing::TRACE_INPUT) {
WindowInfo info;
if (useDrawing) {
info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true);
@@ -2151,7 +2144,7 @@
[&]() { return layerInfo->mutable_input_window_info(); });
}
- if (traceFlags & SurfaceTracing::TRACE_EXTRA) {
+ if (traceFlags & LayerTracing::TRACE_EXTRA) {
auto protoMap = layerInfo->mutable_metadata();
for (const auto& entry : state.metadata.mMap) {
(*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend());
@@ -2188,6 +2181,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;
}
@@ -2435,8 +2430,7 @@
}
void Layer::setInitialValuesForClone(const sp<Layer>& clonedFrom) {
- // copy drawing state from cloned layer
- mDrawingState = clonedFrom->mDrawingState;
+ cloneDrawingState(clonedFrom.get());
mClonedFrom = clonedFrom;
}
@@ -2471,7 +2465,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);
}
@@ -2634,15 +2628,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 bf338c1..31cdf0b 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,15 +52,13 @@
#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 "SurfaceTracing.h"
+#include "Tracing/LayerTracing.h"
#include "TransactionCallbackInvoker.h"
using namespace android::surfaceflinger;
@@ -85,20 +86,18 @@
} // namespace frametimeline
struct LayerCreationArgs {
- LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t w, uint32_t h,
- uint32_t flags, LayerMetadata);
+ LayerCreationArgs(SurfaceFlinger*, sp<Client>, std::string name, uint32_t flags, LayerMetadata);
SurfaceFlinger* flinger;
const sp<Client> client;
std::string name;
- uint32_t w;
- uint32_t h;
uint32_t flags;
LayerMetadata metadata;
pid_t callingPid;
uid_t callingUid;
uint32_t textureName;
+ std::optional<uint32_t> sequence = std::nullopt;
};
class Layer : public virtual RefBase, compositionengine::LayerFE {
@@ -280,6 +279,8 @@
sp<IBinder> releaseBufferEndpoint;
gui::DropInputMode dropInputMode;
+
+ bool autoRefresh = false;
};
/*
@@ -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;
@@ -693,7 +693,7 @@
// external mStateLock. If writing drawing state, this function should be called on the
// main or tracing thread.
void writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL);
+ uint32_t traceFlags = LayerTracing::TRACE_ALL);
gui::WindowInfo::Type getWindowType() const { return mWindowType; }
@@ -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; }
@@ -879,7 +879,7 @@
// Layer serial number. This gives layers an explicit ordering, so we
// have a stable sort order when their layer stack and Z-order are
// the same.
- int32_t sequence{sSequence++};
+ const int32_t sequence;
bool mPendingHWCDestroy{false};
@@ -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);
@@ -1036,7 +1037,8 @@
// Returns true if the layer can draw shadows on its border.
virtual bool canDrawShadows() const { return true; }
- Hwc2::IComposerClient::Composition getCompositionType(const DisplayDevice&) const;
+ aidl::android::hardware::graphics::composer3::Composition getCompositionType(
+ const DisplayDevice&) const;
/**
* Returns an unsorted vector of all layers that are part of this tree.
@@ -1109,14 +1111,15 @@
// 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;
bool mIsAtRoot = false;
+
+ uint32_t mLayerCreationFlags;
};
std::ostream& operator<<(std::ostream& stream, const Layer::FrameRate& rate);
diff --git a/services/surfaceflinger/LayerRenderArea.cpp b/services/surfaceflinger/LayerRenderArea.cpp
index e84508f..a1e1455 100644
--- a/services/surfaceflinger/LayerRenderArea.cpp
+++ b/services/surfaceflinger/LayerRenderArea.cpp
@@ -94,16 +94,28 @@
// no need to check rotation because there is none
mNeedsFiltering = sourceCrop.width() != getReqWidth() || sourceCrop.height() != getReqHeight();
+ // If layer is offscreen, update mirroring info if it exists
+ if (mLayer->isRemovedFromCurrentState()) {
+ mLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->updateMirrorInfo(); });
+ mLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->updateCloneBufferInfo(); });
+ }
+
if (!mChildrenOnly) {
mTransform = mLayer->getTransform().inverse();
+ // If the layer is offscreen, compute bounds since we don't compute bounds for offscreen
+ // layers in a regular cycles.
+ if (mLayer->isRemovedFromCurrentState()) {
+ FloatRect maxBounds = mFlinger.getMaxDisplayBounds();
+ mLayer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
+ }
drawLayers();
} else {
- uint32_t w = static_cast<uint32_t>(getWidth());
- uint32_t h = static_cast<uint32_t>(getHeight());
// In the "childrenOnly" case we reparent the children to a screenshot
// layer which has no properties set and which does not draw.
sp<ContainerLayer> screenshotParentLayer = mFlinger.getFactory().createContainerLayer(
- {&mFlinger, nullptr, "Screenshot Parent"s, w, h, 0, LayerMetadata()});
+ {&mFlinger, nullptr, "Screenshot Parent"s, 0, LayerMetadata()});
ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop);
drawLayers();
diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp
index 6b2d745..df76f50 100644
--- a/services/surfaceflinger/MonitoredProducer.cpp
+++ b/services/surfaceflinger/MonitoredProducer.cpp
@@ -33,13 +33,7 @@
mFlinger(flinger),
mLayer(layer) {}
-MonitoredProducer::~MonitoredProducer() {
- // Remove ourselves from SurfaceFlinger's list. We do this asynchronously
- // because we don't know where this destructor is called from. It could be
- // called with the mStateLock held, leading to a dead-lock (it actually
- // happens).
- mFlinger->removeGraphicBufferProducerAsync(onAsBinder());
-}
+MonitoredProducer::~MonitoredProducer() {}
status_t MonitoredProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
return mProducer->requestBuffer(slot, buf);
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 2231853..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>
@@ -30,15 +31,17 @@
#include <unordered_map>
#include "Scheduler/OneShotTimer.h"
+#include "WpHash.h"
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);
@@ -88,11 +91,6 @@
sp<IRegionSamplingListener> listener;
};
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
std::vector<float> sampleBuffer(
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors, uint32_t orientation);
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 e07eae7..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) {
@@ -560,8 +561,8 @@
}
}
-int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
- nsecs_t deadlineTimestamp) const {
+int64_t EventThread::generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const {
if (mTokenManager != nullptr) {
return mTokenManager->generateTokenForPredictions(
{timestamp, deadlineTimestamp, expectedVSyncTimestamp});
@@ -586,7 +587,7 @@
nsecs_t expectedVSync =
event.vsync.expectedVSyncTimestamp + multiplier * event.vsync.frameInterval;
event.vsync.frameTimelines[currentIndex] =
- {.vsyncId = generateToken(event.header.timestamp, expectedVSync, deadline),
+ {.vsyncId = generateToken(event.header.timestamp, deadline, expectedVSync),
.deadlineTimestamp = deadline,
.expectedVSyncTimestamp = expectedVSync};
}
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index 73ae5dc..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;
@@ -204,8 +204,8 @@
void onVSyncEvent(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
nsecs_t deadlineTimestamp) override;
- int64_t generateToken(nsecs_t timestamp, nsecs_t expectedVSyncTimestamp,
- nsecs_t deadlineTimestamp) const;
+ int64_t generateToken(nsecs_t timestamp, nsecs_t deadlineTimestamp,
+ nsecs_t expectedVSyncTimestamp) const;
void generateFrameTimeline(DisplayEventReceiver::Event& event) const;
const std::unique_ptr<VSyncSource> mVSyncSource GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 84e3548..74a2ca7 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -84,42 +84,38 @@
void LayerHistory::registerLayer(Layer* layer, LayerVoteType type) {
std::lock_guard lock(mLock);
- for (const auto& info : mLayerInfos) {
- LOG_ALWAYS_FATAL_IF(info.first == layer, "%s already registered", layer->getName().c_str());
- }
+ LOG_ALWAYS_FATAL_IF(findLayer(layer->getSequence()).first !=
+ LayerHistory::layerStatus::NotFound,
+ "%s already registered", layer->getName().c_str());
auto info = std::make_unique<LayerInfo>(layer->getName(), layer->getOwnerUid(), type);
- mLayerInfos.emplace_back(layer, std::move(info));
+
+ // The layer can be placed on either map, it is assumed that partitionLayers() will be called
+ // to correct them.
+ mInactiveLayerInfos.insert({layer->getSequence(), std::make_pair(layer, std::move(info))});
}
void LayerHistory::deregisterLayer(Layer* layer) {
std::lock_guard lock(mLock);
-
- const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
- [layer](const auto& pair) { return pair.first == layer; });
- LOG_ALWAYS_FATAL_IF(it == mLayerInfos.end(), "%s: unknown layer %p", __FUNCTION__, layer);
-
- const size_t i = static_cast<size_t>(it - mLayerInfos.begin());
- if (i < mActiveLayersEnd) {
- mActiveLayersEnd--;
+ if (!mActiveLayerInfos.erase(layer->getSequence())) {
+ if (!mInactiveLayerInfos.erase(layer->getSequence())) {
+ LOG_ALWAYS_FATAL("%s: unknown layer %p", __FUNCTION__, layer);
+ }
}
- const size_t last = mLayerInfos.size() - 1;
- std::swap(mLayerInfos[i], mLayerInfos[last]);
- mLayerInfos.erase(mLayerInfos.begin() + static_cast<long>(last));
}
void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now,
LayerUpdateType updateType) {
std::lock_guard lock(mLock);
+ auto id = layer->getSequence();
- const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
- [layer](const auto& pair) { return pair.first == layer; });
- if (it == mLayerInfos.end()) {
+ auto [found, layerPair] = findLayer(id);
+ if (found == LayerHistory::layerStatus::NotFound) {
// Offscreen layer
ALOGV("LayerHistory::record: %s not registered", layer->getName().c_str());
return;
}
- const auto& info = it->second;
+ const auto& info = layerPair->second;
const auto layerProps = LayerInfo::LayerProps{
.visible = layer->isVisible(),
.bounds = layer->getBounds(),
@@ -131,9 +127,10 @@
info->setLastPresentTime(presentTime, now, updateType, mModeChangePending, layerProps);
// Activate layer if inactive.
- if (const auto end = activeLayers().end(); it >= end) {
- std::iter_swap(it, end);
- mActiveLayersEnd++;
+ if (found == LayerHistory::layerStatus::LayerInInactiveMap) {
+ mActiveLayerInfos.insert(
+ {id, std::make_pair(layerPair->first, std::move(layerPair->second))});
+ mInactiveLayerInfos.erase(id);
}
}
@@ -145,7 +142,8 @@
partitionLayers(now);
- for (const auto& [layer, info] : activeLayers()) {
+ for (const auto& [key, value] : mActiveLayerInfos) {
+ auto& info = value.second;
const auto frameRateSelectionPriority = info->getFrameRateSelectionPriority();
const auto layerFocused = Layer::isLayerFocusedBasedOnPriority(frameRateSelectionPriority);
ALOGV("%s has priority: %d %s focused", info->getName().c_str(), frameRateSelectionPriority,
@@ -179,12 +177,29 @@
void LayerHistory::partitionLayers(nsecs_t now) {
const nsecs_t threshold = getActiveLayerThreshold(now);
- // Collect expired and inactive layers after active layers.
- size_t i = 0;
- while (i < mActiveLayersEnd) {
- auto& [layerUnsafe, info] = mLayerInfos[i];
+ // iterate over inactive map
+ LayerInfos::iterator it = mInactiveLayerInfos.begin();
+ while (it != mInactiveLayerInfos.end()) {
+ auto& [layerUnsafe, info] = it->second;
if (isLayerActive(*info, threshold)) {
- i++;
+ // move this to the active map
+
+ mActiveLayerInfos.insert({it->first, std::move(it->second)});
+ it = mInactiveLayerInfos.erase(it);
+ } else {
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ info->onLayerInactive(now);
+ it++;
+ }
+ }
+
+ // iterate over active map
+ it = mActiveLayerInfos.begin();
+ while (it != mActiveLayerInfos.end()) {
+ auto& [layerUnsafe, info] = it->second;
+ if (isLayerActive(*info, threshold)) {
// Set layer vote if set
const auto frameRate = info->getSetFrameRateVote();
const auto voteType = [&]() {
@@ -206,30 +221,68 @@
} else {
info->resetLayerVote();
}
- continue;
- }
- if (CC_UNLIKELY(mTraceEnabled)) {
- trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ it++;
+ } else {
+ if (CC_UNLIKELY(mTraceEnabled)) {
+ trace(*info, LayerHistory::LayerVoteType::NoVote, 0);
+ }
+ info->onLayerInactive(now);
+ // move this to the inactive map
+ mInactiveLayerInfos.insert({it->first, std::move(it->second)});
+ it = mActiveLayerInfos.erase(it);
}
-
- info->onLayerInactive(now);
- std::swap(mLayerInfos[i], mLayerInfos[--mActiveLayersEnd]);
}
}
void LayerHistory::clear() {
std::lock_guard lock(mLock);
-
- for (const auto& [layer, info] : activeLayers()) {
- info->clearHistory(systemTime());
+ for (const auto& [key, value] : mActiveLayerInfos) {
+ value.second->clearHistory(systemTime());
}
}
std::string LayerHistory::dump() const {
std::lock_guard lock(mLock);
- return base::StringPrintf("LayerHistory{size=%zu, active=%zu}", mLayerInfos.size(),
- mActiveLayersEnd);
+ return base::StringPrintf("LayerHistory{size=%zu, active=%zu}",
+ mActiveLayerInfos.size() + mInactiveLayerInfos.size(),
+ mActiveLayerInfos.size());
+}
+
+float LayerHistory::getLayerFramerate(nsecs_t now, int32_t id) const {
+ std::lock_guard lock(mLock);
+ auto [found, layerPair] = findLayer(id);
+ if (found != LayerHistory::layerStatus::NotFound) {
+ return layerPair->second->getFps(now).getValue();
+ }
+ return 0.f;
+}
+
+std::pair<LayerHistory::layerStatus, LayerHistory::LayerPair*> LayerHistory::findLayer(int32_t id) {
+ // the layer could be in either the active or inactive map, try both
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+ }
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+ }
+ return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
+}
+
+std::pair<LayerHistory::layerStatus, const LayerHistory::LayerPair*> LayerHistory::findLayer(
+ int32_t id) const {
+ // the layer could be in either the active or inactive map, try both
+ auto it = mActiveLayerInfos.find(id);
+ if (it != mActiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInActiveMap, &(it->second));
+ }
+ it = mInactiveLayerInfos.find(id);
+ if (it != mInactiveLayerInfos.end()) {
+ return std::make_pair(LayerHistory::layerStatus::LayerInInactiveMap, &(it->second));
+ }
+ return std::make_pair(LayerHistory::layerStatus::NotFound, nullptr);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index 92236f5..cc55700 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -20,6 +20,7 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <map>
#include <memory>
#include <mutex>
#include <string>
@@ -31,11 +32,9 @@
namespace android {
class Layer;
-class TestableScheduler;
namespace scheduler {
-class LayerHistoryTest;
class LayerInfo;
class LayerHistory {
@@ -74,34 +73,43 @@
void deregisterLayer(Layer*);
std::string dump() const;
+ // return the frames per second of the layer with the given sequence id.
+ float getLayerFramerate(nsecs_t now, int32_t id) 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>;
+ // keyed by id as returned from Layer::getSequence()
+ using LayerInfos = std::unordered_map<int32_t, LayerPair>;
- struct ActiveLayers {
- LayerInfos& infos;
- const size_t index;
+ // Iterates over layers maps moving all active layers to mActiveLayerInfos and all inactive
+ // layers to mInactiveLayerInfos.
+ // worst case time complexity is O(2 * inactive + active)
+ void partitionLayers(nsecs_t now) REQUIRES(mLock);
- auto begin() { return infos.begin(); }
- auto end() { return begin() + static_cast<long>(index); }
+ enum class layerStatus {
+ NotFound,
+ LayerInActiveMap,
+ LayerInInactiveMap,
};
- ActiveLayers activeLayers() REQUIRES(mLock) { return {mLayerInfos, mActiveLayersEnd}; }
-
- // Iterates over layers in a single pass, swapping pairs such that active layers precede
- // inactive layers, and inactive layers precede expired layers. Removes expired layers by
- // truncating after inactive layers.
- void partitionLayers(nsecs_t now) REQUIRES(mLock);
+ // looks up a layer by sequence id in both layerInfo maps.
+ // The first element indicates if and where the item was found
+ std::pair<layerStatus, LayerHistory::LayerPair*> findLayer(int32_t id) REQUIRES(mLock);
+ std::pair<layerStatus, const LayerHistory::LayerPair*> findLayer(int32_t id) const
+ REQUIRES(mLock);
mutable std::mutex mLock;
- // Partitioned such that active layers precede inactive layers. For fast lookup, the few active
- // layers are at the front, and weak pointers are stored in contiguous memory to hit the cache.
- LayerInfos mLayerInfos GUARDED_BY(mLock);
- size_t mActiveLayersEnd GUARDED_BY(mLock) = 0;
+ // Partitioned into two maps to facility two kinds of retrieval:
+ // 1. retrieval of a layer by id (attempt lookup in both maps)
+ // 2. retrieval of all active layers (iterate that map)
+ // The partitioning is allowed to become out of date but calling partitionLayers refreshes the
+ // validity of each map.
+ LayerInfos mActiveLayerInfos GUARDED_BY(mLock);
+ LayerInfos mInactiveLayerInfos GUARDED_BY(mLock);
uint32_t mDisplayArea = 0;
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 314526a..943615c 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"
@@ -74,12 +75,16 @@
}
bool LayerInfo::isFrequent(nsecs_t now) const {
+ using fps_approx_ops::operator>=;
// If we know nothing about this layer we consider it as frequent as it might be the start
// of an animation.
if (mFrameTimes.size() < kFrequentLayerWindowSize) {
return true;
}
+ return getFps(now) >= kMinFpsForFrequentLayer;
+}
+Fps LayerInfo::getFps(nsecs_t now) const {
// Find the first active frame
auto it = mFrameTimes.begin();
for (; it != mFrameTimes.end(); ++it) {
@@ -90,14 +95,12 @@
const auto numFrames = std::distance(it, mFrameTimes.end());
if (numFrames < kFrequentLayerWindowSize) {
- return false;
+ return Fps();
}
- using fps_approx_ops::operator>=;
-
// Layer is considered frequent if the average frame rate is higher than the threshold
const auto totalTime = mFrameTimes.back().queueTime - it->queueTime;
- return Fps::fromPeriodNsecs(totalTime / (numFrames - 1)) >= kMinFpsForFrequentLayer;
+ return Fps::fromPeriodNsecs(totalTime / (numFrames - 1));
}
bool LayerInfo::isAnimating(nsecs_t now) const {
@@ -235,7 +238,7 @@
if (!isFrequent(now)) {
ALOGV("%s is infrequent", mName.c_str());
mLastRefreshRate.animatingOrInfrequent = true;
- // Infrequent layers vote for minimal refresh rate for
+ // Infrequent layers vote for mininal refresh rate for
// battery saving purposes and also to prevent b/135718869.
return {LayerHistory::LayerVoteType::Min, Fps()};
}
@@ -257,10 +260,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..2d88a4f 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
@@ -172,6 +178,9 @@
// Returns a C string for tracing a vote
const char* getTraceTag(LayerHistory::LayerVoteType type) const;
+ // Return the framerate of this layer.
+ Fps getFps(nsecs_t now) const;
+
void onLayerInactive(nsecs_t now) {
// Mark mFrameTimeValidSince to now to ignore all previous frame times.
// We are not deleting the old frame to keep track of whether we should treat the first
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.cpp b/services/surfaceflinger/Scheduler/MessageQueue.cpp
index 043a536..a020e2c 100644
--- a/services/surfaceflinger/Scheduler/MessageQueue.cpp
+++ b/services/surfaceflinger/Scheduler/MessageQueue.cpp
@@ -30,41 +30,30 @@
namespace android::impl {
-void MessageQueue::Handler::dispatchComposite() {
- if ((mEventMask.fetch_or(kComposite) & kComposite) == 0) {
- mQueue.mLooper->sendMessage(this, Message(kComposite));
- }
-}
-
-void MessageQueue::Handler::dispatchCommit(int64_t vsyncId, nsecs_t expectedVsyncTime) {
- if ((mEventMask.fetch_or(kCommit) & kCommit) == 0) {
+void MessageQueue::Handler::dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime) {
+ if (!mFramePending.exchange(true)) {
mVsyncId = vsyncId;
mExpectedVsyncTime = expectedVsyncTime;
- mQueue.mLooper->sendMessage(this, Message(kCommit));
+ mQueue.mLooper->sendMessage(this, Message());
}
}
bool MessageQueue::Handler::isFramePending() const {
- constexpr auto kPendingMask = kCommit | kComposite;
- return (mEventMask.load() & kPendingMask) != 0;
+ return mFramePending.load();
}
-void MessageQueue::Handler::handleMessage(const Message& message) {
+void MessageQueue::Handler::handleMessage(const Message&) {
+ mFramePending.store(false);
+
const nsecs_t frameTime = systemTime();
- switch (message.what) {
- case kCommit:
- mEventMask.fetch_and(~kCommit);
- if (!mQueue.mCompositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
- return;
- }
- // Composite immediately, rather than after pending tasks through scheduleComposite.
- [[fallthrough]];
- case kComposite:
- mEventMask.fetch_and(~kComposite);
- mQueue.mCompositor.composite(frameTime);
- mQueue.mCompositor.sample();
- break;
+ auto& compositor = mQueue.mCompositor;
+
+ if (!compositor.commit(frameTime, mVsyncId, mExpectedVsyncTime)) {
+ return;
}
+
+ compositor.composite(frameTime);
+ compositor.sample();
}
MessageQueue::MessageQueue(ICompositor& compositor)
@@ -122,7 +111,7 @@
const auto vsyncId = mVsync.tokenManager->generateTokenForPredictions(
{targetWakeupTime, readyTime, vsyncTime});
- mHandler->dispatchCommit(vsyncId, vsyncTime);
+ mHandler->dispatchFrame(vsyncId, vsyncTime);
}
void MessageQueue::initVsync(scheduler::VSyncDispatch& dispatch,
@@ -176,7 +165,7 @@
mLooper->sendMessage(handler, Message());
}
-void MessageQueue::scheduleCommit() {
+void MessageQueue::scheduleFrame() {
ATRACE_CALL();
{
@@ -195,18 +184,14 @@
.earliestVsync = mVsync.lastCallbackTime.count()});
}
-void MessageQueue::scheduleComposite() {
- mHandler->dispatchComposite();
-}
-
void MessageQueue::injectorCallback() {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
while ((n = DisplayEventReceiver::getEvents(&mInjector.tube, buffer, 8)) > 0) {
for (int i = 0; i < n; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
- mHandler->dispatchCommit(buffer[i].vsync.vsyncId,
- buffer[i].vsync.expectedVSyncTimestamp);
+ auto& vsync = buffer[i].vsync;
+ mHandler->dispatchFrame(vsync.vsyncId, vsync.expectedVSyncTimestamp);
break;
}
}
diff --git a/services/surfaceflinger/Scheduler/MessageQueue.h b/services/surfaceflinger/Scheduler/MessageQueue.h
index 2c908a6..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>
@@ -71,8 +71,7 @@
virtual void setInjector(sp<EventThreadConnection>) = 0;
virtual void waitMessage() = 0;
virtual void postMessage(sp<MessageHandler>&&) = 0;
- virtual void scheduleCommit() = 0;
- virtual void scheduleComposite() = 0;
+ virtual void scheduleFrame() = 0;
using Clock = std::chrono::steady_clock;
virtual std::optional<Clock::time_point> getScheduledFrameTime() const = 0;
@@ -83,11 +82,8 @@
class MessageQueue : public android::MessageQueue {
protected:
class Handler : public MessageHandler {
- static constexpr uint32_t kCommit = 0b1;
- static constexpr uint32_t kComposite = 0b10;
-
MessageQueue& mQueue;
- std::atomic<uint32_t> mEventMask = 0;
+ std::atomic_bool mFramePending = false;
std::atomic<int64_t> mVsyncId = 0;
std::atomic<nsecs_t> mExpectedVsyncTime = 0;
@@ -97,8 +93,7 @@
bool isFramePending() const;
- virtual void dispatchCommit(int64_t vsyncId, nsecs_t expectedVsyncTime);
- void dispatchComposite();
+ virtual void dispatchFrame(int64_t vsyncId, nsecs_t expectedVsyncTime);
};
friend class Handler;
@@ -147,8 +142,7 @@
void waitMessage() override;
void postMessage(sp<MessageHandler>&&) override;
- void scheduleCommit() override;
- void scheduleComposite() override;
+ void scheduleFrame() override;
std::optional<Clock::time_point> getScheduledFrameTime() const override;
};
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index aabd88a..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;
@@ -744,7 +729,6 @@
[getCallback] {
if (const auto callback = getCallback()) callback->onExpired();
});
- mIdleTimer->start();
}
}
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 53472ef..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;
@@ -355,10 +354,22 @@
std::function<void()> kernelTimerExpired) {
std::scoped_lock lock(mIdleTimerCallbacksMutex);
mIdleTimerCallbacks.emplace();
- mIdleTimerCallbacks->platform.onReset = platformTimerReset;
- mIdleTimerCallbacks->platform.onExpired = platformTimerExpired;
- mIdleTimerCallbacks->kernel.onReset = kernelTimerReset;
- mIdleTimerCallbacks->kernel.onExpired = kernelTimerExpired;
+ mIdleTimerCallbacks->platform.onReset = std::move(platformTimerReset);
+ mIdleTimerCallbacks->platform.onExpired = std::move(platformTimerExpired);
+ mIdleTimerCallbacks->kernel.onReset = std::move(kernelTimerReset);
+ mIdleTimerCallbacks->kernel.onExpired = std::move(kernelTimerExpired);
+ }
+
+ void startIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->start();
+ }
+ }
+
+ void stopIdleTimer() {
+ if (mIdleTimer) {
+ mIdleTimer->stop();
+ }
}
void resetIdleTimer(bool kernelOnly) {
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 eeea25d..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,74 +57,14 @@
} \
} while (false)
-using namespace std::string_literals;
+namespace android::scheduler {
-namespace android {
+Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features)
+ : impl::MessageQueue(compositor), mFeatures(features), mSchedulerCallback(callback) {}
-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(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& callback)
- : Scheduler(configs, callback,
- {.useContentDetection = sysprop::use_content_detection_for_refresh_rate(false)}) {
-}
-
-Scheduler::Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& callback, Options options)
- : Scheduler(createVsyncSchedule(configs->supportsKernelIdleTimer()), configs, callback,
- createLayerHistory(), options) {
+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
@@ -147,22 +84,6 @@
}
}
-Scheduler::Scheduler(VsyncSchedule schedule,
- const std::shared_ptr<scheduler::RefreshRateConfigs>& configs,
- ISchedulerCallback& schedulerCallback,
- std::unique_ptr<LayerHistory> layerHistory, Options options)
- : mOptions(options),
- mVsyncSchedule(std::move(schedule)),
- mLayerHistory(std::move(layerHistory)),
- mSchedulerCallback(schedulerCallback),
- mPredictedVsyncTracer(
- base::GetBoolProperty("debug.sf.show_predicted_vsync", false)
- ? std::make_unique<PredictedVsyncTracer>(*mVsyncSchedule.dispatch)
- : nullptr) {
- setRefreshRateConfigs(configs);
- mSchedulerCallback.setVsyncEnabled(false);
-}
-
Scheduler::~Scheduler() {
// Ensure the OneShotTimer threads are joined before we start destroying state.
mDisplayPowerTimer.reset();
@@ -170,27 +91,20 @@
mRefreshRateConfigs.reset();
}
-Scheduler::VsyncSchedule 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);
- return {std::move(controller), std::move(tracker), std::move(dispatch)};
+void Scheduler::run() {
+ while (true) {
+ waitMessage();
+ }
}
-std::unique_ptr<LayerHistory> Scheduler::createLayerHistory() {
- return std::make_unique<scheduler::LayerHistory>();
+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);
}
@@ -226,7 +140,7 @@
return true;
}
- return mVsyncSchedule.tracker->isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
+ return mVsyncSchedule->getTracker().isVSyncInPhase(expectedVsyncTimestamp, *frameRate);
}
impl::EventThread::ThrottleVsyncCallback Scheduler::makeThrottleVsyncCallback() const {
@@ -261,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) {
@@ -275,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);
@@ -362,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;
}
@@ -388,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) {
@@ -440,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 {};
}
@@ -486,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;
}
@@ -539,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;
}
@@ -555,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);
}
}
@@ -567,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 {
@@ -594,11 +508,11 @@
// If the content detection feature is off, we still keep the layer history,
// since we use it for other features (like Frame Rate API), so layers
// still need to be registered.
- mLayerHistory->registerLayer(layer, voteType);
+ mLayerHistory.registerLayer(layer, voteType);
}
void Scheduler::deregisterLayer(Layer* layer) {
- mLayerHistory->deregisterLayer(layer);
+ mLayerHistory.deregisterLayer(layer);
}
void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime,
@@ -608,11 +522,11 @@
if (!mRefreshRateConfigs->canSwitch()) return;
}
- mLayerHistory->record(layer, presentTime, systemTime(), updateType);
+ mLayerHistory.record(layer, presentTime, systemTime(), updateType);
}
void Scheduler::setModeChangePending(bool pending) {
- mLayerHistory->setModeChangePending(pending);
+ mLayerHistory.setModeChangePending(pending);
}
void Scheduler::chooseRefreshRateForContent() {
@@ -625,19 +539,19 @@
const auto refreshRateConfigs = holdRefreshRateConfigs();
scheduler::LayerHistory::Summary summary =
- mLayerHistory->summarize(*refreshRateConfigs, systemTime());
+ mLayerHistory.summarize(*refreshRateConfigs, systemTime());
scheduler::RefreshRateConfigs::GlobalSignals consideredSignals;
DisplayModePtr newMode;
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) {
@@ -645,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();
@@ -676,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) {
@@ -686,7 +601,7 @@
// Display Power event will boost the refresh rate to performance.
// Clear Layer History to get fresh FPS detection
- mLayerHistory->clear();
+ mLayerHistory.clear();
}
void Scheduler::kernelIdleTimerCallback(TimerState state) {
@@ -719,7 +634,7 @@
}
void Scheduler::idleTimerCallback(TimerState state) {
- handleTimerStateChanged(&mFeatures.idleTimer, state);
+ handleTimerStateChanged(&mPolicy.idleTimer, state);
ATRACE_INT("ExpiredIdleTimer", static_cast<int>(state));
}
@@ -729,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)) {
- mLayerHistory->clear();
+ 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));
}
@@ -746,8 +661,8 @@
StringAppendF(&result, "+ Touch timer: %s\n",
mTouchTimer ? mTouchTimer->dump().c_str() : "off");
StringAppendF(&result, "+ Content detection: %s %s\n\n",
- toContentDetectionString(mOptions.useContentDetection),
- mLayerHistory ? mLayerHistory->dump().c_str() : "(no layer history)");
+ mFeatures.test(Feature::kContentDetection) ? "on" : "off",
+ mLayerHistory.dump().c_str());
{
std::lock_guard lock(mFrameRateOverridesLock);
@@ -772,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(
@@ -790,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(),
@@ -813,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();
@@ -854,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) {
@@ -911,7 +819,7 @@
}
void Scheduler::onActiveDisplayAreaChanged(uint32_t displayArea) {
- mLayerHistory->setDisplayArea(displayArea);
+ mLayerHistory.setDisplayArea(displayArea);
}
void Scheduler::setPreferredRefreshRateForUid(FrameRateOverride frameRateOverride) {
@@ -931,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 8397738..818f1ed 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -18,6 +18,7 @@
#include <atomic>
#include <functional>
+#include <future>
#include <memory>
#include <mutex>
#include <optional>
@@ -30,39 +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;
@@ -70,15 +69,33 @@
~ISchedulerCallback() = default;
};
-class Scheduler {
-public:
- using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
- using ModeEvent = scheduler::RefreshRateConfigEvent;
+class Scheduler : impl::MessageQueue {
+ using Impl = impl::MessageQueue;
- Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&);
+public:
+ Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags);
~Scheduler();
- using ConnectionHandle = scheduler::ConnectionHandle;
+ void createVsyncSchedule(FeatureFlags);
+ void startTimers();
+ void run();
+
+ using Impl::initVsync;
+ using Impl::setInjector;
+
+ using Impl::getScheduledFrameTime;
+ using Impl::setDuration;
+
+ using Impl::scheduleFrame;
+
+ // Schedule an asynchronous or synchronous task on the main thread.
+ template <typename F, typename T = std::invoke_result_t<F>>
+ [[nodiscard]] std::future<T> schedule(F&& f) {
+ auto [task, future] = makeTask(std::move(f));
+ postMessage(std::move(task));
+ return std::move(future);
+ }
+
ConnectionHandle createConnection(const char* connectionName, frametimeline::TokenManager*,
std::chrono::nanoseconds workDuration,
std::chrono::nanoseconds readyDuration,
@@ -90,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);
@@ -123,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*);
@@ -143,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
@@ -182,19 +198,34 @@
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) {
- std::scoped_lock lock(mRefreshRateConfigsLock);
- mRefreshRateConfigs = std::move(refreshRateConfigs);
- mRefreshRateConfigs->setIdleTimerCallbacks(
- [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); },
- [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired); },
- [this] {
- std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset);
- },
- [this] {
- std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired);
- });
+ // 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.
+ {
+ std::scoped_lock lock(mRefreshRateConfigsLock);
+ if (mRefreshRateConfigs) mRefreshRateConfigs->stopIdleTimer();
+ }
+ {
+ std::scoped_lock lock(mPolicyLock);
+ mPolicy = {};
+ }
+ {
+ std::scoped_lock lock(mRefreshRateConfigsLock);
+ mRefreshRateConfigs = std::move(refreshRateConfigs);
+ mRefreshRateConfigs->setIdleTimerCallbacks(
+ [this] { std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Reset); },
+ [this] {
+ std::invoke(&Scheduler::idleTimerCallback, this, TimerState::Expired);
+ },
+ [this] {
+ std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Reset);
+ },
+ [this] {
+ std::invoke(&Scheduler::kernelIdleTimerCallback, this, TimerState::Expired);
+ });
+ mRefreshRateConfigs->startIdleTimer();
+ }
}
nsecs_t getVsyncPeriodFromRefreshRateConfigs() const EXCLUDES(mRefreshRateConfigsLock) {
@@ -202,38 +233,20 @@
return mRefreshRateConfigs->getCurrentRefreshRate().getVsyncPeriod();
}
+ // Returns the framerate of the layer with the given sequence ID
+ float getLayerFramerate(nsecs_t now, int32_t id) const {
+ return mLayerHistory.getLayerFramerate(now, id);
+ }
+
private:
friend class TestableScheduler;
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 Options {
- // Whether to use content detection at all.
- bool useContentDetection;
- };
-
- struct VsyncSchedule {
- std::unique_ptr<scheduler::VsyncController> controller;
- std::unique_ptr<scheduler::VSyncTracker> tracker;
- std::unique_ptr<scheduler::VSyncDispatch> dispatch;
- };
-
- // Unlike the testing constructor, this creates the VsyncSchedule, LayerHistory, and timers.
- Scheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&, Options);
-
- // Used by tests to inject mocks.
- Scheduler(VsyncSchedule, const std::shared_ptr<scheduler::RefreshRateConfigs>&,
- ISchedulerCallback&, std::unique_ptr<LayerHistory>, Options);
-
- static VsyncSchedule createVsyncSchedule(bool supportKernelIdleTimer);
- static std::unique_ptr<LayerHistory> createLayerHistory();
-
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
sp<EventThreadConnection> createConnectionInternal(
@@ -255,19 +268,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;
@@ -293,66 +304,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.
- std::unique_ptr<LayerHistory> mLayerHistory;
+ 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;
- const 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/VsyncModulator.h b/services/surfaceflinger/Scheduler/VsyncModulator.h
index b2b0451..2000c54 100644
--- a/services/surfaceflinger/Scheduler/VsyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VsyncModulator.h
@@ -25,6 +25,8 @@
#include <binder/IBinder.h>
#include <utils/Timers.h>
+#include "../WpHash.h"
+
namespace android::scheduler {
// State machine controlled by transaction flags. VsyncModulator switches to early phase offsets
@@ -124,12 +126,6 @@
using Schedule = TransactionSchedule;
std::atomic<Schedule> mTransactionSchedule = Schedule::Late;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
-
std::unordered_set<wp<IBinder>, WpHash> mEarlyWakeupRequests GUARDED_BY(mMutex);
std::atomic<bool> mRefreshRateChangePending = false;
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 8d7221c..5b78314 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>
@@ -67,6 +67,7 @@
#include <renderengine/RenderEngine.h>
#include <sys/types.h>
#include <ui/ColorSpace.h>
+#include <ui/DataspaceUtils.h>
#include <ui/DebugUtils.h>
#include <ui/DisplayId.h>
#include <ui/DisplayMode.h>
@@ -93,6 +94,7 @@
#include <type_traits>
#include <unordered_map>
+#include "BackgroundExecutor.h"
#include "BufferLayer.h"
#include "BufferQueueLayer.h"
#include "BufferStateLayer.h"
@@ -125,7 +127,6 @@
#include "Scheduler/DispSyncSource.h"
#include "Scheduler/EventThread.h"
#include "Scheduler/LayerHistory.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
@@ -170,6 +171,7 @@
using android::hardware::power::Boost;
using base::StringAppendF;
using gui::DisplayInfo;
+using gui::IDisplayEventConnection;
using gui::IWindowInfosListener;
using gui::WindowInfo;
using ui::ColorMode;
@@ -263,14 +265,6 @@
return dataspace == Dataspace::V0_SRGB || dataspace == Dataspace::DISPLAY_P3;
}
-class FrameRateFlexibilityToken : public BBinder {
-public:
- FrameRateFlexibilityToken(std::function<void()> callback) : mCallback(callback) {}
- virtual ~FrameRateFlexibilityToken() { mCallback(); }
-
-private:
- std::function<void()> mCallback;
-};
enum Permission {
ACCESS_SURFACE_FLINGER = 0x1,
@@ -339,9 +333,8 @@
ui::PixelFormat SurfaceFlinger::defaultCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
Dataspace SurfaceFlinger::wideColorGamutCompositionDataspace = Dataspace::V0_SRGB;
ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::PixelFormat::RGBA_8888;
-bool SurfaceFlinger::useFrameRateApi;
bool SurfaceFlinger::enableSdrDimming;
-bool SurfaceFlinger::enableLatchUnsignaled;
+LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig;
std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) {
switch(displayColorSetting) {
@@ -367,11 +360,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())),
- mEventQueue(mFactory.createMessageQueue(*this)),
+ mFrameTimeline(mFactory.createFrameTimeline(mTimeStats, mPid)),
mCompositionEngine(mFactory.createCompositionEngine()),
mHwcServiceName(base::GetProperty("debug.sf.hwc_service_name"s, "default"s)),
mTunnelModeEnabledReporter(new TunnelModeEnabledReporter()),
@@ -494,14 +487,28 @@
android::hardware::details::setTrebleTestingOverride(true);
}
- useFrameRateApi = use_frame_rate_api(true);
-
mRefreshRateOverlaySpinner = property_get_bool("sf.debug.show_refresh_rate_overlay_spinner", 0);
// Debug property overrides ro. property
enableSdrDimming = property_get_bool("debug.sf.enable_sdr_dimming", enable_sdr_dimming(false));
- enableLatchUnsignaled = base::GetBoolProperty("debug.sf.latch_unsignaled"s, false);
+ enableLatchUnsignaledConfig = getLatchUnsignaledConfig();
+
+ mTransactionTracingEnabled =
+ !mIsUserBuild && property_get_bool("debug.sf.enable_transaction_tracing", true);
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.enable();
+ }
+}
+
+LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() {
+ if (base::GetBoolProperty("debug.sf.latch_unsignaled"s, false)) {
+ return LatchUnsignaledConfig::Always;
+ } else if (base::GetBoolProperty("debug.sf.auto_latch_unsignaled"s, false)) {
+ return LatchUnsignaledConfig::Auto;
+ } else {
+ return LatchUnsignaledConfig::Disabled;
+ }
}
SurfaceFlinger::~SurfaceFlinger() = default;
@@ -510,8 +517,8 @@
// the window manager died on us. prepare its eulogy.
mBootFinished = false;
- // Sever the link to inputflinger since its gone as well.
- static_cast<void>(schedule([=] { mInputFlinger = nullptr; }));
+ // Sever the link to inputflinger since it's gone as well.
+ static_cast<void>(mScheduler->schedule([=] { mInputFlinger = nullptr; }));
// restore initial conditions (default device unblank, etc)
initializeDisplays();
@@ -521,16 +528,7 @@
}
void SurfaceFlinger::run() {
- while (true) {
- mEventQueue->waitMessage();
- }
-}
-
-template <typename F, typename T>
-inline std::future<T> SurfaceFlinger::schedule(F&& f) {
- auto [task, future] = makeTask(std::move(f));
- mEventQueue->postMessage(std::move(task));
- return std::move(future);
+ mScheduler->run();
}
sp<ISurfaceComposerClient> SurfaceFlinger::createConnection() {
@@ -731,7 +729,7 @@
sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
if (input == nullptr) {
ALOGE("Failed to link to input service");
} else {
@@ -772,7 +770,7 @@
if (std::this_thread::get_id() == mMainThreadId) {
return genTextures();
} else {
- return schedule(genTextures).get();
+ return mScheduler->schedule(genTextures).get();
}
}
@@ -784,6 +782,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() {
@@ -794,19 +811,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());
@@ -905,9 +924,8 @@
}
bool SurfaceFlinger::authenticateSurfaceTextureLocked(
- const sp<IGraphicBufferProducer>& bufferProducer) const {
- sp<IBinder> surfaceTextureBinder(IInterface::asBinder(bufferProducer));
- return mGraphicBufferProducerList.count(surfaceTextureBinder.get()) > 0;
+ const sp<IGraphicBufferProducer>& /* bufferProducer */) const {
+ return false;
}
status_t SurfaceFlinger::getSupportedFrameTimestamps(
@@ -1115,14 +1133,14 @@
}
}
-status_t SurfaceFlinger::setActiveMode(const sp<IBinder>& displayToken, int modeId) {
+status_t SurfaceFlinger::setActiveModeFromBackdoor(const sp<IBinder>& displayToken, int modeId) {
ATRACE_CALL();
if (!displayToken) {
return BAD_VALUE;
}
- auto future = schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set allowed display modes for invalid display token %p",
@@ -1156,7 +1174,7 @@
return future.get();
}
-void SurfaceFlinger::setActiveModeInternal() {
+void SurfaceFlinger::updateInternalStateWithChangedMode() {
ATRACE_CALL();
const auto display = getDefaultDisplayDeviceLocked();
@@ -1192,7 +1210,7 @@
mRefreshRateStats->setRefreshRate(refreshRate);
updatePhaseConfiguration(refreshRate);
- if (upcomingModeInfo.event != Scheduler::ModeEvent::None) {
+ if (upcomingModeInfo.event != DisplayModeEvent::None) {
mScheduler->onPrimaryDisplayModeChanged(mAppConnectionHandle, upcomingModeInfo.mode);
}
}
@@ -1211,9 +1229,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;
@@ -1248,8 +1267,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;
}
@@ -1260,7 +1278,7 @@
const auto displayModeAllowed =
display->refreshRateConfigs().isModeAllowed(desiredActiveMode->mode->getId());
if (!displayModeAllowed) {
- desiredActiveModeChangeDone(display);
+ clearDesiredActiveModeState(display);
continue;
}
@@ -1280,13 +1298,31 @@
}
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);
+ }
}
}
void SurfaceFlinger::disableExpensiveRendering() {
- schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() MAIN_THREAD {
ATRACE_CALL();
if (mPowerAdvisor.isUsingExpensiveRendering()) {
const auto& displays = ON_MAIN_THREAD(mDisplays);
@@ -1295,7 +1331,9 @@
mPowerAdvisor.setExpensiveRenderingExpected(display->getId(), kDisable);
}
}
- }).wait();
+ });
+
+ future.wait();
}
std::vector<ColorMode> SurfaceFlinger::getDisplayColorModes(const DisplayDevice& display) {
@@ -1334,7 +1372,7 @@
return BAD_VALUE;
}
- auto future = schedule([=]() MAIN_THREAD -> status_t {
+ auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set active color mode %s (%d) for invalid display token %p",
@@ -1369,22 +1407,24 @@
}
void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
getHwComposer().setAutoLowLatencyMode(*displayId, on);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
const auto type = on ? hal::ContentType::GAME : hal::ContentType::NONE;
getHwComposer().setContentType(*displayId, type);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
}
}));
}
@@ -1443,17 +1483,18 @@
status_t SurfaceFlinger::setDisplayContentSamplingEnabled(const sp<IBinder>& displayToken,
bool enable, uint8_t componentMask,
uint64_t maxFrames) {
- return schedule([=]() MAIN_THREAD -> status_t {
- if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
- return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
- componentMask,
- maxFrames);
- } else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
- return NAME_NOT_FOUND;
- }
- })
- .get();
+ const char* const whence = __func__;
+ auto future = mScheduler->schedule([=]() MAIN_THREAD -> status_t {
+ if (const auto displayId = getPhysicalDisplayIdLocked(displayToken)) {
+ return getHwComposer().setDisplayContentSamplingEnabled(*displayId, enable,
+ componentMask, maxFrames);
+ } else {
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ });
+
+ return future.get();
}
status_t SurfaceFlinger::getDisplayedContentSample(const sp<IBinder>& displayToken,
@@ -1495,14 +1536,15 @@
}
status_t SurfaceFlinger::enableVSyncInjections(bool enable) {
- schedule([=] {
+ auto future = mScheduler->schedule([=] {
Mutex::Autolock lock(mStateLock);
if (const auto handle = mScheduler->enableVSyncInjection(enable)) {
- mEventQueue->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
+ mScheduler->setInjector(enable ? mScheduler->getEventConnection(handle) : nullptr);
}
- }).wait();
+ });
+ future.wait();
return NO_ERROR;
}
@@ -1518,12 +1560,14 @@
status_t SurfaceFlinger::getLayerDebugInfo(std::vector<LayerDebugInfo>* outLayers) {
outLayers->clear();
- schedule([=] {
+ auto future = mScheduler->schedule([=] {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
mDrawingState.traverseInZOrder([&](Layer* layer) {
outLayers->push_back(layer->getLayerDebugInfo(display.get()));
});
- }).wait();
+ });
+
+ future.wait();
return NO_ERROR;
}
@@ -1618,7 +1662,8 @@
return BAD_VALUE;
}
- return ftl::chain(schedule([=]() MAIN_THREAD {
+ const char* const whence = __func__;
+ return ftl::chain(mScheduler->schedule([=]() MAIN_THREAD {
if (const auto display = getDisplayDeviceLocked(displayToken)) {
if (enableSdrDimming) {
display->getCompositionDisplay()
@@ -1628,7 +1673,7 @@
return getHwComposer().setDisplayBrightness(display->getPhysicalId(),
brightness.displayBrightness);
} else {
- ALOGE("%s: Invalid display token %p", __FUNCTION__, displayToken.get());
+ ALOGE("%s: Invalid display token %p", whence, displayToken.get());
return ftl::yield<status_t>(NAME_NOT_FOUND);
}
}))
@@ -1706,7 +1751,7 @@
mScheduler->resetIdleTimer();
}
mPowerAdvisor.notifyDisplayUpdateImminent();
- mEventQueue->scheduleCommit();
+ mScheduler->scheduleFrame();
}
void SurfaceFlinger::scheduleComposite(FrameHint hint) {
@@ -1720,7 +1765,7 @@
}
void SurfaceFlinger::scheduleSample() {
- static_cast<void>(schedule([this] { sample(); }));
+ static_cast<void>(mScheduler->schedule([this] { sample(); }));
}
nsecs_t SurfaceFlinger::getVsyncPeriodFromHWC() const {
@@ -1774,24 +1819,6 @@
*compositorTiming = getBE().mCompositorTiming;
}
-void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
- Scheduler::ModeEvent event) {
- const auto display = getDefaultDisplayDeviceLocked();
- if (!display || mBootStage != BootStage::FINISHED) {
- return;
- }
- ATRACE_CALL();
-
- // Don't do any updating if the current fps is the same as the new one.
- if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
- ALOGV("Skipping mode %d as it is not part of allowed modes",
- refreshRate.getModeId().value());
- return;
- }
-
- setDesiredActiveMode({refreshRate.getMode(), event});
-}
-
void SurfaceFlinger::onComposerHalHotplug(hal::HWDisplayId hwcDisplayId,
hal::Connection connection) {
const bool connected = connection == hal::Connection::CONNECTED;
@@ -1833,7 +1860,7 @@
ATRACE_CALL();
// On main thread to avoid race conditions with display power state.
- static_cast<void>(schedule([=]() MAIN_THREAD {
+ static_cast<void>(mScheduler->schedule([=]() MAIN_THREAD {
mHWCVsyncPendingState = enabled ? hal::Vsync::ENABLE : hal::Vsync::DISABLE;
if (const auto display = getDefaultDisplayDeviceLocked();
@@ -1950,14 +1977,14 @@
// fired yet just wait for the next commit.
if (mSetActiveModePending) {
if (framePending) {
- mEventQueue->scheduleCommit();
+ mScheduler->scheduleFrame();
return false;
}
// 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) {
@@ -1968,7 +1995,7 @@
}
if (mTracingEnabledChanged) {
- mTracingEnabled = mTracing.isEnabled();
+ mLayerTracingEnabled = mLayerTracing.isEnabled();
mTracingEnabledChanged = false;
}
@@ -1982,24 +2009,37 @@
// Composite if transactions were committed, or if requested by HWC.
bool mustComposite = mMustComposite.exchange(false);
{
- mTracePostComposition = mTracing.flagIsSet(SurfaceTracing::TRACE_COMPOSITION) ||
- mTracing.flagIsSet(SurfaceTracing::TRACE_SYNC) ||
- mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS);
- const bool tracePreComposition = mTracingEnabled && !mTracePostComposition;
- ConditionalLockGuard<std::mutex> lock(mTracingLock, tracePreComposition);
-
mFrameTimeline->setSfWakeUp(vsyncId, frameTime, Fps::fromPeriodNsecs(stats.vsyncPeriod));
- mustComposite |= flushAndCommitTransactions();
+ bool needsTraversal = false;
+ if (clearTransactionFlags(eTransactionFlushNeeded)) {
+ needsTraversal = flushTransactionQueues(vsyncId);
+ }
+
+ const bool shouldCommit =
+ (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
+ if (shouldCommit) {
+ commitTransactions();
+ }
+
+ if (transactionFlushNeeded()) {
+ setTransactionFlags(eTransactionFlushNeeded);
+ }
+
+ mustComposite |= shouldCommit;
mustComposite |= latchBuffers();
- updateLayerGeometry();
-
- if (tracePreComposition) {
- if (mVisibleRegionsDirty) {
- mTracing.notifyLocked("visibleRegionsDirty");
- }
+ // This has to be called after latchBuffers because we want to include the layers that have
+ // been latched in the commit callback
+ if (!needsTraversal) {
+ // Invoke empty transaction callbacks early.
+ mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
+ } else {
+ // Invoke OnCommit callbacks.
+ mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
}
+
+ updateLayerGeometry();
}
// Layers need to get updated (in the previous line) before we can use them for
@@ -2011,7 +2051,7 @@
mScheduler->chooseRefreshRateForContent();
}
- ON_MAIN_THREAD(performSetActiveMode());
+ ON_MAIN_THREAD(setActiveModeInHwcIfNeeded());
updateCursorAsync();
updateInputFlinger();
@@ -2019,34 +2059,6 @@
return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}
-bool SurfaceFlinger::flushAndCommitTransactions() {
- ATRACE_CALL();
-
- bool needsTraversal = false;
- if (clearTransactionFlags(eTransactionFlushNeeded)) {
- needsTraversal = flushTransactionQueues();
- }
-
- const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
- if (shouldCommit) {
- commitTransactions();
- }
-
- if (!needsTraversal) {
- // Invoke empty transaction callbacks early.
- mTransactionCallbackInvoker.sendCallbacks(false /* onCommitOnly */);
- } else {
- // Invoke OnCommit callbacks.
- mTransactionCallbackInvoker.sendCallbacks(true /* onCommitOnly */);
- }
-
- if (transactionFlushNeeded()) {
- setTransactionFlags(eTransactionFlushNeeded);
- }
-
- return shouldCommit;
-}
-
void SurfaceFlinger::composite(nsecs_t frameTime) {
ATRACE_CALL();
@@ -2089,11 +2101,13 @@
refreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::milliseconds(mDebugFlashDelay);
}
- const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(mExpectedPresentTime);
+ const auto expectedPresentTime = mExpectedPresentTime.load();
+ const auto prevVsyncTime = mScheduler->getPreviousVsyncFrom(expectedPresentTime);
const auto hwcMinWorkDuration = mVsyncConfiguration->getCurrentConfigs().hwcMinWorkDuration;
refreshArgs.earliestPresentTime = prevVsyncTime - hwcMinWorkDuration;
refreshArgs.previousPresentFence = mPreviousPresentFences[0].fenceTime;
- refreshArgs.scheduledFrameTime = mEventQueue->getScheduledFrameTime();
+ refreshArgs.scheduledFrameTime = mScheduler->getScheduledFrameTime();
+ refreshArgs.expectedPresentTime = expectedPresentTime;
// Store the present time just before calling to the composition engine so we could notify
// the scheduler.
@@ -2132,12 +2146,12 @@
modulateVsync(&VsyncModulator::onDisplayRefresh, usedGpuComposition);
mLayersWithQueuedFrames.clear();
- if (mTracingEnabled && mTracePostComposition) {
- // This may block if SurfaceTracing is running in sync mode.
+ if (mLayerTracingEnabled) {
+ // This will block and should only be used for debugging.
if (mVisibleRegionsDirty) {
- mTracing.notify("visibleRegionsDirty");
- } else if (mTracing.flagIsSet(SurfaceTracing::TRACE_BUFFERS)) {
- mTracing.notify("bufferLatched");
+ mLayerTracing.notify("visibleRegionsDirty");
+ } else if (mLayerTracing.flagIsSet(LayerTracing::TRACE_BUFFERS)) {
+ mLayerTracing.notify("bufferLatched");
}
}
@@ -2306,12 +2320,7 @@
mDrawingState.traverse([&, compositionDisplay = compositionDisplay](Layer* layer) {
const auto layerFe = layer->getCompositionEngineLayerFE();
if (layer->isVisible() && compositionDisplay->includesLayer(layerFe)) {
- const Dataspace transfer =
- static_cast<Dataspace>(layer->getDataSpace() & Dataspace::TRANSFER_MASK);
- const bool isHdr = (transfer == Dataspace::TRANSFER_ST2084 ||
- transfer == Dataspace::TRANSFER_HLG);
-
- if (isHdr) {
+ if (isHdrDataspace(layer->getDataSpace())) {
const auto* outputLayer =
compositionDisplay->getOutputLayerForLayer(layerFe);
if (outputLayer) {
@@ -2428,7 +2437,7 @@
}
}
-void SurfaceFlinger::computeLayerBounds() {
+FloatRect SurfaceFlinger::getMaxDisplayBounds() {
// Find the largest width and height among all the displays.
int32_t maxDisplayWidth = 0;
int32_t maxDisplayHeight = 0;
@@ -2446,8 +2455,13 @@
// Ignore display bounds for now since they will be computed later. Use a large Rect bound
// to ensure it's bigger than an actual display will be.
- FloatRect maxBounds(-maxDisplayWidth * 10, -maxDisplayHeight * 10, maxDisplayWidth * 10,
- maxDisplayHeight * 10);
+ FloatRect maxBounds = FloatRect(-maxDisplayWidth * 10, -maxDisplayHeight * 10,
+ maxDisplayWidth * 10, maxDisplayHeight * 10);
+ return maxBounds;
+}
+
+void SurfaceFlinger::computeLayerBounds() {
+ FloatRect maxBounds = getMaxDisplayBounds();
for (const auto& layer : mDrawingState.layersSortedByZ) {
layer->computeBounds(maxBounds, ui::Transform(), 0.f /* shadowRadius */);
}
@@ -2815,7 +2829,7 @@
mDisplays.erase(displayToken);
if (display && display->isVirtual()) {
- static_cast<void>(schedule([display = std::move(display)] {
+ static_cast<void>(mScheduler->schedule([display = std::move(display)] {
// Destroy the display without holding the mStateLock.
// This is a temporary solution until we can manage transaction queues without
// holding the mStateLock.
@@ -3040,68 +3054,77 @@
return;
}
+ std::vector<WindowInfo> windowInfos;
+ std::vector<DisplayInfo> displayInfos;
+ bool updateWindowInfo = false;
if (mVisibleRegionsDirty || mInputInfoChanged) {
mInputInfoChanged = false;
- notifyWindowInfos();
- } else if (mInputWindowCommands.syncInputWindows) {
- // If the caller requested to sync input windows, but there are no
- // changes to input windows, notify immediately.
- windowInfosReported();
+ updateWindowInfo = true;
+ buildWindowInfos(windowInfos, displayInfos);
}
+ if (!updateWindowInfo && mInputWindowCommands.empty()) {
+ return;
+ }
+ BackgroundExecutor::getInstance().execute([updateWindowInfo,
+ windowInfos = std::move(windowInfos),
+ displayInfos = std::move(displayInfos),
+ inputWindowCommands =
+ std::move(mInputWindowCommands),
+ inputFlinger = mInputFlinger, this]() {
+ ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
+ if (updateWindowInfo) {
+ mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
+ inputWindowCommands.syncInputWindows);
+ } else if (inputWindowCommands.syncInputWindows) {
+ // If the caller requested to sync input windows, but there are no
+ // changes to input windows, notify immediately.
+ windowInfosReported();
+ }
+ for (const auto& focusRequest : inputWindowCommands.focusRequests) {
+ inputFlinger->setFocusedWindow(focusRequest);
+ }
+ });
- for (const auto& focusRequest : mInputWindowCommands.focusRequests) {
- mInputFlinger->setFocusedWindow(focusRequest);
- }
mInputWindowCommands.clear();
}
-bool enablePerWindowInputRotation() {
- static bool value =
- android::base::GetBoolProperty("persist.debug.per_window_input_rotation", true);
- return value;
-}
-
-void SurfaceFlinger::notifyWindowInfos() {
- std::vector<WindowInfo> windowInfos;
- std::vector<DisplayInfo> displayInfos;
- std::unordered_map<uint32_t /*layerStackId*/, const ui::Transform> displayTransforms;
-
- if (enablePerWindowInputRotation()) {
- for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
- if (!display->receivesInput()) {
- continue;
- }
- const uint32_t layerStackId = display->getLayerStack().id;
- const auto& [info, transform] = display->getInputInfo();
- const auto& [it, emplaced] = displayTransforms.try_emplace(layerStackId, transform);
- if (!emplaced) {
- ALOGE("Multiple displays claim to accept input for the same layer stack: %u",
- layerStackId);
- continue;
- }
- displayInfos.emplace_back(info);
+void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
+ std::vector<DisplayInfo>& outDisplayInfos) {
+ std::unordered_map<uint32_t /*layerStackId*/,
+ std::pair<bool /* isSecure */, const ui::Transform>>
+ inputDisplayDetails;
+ for (const auto& [_, display] : ON_MAIN_THREAD(mDisplays)) {
+ if (!display->receivesInput()) {
+ continue;
}
+ const uint32_t layerStackId = display->getLayerStack().id;
+ const auto& [info, transform] = display->getInputInfo();
+ const auto& [it, emplaced] =
+ inputDisplayDetails.try_emplace(layerStackId, display->isSecure(), transform);
+ if (!emplaced) {
+ ALOGE("Multiple displays claim to accept input for the same layer stack: %u",
+ layerStackId);
+ continue;
+ }
+ outDisplayInfos.emplace_back(info);
}
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
if (!layer->needsInputInfo()) return;
- const DisplayDevice* display = ON_MAIN_THREAD(getDisplayWithInputByLayer(layer)).get();
+ bool isSecure = true;
ui::Transform displayTransform = ui::Transform();
- if (enablePerWindowInputRotation() && display != nullptr) {
- // When calculating the screen bounds we ignore the transparent region since it may
- // result in an unwanted offset.
- const auto it = displayTransforms.find(display->getLayerStack().id);
- if (it != displayTransforms.end()) {
- displayTransform = it->second;
- }
+ const uint32_t layerStackId = layer->getLayerStack().id;
+ const auto it = inputDisplayDetails.find(layerStackId);
+ if (it != inputDisplayDetails.end()) {
+ const auto& [secure, transform] = it->second;
+ isSecure = secure;
+ displayTransform = transform;
}
- const bool displayIsSecure = !display || display->isSecure();
- windowInfos.push_back(layer->fillInputInfo(displayTransform, displayIsSecure));
+
+ outWindowInfos.push_back(layer->fillInputInfo(displayTransform, isSecure));
});
- mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos,
- mInputWindowCommands.syncInputWindows);
}
void SurfaceFlinger::updateCursorAsync() {
@@ -3115,13 +3138,27 @@
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
ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId);
- changeRefreshRateLocked(refreshRate, event);
+
+ const auto display = getDefaultDisplayDeviceLocked();
+ if (!display || mBootStage != BootStage::FINISHED) {
+ return;
+ }
+ ATRACE_CALL();
+
+ // Don't do any updating if the current fps is the same as the new one.
+ if (!display->refreshRateConfigs().isModeAllowed(refreshRate.getModeId())) {
+ ALOGV("Skipping mode %d as it is not part of allowed modes",
+ refreshRate.getModeId().value());
+ return;
+ }
+
+ setDesiredActiveMode({refreshRate.getMode(), event});
}
void SurfaceFlinger::triggerOnFrameRateOverridesChanged() {
@@ -3149,8 +3186,35 @@
mVsyncConfiguration = getFactory().createVsyncConfiguration(currRefreshRate);
mVsyncModulator = sp<VsyncModulator>::make(mVsyncConfiguration->getCurrentConfigs());
- // start the EventThread
- mScheduler = getFactory().createScheduler(display->holdRefreshRateConfigs(), *this);
+ using Feature = scheduler::Feature;
+ scheduler::FeatureFlags features;
+
+ 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();
+
const auto configs = mVsyncConfiguration->getCurrentConfigs();
const nsecs_t vsyncPeriod = currRefreshRate.getPeriodNsecs();
mAppConnectionHandle =
@@ -3166,8 +3230,8 @@
mInterceptor->saveVSyncEvent(timestamp);
});
- mEventQueue->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
- configs.late.sfWorkDuration);
+ mScheduler->initVsync(mScheduler->getVsyncDispatch(), *mFrameTimeline->getTokenManager(),
+ configs.late.sfWorkDuration);
mRegionSamplingThread =
new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
@@ -3180,11 +3244,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) {
@@ -3201,7 +3260,7 @@
mScheduler->setDuration(mSfConnectionHandle,
/*workDuration=*/std::chrono::nanoseconds(vsyncPeriod),
/*readyDuration=*/config.sfWorkDuration);
- mEventQueue->setDuration(config.sfWorkDuration);
+ mScheduler->setDuration(config.sfWorkDuration);
}
void SurfaceFlinger::doCommitTransactions() {
@@ -3355,21 +3414,15 @@
}
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
- const sp<IBinder>& parentHandle,
- const sp<Layer>& parentLayer, bool addToRoot,
- uint32_t* outTransformHint) {
+ const sp<Layer>& lbc, const wp<Layer>& parent,
+ bool addToRoot, uint32_t* outTransformHint) {
if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
ISurfaceComposer::MAX_LAYERS);
return NO_MEMORY;
}
- wp<IBinder> initialProducer;
- if (gbc != nullptr) {
- initialProducer = IInterface::asBinder(gbc);
- }
- setLayerCreatedState(handle, lbc, parentHandle, parentLayer, initialProducer, addToRoot);
+ setLayerCreatedState(handle, lbc, parent, addToRoot);
// Create a transaction includes the initial parent and producer.
Vector<ComposerState> states;
@@ -3385,19 +3438,15 @@
*outTransformHint = mActiveDisplayTransformHint;
}
// attach this layer to the client
- client->attachLayer(handle, lbc);
+ if (client != nullptr) {
+ 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 */);
-}
-
-void SurfaceFlinger::removeGraphicBufferProducerAsync(const wp<IBinder>& binder) {
- static_cast<void>(schedule([=] {
- Mutex::Autolock lock(mStateLock);
- mGraphicBufferProducerList.erase(binder);
- }));
+ transactionId);
}
uint32_t SurfaceFlinger::getTransactionFlags() const {
@@ -3420,30 +3469,35 @@
return old;
}
-bool SurfaceFlinger::flushTransactionQueues() {
- bool needsTraversal = false;
+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
- std::vector<const TransactionState> transactions;
+ std::vector<TransactionState> transactions;
// Layer handles that have transactions with buffers that are ready to be applied.
std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>> bufferLayersReadyToPresent;
{
Mutex::Autolock _l(mStateLock);
{
Mutex::Autolock _l(mQueueLock);
+ // allowLatchUnsignaled acts as a filter condition when latch unsignaled is either auto
+ // or always. auto: in this case we let buffer latch unsignaled if we have only one
+ // applyToken and if only first transaction is latch unsignaled. If more than one
+ // applyToken we don't latch unsignaled.
+ bool allowLatchUnsignaled = allowedLatchUnsignaled();
+ bool isFirstUnsignaledTransactionApplied = false;
// Collect transactions from pending transaction queue.
auto it = mPendingTransactionQueues.begin();
while (it != mPendingTransactionQueues.end()) {
auto& [applyToken, transactionQueue] = *it;
-
while (!transactionQueue.empty()) {
auto& transaction = transactionQueue.front();
if (!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- bufferLayersReadyToPresent)) {
+ bufferLayersReadyToPresent,
+ allowLatchUnsignaled)) {
setTransactionFlags(eTransactionFlushNeeded);
break;
}
@@ -3452,6 +3506,14 @@
});
transactions.emplace_back(std::move(transaction));
transactionQueue.pop();
+ if (allowLatchUnsignaled &&
+ enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+ // if allowLatchUnsignaled && we are in LatchUnsignaledConfig::Auto
+ // then we should have only one applyToken for processing.
+ // so we can stop further transactions on this applyToken.
+ isFirstUnsignaledTransactionApplied = true;
+ break;
+ }
}
if (transactionQueue.empty()) {
@@ -3463,52 +3525,123 @@
}
// Collect transactions from current transaction queue or queue to pending transactions.
- // Case 1: push to pending when transactionIsReadyToBeApplied is false.
+ // Case 1: push to pending when transactionIsReadyToBeApplied is false
+ // or the first transaction was unsignaled.
// Case 2: push to pending when there exist a pending queue.
- // Case 3: others are ready to apply.
+ // Case 3: others are the transactions that are ready to apply.
while (!mTransactionQueue.empty()) {
auto& transaction = mTransactionQueue.front();
bool pendingTransactions = mPendingTransactionQueues.find(transaction.applyToken) !=
mPendingTransactionQueues.end();
- if (pendingTransactions ||
+ if (isFirstUnsignaledTransactionApplied || pendingTransactions ||
!transactionIsReadyToBeApplied(transaction.frameTimelineInfo,
transaction.isAutoTimestamp,
transaction.desiredPresentTime,
transaction.originUid, transaction.states,
- bufferLayersReadyToPresent)) {
+ bufferLayersReadyToPresent,
+ allowLatchUnsignaled)) {
mPendingTransactionQueues[transaction.applyToken].push(std::move(transaction));
} else {
transaction.traverseStatesWithBuffers([&](const layer_state_t& state) {
bufferLayersReadyToPresent.insert(state.surface);
});
transactions.emplace_back(std::move(transaction));
+ if (allowLatchUnsignaled &&
+ enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto) {
+ isFirstUnsignaledTransactionApplied = true;
+ }
}
- mTransactionQueue.pop();
+ mTransactionQueue.pop_front();
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
}
- }
- // Now apply all transactions.
- for (const auto& transaction : transactions) {
- needsTraversal |=
- applyTransactionState(transaction.frameTimelineInfo, transaction.states,
- transaction.displays, transaction.flags,
- transaction.inputWindowCommands,
- transaction.desiredPresentTime,
- transaction.isAutoTimestamp, transaction.buffer,
- transaction.postTime, transaction.permissions,
- transaction.hasListenerCallbacks,
- transaction.listenerCallbacks, transaction.originPid,
- transaction.originUid, transaction.id);
- if (transaction.transactionCommittedSignal) {
- mTransactionCommittedSignals.emplace_back(
- std::move(transaction.transactionCommittedSignal));
- }
+ return applyTransactions(transactions, vsyncId);
}
- } // unlock mStateLock
+ }
+}
+
+bool SurfaceFlinger::applyTransactions(std::vector<TransactionState>& transactions,
+ int64_t vsyncId) {
+ bool needsTraversal = false;
+ // Now apply all transactions.
+ for (const auto& transaction : transactions) {
+ needsTraversal |=
+ applyTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.buffer, transaction.postTime,
+ transaction.permissions, transaction.hasListenerCallbacks,
+ transaction.listenerCallbacks, transaction.originPid,
+ transaction.originUid, transaction.id);
+ if (transaction.transactionCommittedSignal) {
+ mTransactionCommittedSignals.emplace_back(
+ std::move(transaction.transactionCommittedSignal));
+ }
+ }
+
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.addCommittedTransactions(transactions, vsyncId);
+ }
return needsTraversal;
}
+bool SurfaceFlinger::allowedLatchUnsignaled() {
+ if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Disabled) {
+ return false;
+ }
+ // Always mode matches the current latch unsignaled behavior.
+ // This behavior is currently used by the partners and we would like
+ // to keep it until we are completely migrated to Auto mode successfully
+ // and we we have our fallback based implementation in place.
+ if (enableLatchUnsignaledConfig == LatchUnsignaledConfig::Always) {
+ return true;
+ }
+
+ // if enableLatchUnsignaledConfig == LatchUnsignaledConfig::Auto
+ // we don't latch unsignaled if more than one applyToken, as it can backpressure
+ // the other transactions.
+ if (mPendingTransactionQueues.size() > 1) {
+ return false;
+ }
+ std::optional<sp<IBinder>> applyToken = std::nullopt;
+ bool isPendingTransactionQueuesItem = false;
+ if (!mPendingTransactionQueues.empty()) {
+ applyToken = mPendingTransactionQueues.begin()->first;
+ isPendingTransactionQueuesItem = true;
+ }
+
+ for (const auto& item : mTransactionQueue) {
+ if (!applyToken.has_value()) {
+ applyToken = item.applyToken;
+ } else if (applyToken.has_value() && applyToken != item.applyToken) {
+ return false;
+ }
+ }
+
+ if (isPendingTransactionQueuesItem) {
+ return checkTransactionCanLatchUnsignaled(
+ mPendingTransactionQueues.begin()->second.front());
+ } else if (applyToken.has_value()) {
+ return checkTransactionCanLatchUnsignaled((mTransactionQueue.front()));
+ }
+ return false;
+}
+
+bool SurfaceFlinger::checkTransactionCanLatchUnsignaled(const TransactionState& transaction) {
+ if (transaction.states.size() == 1) {
+ const auto& state = transaction.states.begin()->state;
+ if ((state.flags & ~layer_state_t::eBufferChanged) == 0 &&
+ state.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged) &&
+ state.bufferData.acquireFence &&
+ state.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
+ ATRACE_NAME("transactionCanLatchUnsignaled");
+ return true;
+ }
+ }
+ return false;
+}
+
bool SurfaceFlinger::transactionFlushNeeded() {
Mutex::Autolock _l(mQueueLock);
return !mPendingTransactionQueues.empty() || !mTransactionQueue.empty();
@@ -3540,8 +3673,9 @@
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- bufferLayersReadyToPresent) const {
- ATRACE_CALL();
+ bufferLayersReadyToPresent,
+ bool allowLatchUnsignaled) const {
+ ATRACE_FORMAT("transactionIsReadyToBeApplied vsyncId: %" PRId64, info.vsyncId);
const nsecs_t expectedPresentTime = mExpectedPresentTime.load();
// Do not present if the desiredPresentTime has not passed unless it is more than one second
// in the future. We ignore timestamps more than 1 second in the future for stability reasons.
@@ -3567,7 +3701,7 @@
const layer_state_t& s = state.state;
const bool acquireFenceChanged =
s.bufferData.flags.test(BufferData::BufferDataChange::fenceChanged);
- if (acquireFenceChanged && s.bufferData.acquireFence && !enableLatchUnsignaled &&
+ if (acquireFenceChanged && s.bufferData.acquireFence && !allowLatchUnsignaled &&
s.bufferData.acquireFence->getStatus() == Fence::Status::Unsignaled) {
ATRACE_NAME("fence unsignaled");
return false;
@@ -3628,7 +3762,7 @@
: CountDownLatch::eSyncTransaction));
}
- mTransactionQueue.emplace(state);
+ mTransactionQueue.emplace_back(state);
ATRACE_INT("TransactionQueue", mTransactionQueue.size());
const auto schedule = [](uint32_t flags) {
@@ -3702,6 +3836,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.
@@ -3739,10 +3877,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);
}
}
}
@@ -4079,13 +4218,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) {
@@ -4200,25 +4340,22 @@
return hasChanges ? eTraversalNeeded : 0;
}
-status_t SurfaceFlinger::mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
- sp<IBinder>* outHandle, int32_t* outLayerId) {
+status_t SurfaceFlinger::mirrorLayer(const LayerCreationArgs& args,
+ const sp<IBinder>& mirrorFromHandle, sp<IBinder>* outHandle,
+ int32_t* outLayerId) {
if (!mirrorFromHandle) {
return NAME_NOT_FOUND;
}
sp<Layer> mirrorLayer;
sp<Layer> mirrorFrom;
- std::string layerName = "MirrorRoot";
-
{
Mutex::Autolock _l(mStateLock);
mirrorFrom = fromHandle(mirrorFromHandle).promote();
if (!mirrorFrom) {
return NAME_NOT_FOUND;
}
-
- status_t result = createContainerLayer(client, std::move(layerName), -1, -1, 0,
- LayerMetadata(), outHandle, &mirrorLayer);
+ status_t result = createContainerLayer(args, outHandle, &mirrorLayer);
if (result != NO_ERROR) {
return result;
}
@@ -4227,22 +4364,17 @@
}
*outLayerId = mirrorLayer->sequence;
- return addClientLayer(client, *outHandle, nullptr, mirrorLayer, nullptr, nullptr, false,
- nullptr /* outTransformHint */);
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onMirrorLayerAdded((*outHandle)->localBinder(), mirrorLayer->sequence,
+ args.name, mirrorFrom->sequence);
+ }
+ return addClientLayer(args.client, *outHandle, mirrorLayer /* layer */, nullptr /* parent */,
+ false /* addAsRoot */, nullptr /* outTransformHint */);
}
-status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
- uint32_t h, PixelFormat format, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
- sp<IGraphicBufferProducer>* gbp,
+status_t SurfaceFlinger::createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
const sp<IBinder>& parentHandle, int32_t* outLayerId,
const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
- if (int32_t(w|h) < 0) {
- ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
-
ALOG_ASSERT(parentLayer == nullptr || parentHandle == nullptr,
"Expected only one of parentLayer or parentHandle to be non-null. "
"Programmer error?");
@@ -4251,40 +4383,22 @@
sp<Layer> layer;
- std::string layerName{name.string()};
-
- switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
+ switch (args.flags & ISurfaceComposerClient::eFXSurfaceMask) {
case ISurfaceComposerClient::eFXSurfaceBufferQueue:
case ISurfaceComposerClient::eFXSurfaceBufferState: {
- result = createBufferStateLayer(client, std::move(layerName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createBufferStateLayer(args, outHandle, &layer);
std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
if (pendingBufferCounter) {
std::string counterName = layer->getPendingBufferCounterName();
- mBufferCountTracker.add((*handle)->localBinder(), counterName,
+ mBufferCountTracker.add((*outHandle)->localBinder(), counterName,
pendingBufferCounter);
}
} break;
case ISurfaceComposerClient::eFXSurfaceEffect:
- // check if buffer size is set for color layer.
- if (w > 0 || h > 0) {
- ALOGE("createLayer() failed, w or h cannot be set for color layer (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
-
- result = createEffectLayer(client, std::move(layerName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createEffectLayer(args, outHandle, &layer);
break;
case ISurfaceComposerClient::eFXSurfaceContainer:
- // check if buffer size is set for container layer.
- if (w > 0 || h > 0) {
- ALOGE("createLayer() failed, w or h cannot be set for container layer (w=%d, h=%d)",
- int(w), int(h));
- return BAD_VALUE;
- }
- result = createContainerLayer(client, std::move(layerName), w, h, flags,
- std::move(metadata), handle, &layer);
+ result = createContainerLayer(args, outHandle, &layer);
break;
default:
result = BAD_VALUE;
@@ -4296,8 +4410,28 @@
}
bool addToRoot = callingThreadHasUnscopedSurfaceFlingerAccess();
- result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer, addToRoot,
- outTransformHint);
+ wp<Layer> parent(parentHandle != nullptr ? fromHandle(parentHandle) : parentLayer);
+ if (parentHandle != nullptr && parent == nullptr) {
+ ALOGE("Invalid parent handle %p.", parentHandle.get());
+ addToRoot = false;
+ }
+ if (parentLayer != nullptr) {
+ addToRoot = false;
+ }
+
+ 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();
+ }
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onLayerAdded((*outHandle)->localBinder(), layer->sequence, args.name,
+ args.flags, parentId);
+ }
+
+ result = addClientLayer(args.client, *outHandle, layer, parent, addToRoot, outTransformHint);
if (result != NO_ERROR) {
return result;
}
@@ -4307,9 +4441,7 @@
return result;
}
-status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, PixelFormat& format,
+status_t SurfaceFlinger::createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
sp<Layer>* outLayer) {
@@ -4325,7 +4457,6 @@
}
sp<BufferQueueLayer> layer;
- LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.textureName = getNewTexture();
{
// Grab the SF state lock during this since it's the only safe way to access
@@ -4335,7 +4466,7 @@
layer = getFactory().createBufferQueueLayer(args);
}
- status_t err = layer->setDefaultBufferProperties(w, h, format);
+ status_t err = layer->setDefaultBufferProperties(0, 0, format);
if (err == NO_ERROR) {
*handle = layer->getHandle();
*gbp = layer->getProducer();
@@ -4346,34 +4477,24 @@
return err;
}
-status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- LayerCreationArgs args(this, client, std::move(name), w, h, flags, std::move(metadata));
args.textureName = getNewTexture();
- sp<BufferStateLayer> layer = getFactory().createBufferStateLayer(args);
- *handle = layer->getHandle();
- *outLayer = layer;
-
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::createEffectLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* handle, sp<Layer>* outLayer) {
- *outLayer = getFactory().createEffectLayer(
- {this, client, std::move(name), w, h, flags, std::move(metadata)});
+ *outLayer = getFactory().createBufferStateLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
-status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, std::string name,
- uint32_t w, uint32_t h, uint32_t flags,
- LayerMetadata metadata, sp<IBinder>* handle,
+status_t SurfaceFlinger::createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
+ sp<Layer>* outLayer) {
+ *outLayer = getFactory().createEffectLayer(args);
+ *handle = (*outLayer)->getHandle();
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* handle,
sp<Layer>* outLayer) {
- *outLayer = getFactory().createContainerLayer(
- {this, client, std::move(name), w, h, flags, std::move(metadata)});
+ *outLayer = getFactory().createContainerLayer(args);
*handle = (*outLayer)->getHandle();
return NO_ERROR;
}
@@ -4397,6 +4518,9 @@
markLayerPendingRemovalLocked(layer);
mBufferCountTracker.remove(handle);
layer.clear();
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onHandleRemoved(handle);
+ }
}
// ---------------------------------------------------------------------------
@@ -4424,10 +4548,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 =
@@ -4442,26 +4568,7 @@
void SurfaceFlinger::initializeDisplays() {
// Async since we may be called from the main thread.
- static_cast<void>(schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
-}
-
-sp<DisplayDevice> SurfaceFlinger::getDisplayWithInputByLayer(Layer* layer) const {
- const auto filter = layer->getOutputFilter();
- sp<DisplayDevice> inputDisplay;
-
- for (const auto& [_, display] : mDisplays) {
- if (!display->receivesInput() || !display->getCompositionDisplay()->includesLayer(filter)) {
- continue;
- }
- // Don't return immediately so that we can log duplicates.
- if (inputDisplay) {
- ALOGE("Multiple displays claim to accept input for the same layer stack: %u",
- filter.layerStack.id);
- continue;
- }
- inputDisplay = display;
- }
- return inputDisplay;
+ static_cast<void>(mScheduler->schedule([this]() MAIN_THREAD { onInitializeDisplays(); }));
}
void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal::PowerMode mode) {
@@ -4561,7 +4668,7 @@
}
void SurfaceFlinger::setPowerMode(const sp<IBinder>& displayToken, int mode) {
- schedule([=]() MAIN_THREAD {
+ auto future = mScheduler->schedule([=]() MAIN_THREAD {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
ALOGE("Attempt to set power mode %d for invalid display token %p", mode,
@@ -4571,7 +4678,9 @@
} else {
setPowerModeInternal(display, static_cast<hal::PowerMode>(mode));
}
- }).wait();
+ });
+
+ future.wait();
}
status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) {
@@ -4621,7 +4730,7 @@
}
if (dumpLayers) {
- LayersTraceFileProto traceFileProto = SurfaceTracing::createLayersTraceFileProto();
+ LayersTraceFileProto traceFileProto = mLayerTracing.createTraceFileProto();
LayersTraceProto* layersTrace = traceFileProto.add_entry();
LayersProto layersProto = dumpProtoFromMainThread();
layersTrace->mutable_layers()->Swap(&layersProto);
@@ -4643,8 +4752,9 @@
}
status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
- if (asProto && mTracing.isEnabled()) {
- mTracing.writeToFile();
+ if (asProto) {
+ mLayerTracing.writeToFile();
+ mTransactionTracing.writeToFile();
}
return doDump(fd, DumpArgs(), asProto);
@@ -4841,7 +4951,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;
@@ -4865,6 +4974,7 @@
});
LayerProtoHelper::writeTransformToProto(display->getTransform(),
displayProto->mutable_transform());
+ displayProto->set_is_virtual(display->isVirtual());
}
}
@@ -4893,21 +5003,21 @@
}
LayersProto SurfaceFlinger::dumpProtoFromMainThread(uint32_t traceFlags) {
- return schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
+ return mScheduler->schedule([=] { return dumpDrawingStateProto(traceFlags); }).get();
}
void SurfaceFlinger::dumpOffscreenLayers(std::string& result) {
+ auto future = mScheduler->schedule([this] {
+ std::string result;
+ for (Layer* offscreenLayer : mOffscreenLayers) {
+ offscreenLayer->traverse(LayerVector::StateSet::Drawing,
+ [&](Layer* layer) { layer->dumpCallingUidPid(result); });
+ }
+ return result;
+ });
+
result.append("Offscreen Layers:\n");
- result.append(schedule([this] {
- std::string result;
- for (Layer* offscreenLayer : mOffscreenLayers) {
- offscreenLayer->traverse(LayerVector::StateSet::Drawing,
- [&](Layer* layer) {
- layer->dumpCallingUidPid(result);
- });
- }
- return result;
- }).get());
+ result.append(future.get());
}
void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const {
@@ -4959,8 +5069,6 @@
*/
colorizer.bold(result);
StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load());
- StringAppendF(&result, "GraphicBufferProducers: %zu, max %zu\n",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize);
colorizer.reset(result);
{
@@ -5042,7 +5150,9 @@
/*
* Tracing state
*/
- mTracing.dump(result);
+ mLayerTracing.dump(result);
+ result.append("\n");
+ mTransactionTracing.dump(result);
result.append("\n");
/*
@@ -5154,11 +5264,9 @@
case SET_GLOBAL_SHADOW_SETTINGS:
case GET_PRIMARY_PHYSICAL_DISPLAY_ID:
case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
- // ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN and OVERRIDE_HDR_TYPES are used by CTS tests,
- // which acquire the necessary permission dynamically. Don't use the permission cache
- // for this check.
- bool usePermissionCache =
- code != ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN && code != OVERRIDE_HDR_TYPES;
+ // OVERRIDE_HDR_TYPES is used by CTS tests, which acquire the necessary
+ // permission dynamically. Don't use the permission cache for this check.
+ bool usePermissionCache = code != OVERRIDE_HDR_TYPES;
if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5278,9 +5386,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;
}
@@ -5321,6 +5429,7 @@
scheduleRepaint();
return NO_ERROR;
case 1004: // Force composite ahead of next VSYNC.
+ case 1006:
scheduleComposite(FrameHint::kActive);
return NO_ERROR;
case 1005: { // Force commit ahead of next VSYNC.
@@ -5329,9 +5438,6 @@
eTraversalNeeded);
return NO_ERROR;
}
- case 1006: // Force composite immediately.
- mEventQueue->scheduleComposite();
- return NO_ERROR;
case 1007: // Unused.
return NAME_NOT_FOUND;
case 1008: // Toggle forced GPU composition.
@@ -5442,7 +5548,8 @@
}
case 1021: { // Disable HWC virtual displays
const bool enable = data.readInt32() != 0;
- static_cast<void>(schedule([this, enable] { enableHalVirtualDisplays(enable); }));
+ static_cast<void>(
+ mScheduler->schedule([this, enable] { enableHalVirtualDisplays(enable); }));
return NO_ERROR;
}
case 1022: { // Set saturation boost
@@ -5471,20 +5578,21 @@
bool tracingEnabledChanged;
if (n) {
ALOGD("LayerTracing enabled");
- tracingEnabledChanged = mTracing.enable();
+ tracingEnabledChanged = mLayerTracing.enable();
if (tracingEnabledChanged) {
- schedule([&]() MAIN_THREAD { mTracing.notify("start"); }).wait();
+ mScheduler->schedule([&]() MAIN_THREAD { mLayerTracing.notify("start"); })
+ .wait();
}
} else {
ALOGD("LayerTracing disabled");
- tracingEnabledChanged = mTracing.disable();
+ tracingEnabledChanged = mLayerTracing.disable();
}
mTracingEnabledChanged = tracingEnabledChanged;
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1026: { // Get layer tracing status
- reply->writeBool(mTracing.isEnabled());
+ reply->writeBool(mLayerTracing.isEnabled());
return NO_ERROR;
}
// Is a DisplayColorSetting supported?
@@ -5525,7 +5633,7 @@
}
ALOGD("Updating trace buffer to %d KB", n);
- mTracing.setBufferSize(n * 1024);
+ mLayerTracing.setBufferSize(n * 1024);
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
@@ -5570,12 +5678,12 @@
case 1033: {
n = data.readUint32();
ALOGD("Updating trace flags to 0x%x", n);
- mTracing.setTraceFlags(n);
+ mLayerTracing.setTraceFlags(n);
reply->writeInt32(NO_ERROR);
return NO_ERROR;
}
case 1034: {
- schedule([&] {
+ auto future = mScheduler->schedule([&] {
switch (n = data.readInt32()) {
case 0:
case 1:
@@ -5585,7 +5693,9 @@
reply->writeBool(ON_MAIN_THREAD(isRefreshRateOverlayEnabled()));
}
}
- }).get();
+ });
+
+ future.wait();
return NO_ERROR;
}
case 1035: {
@@ -5606,21 +5716,47 @@
}();
mDebugDisplayModeSetByBackdoor = false;
- const status_t result = setActiveMode(display, modeId);
+ const status_t result = setActiveModeFromBackdoor(display, modeId);
mDebugDisplayModeSetByBackdoor = result == NO_ERROR;
return result;
}
+ // Turn on/off frame rate flexibility mode. When turned on it overrides the display
+ // manager frame rate policy a new policy which allows switching between all refresh
+ // rates.
case 1036: {
- if (data.readInt32() > 0) {
- status_t result =
- acquireFrameRateFlexibilityToken(&mDebugFrameRateFlexibilityToken);
- if (result != NO_ERROR) {
- return result;
- }
- } else {
- mDebugFrameRateFlexibilityToken = nullptr;
+ if (data.readInt32() > 0) { // turn on
+ return mScheduler
+ ->schedule([this] {
+ const auto display =
+ ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+
+ // This is a little racy, but not in a way that hurts anything. As
+ // we grab the defaultMode from the display manager policy, we could
+ // be setting a new display manager policy, leaving us using a stale
+ // defaultMode. The defaultMode doesn't matter for the override
+ // policy though, since we set allowGroupSwitching to true, so it's
+ // not a problem.
+ scheduler::RefreshRateConfigs::Policy overridePolicy;
+ overridePolicy.defaultMode = display->refreshRateConfigs()
+ .getDisplayManagerPolicy()
+ .defaultMode;
+ overridePolicy.allowGroupSwitching = true;
+ constexpr bool kOverridePolicy = true;
+ return setDesiredDisplayModeSpecsInternal(display, overridePolicy,
+ kOverridePolicy);
+ })
+ .get();
+ } else { // turn off
+ return mScheduler
+ ->schedule([this] {
+ const auto display =
+ ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
+ constexpr bool kOverridePolicy = true;
+ return setDesiredDisplayModeSpecsInternal(display, {},
+ kOverridePolicy);
+ })
+ .get();
}
- return NO_ERROR;
}
// Inject a hotplug connected event for the primary display. This will deallocate and
// reallocate the display state including framebuffers.
@@ -5663,36 +5799,48 @@
// Second argument is an optional uint64 - if present, then limits enabling/disabling
// caching to a particular physical display
case 1040: {
- status_t error =
- schedule([&] {
- n = data.readInt32();
- std::optional<PhysicalDisplayId> inputId = std::nullopt;
- if (uint64_t inputDisplayId;
- data.readUint64(&inputDisplayId) == NO_ERROR) {
- inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
- if (!inputId || getPhysicalDisplayToken(*inputId)) {
- ALOGE("No display with id: %" PRIu64, inputDisplayId);
- return NAME_NOT_FOUND;
- }
+ auto future = mScheduler->schedule([&] {
+ n = data.readInt32();
+ std::optional<PhysicalDisplayId> inputId = std::nullopt;
+ if (uint64_t inputDisplayId; data.readUint64(&inputDisplayId) == NO_ERROR) {
+ inputId = DisplayId::fromValue<PhysicalDisplayId>(inputDisplayId);
+ if (!inputId || getPhysicalDisplayToken(*inputId)) {
+ ALOGE("No display with id: %" PRIu64, inputDisplayId);
+ return NAME_NOT_FOUND;
+ }
+ }
+ {
+ Mutex::Autolock lock(mStateLock);
+ mLayerCachingEnabled = n != 0;
+ for (const auto& [_, display] : mDisplays) {
+ if (!inputId || *inputId == display->getPhysicalId()) {
+ display->enableLayerCaching(mLayerCachingEnabled);
}
- {
- Mutex::Autolock lock(mStateLock);
- mLayerCachingEnabled = n != 0;
- for (const auto& [_, display] : mDisplays) {
- if (!inputId || *inputId == display->getPhysicalId()) {
- display->enableLayerCaching(mLayerCachingEnabled);
- }
- }
- }
- return OK;
- }).get();
+ }
+ }
+ return OK;
+ });
- if (error != OK) {
+ if (const status_t error = future.get(); error != OK) {
return error;
}
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;
@@ -5706,7 +5854,7 @@
// Update the overlay on the main thread to avoid race conditions with
// mRefreshRateConfigs->getCurrentRefreshRate()
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
if (!display) {
ALOGW("%s: default display is null", __func__);
@@ -5721,7 +5869,7 @@
const bool timerExpired = mKernelIdleTimerEnabled && expired;
if (display->onKernelTimerChanged(desiredModeId, timerExpired)) {
- mEventQueue->scheduleCommit();
+ mScheduler->scheduleFrame();
}
}));
}
@@ -5978,9 +6126,7 @@
sp<Layer> parent;
Rect crop(args.sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
- Rect layerStackSpaceRect;
ui::Dataspace dataspace;
- bool captureSecureLayers;
// Call this before holding mStateLock to avoid any deadlocking.
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
@@ -5989,7 +6135,7 @@
Mutex::Autolock lock(mStateLock);
parent = fromHandle(args.layerHandle).promote();
- if (parent == nullptr || parent->isRemovedFromCurrentState()) {
+ if (parent == nullptr) {
ALOGE("captureLayers called with an invalid or removed parent");
return NAME_NOT_FOUND;
}
@@ -6028,43 +6174,38 @@
}
}
- const auto display =
- findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
- return display.getLayerStack() == layerStack;
- });
-
- if (!display) {
- return NAME_NOT_FOUND;
- }
-
- layerStackSpaceRect = display->getLayerStackSpaceRect();
-
// The dataspace is depended on the color mode of display, that could use non-native mode
// (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
// and failed if display is not in native mode. This provide a way to force using native
// colors when capture.
dataspace = args.dataspace;
if (dataspace == ui::Dataspace::UNKNOWN) {
+ auto display = findDisplay([layerStack = parent->getLayerStack()](const auto& display) {
+ return display.getLayerStack() == layerStack;
+ });
+ if (!display) {
+ // If the layer is not on a display, use the dataspace for the default display.
+ display = getDefaultDisplayDeviceLocked();
+ }
+
const ui::ColorMode colorMode = display->getCompositionDisplay()->getState().colorMode;
dataspace = pickDataspaceFromColorMode(colorMode);
}
- captureSecureLayers = args.captureSecureLayers && display->isSecure();
} // mStateLock
// really small crop or frameScale
- if (reqSize.width <= 0) {
- reqSize.width = 1;
- }
- if (reqSize.height <= 0) {
- reqSize.height = 1;
+ if (reqSize.width <= 0 || reqSize.height <= 0) {
+ ALOGW("Failed to captureLayes: crop or scale too small");
+ return BAD_VALUE;
}
+ Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
bool childrenOnly = args.childrenOnly;
RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
childrenOnly, layerStackSpaceRect,
- captureSecureLayers);
+ args.captureSecureLayers);
});
auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
@@ -6120,14 +6261,15 @@
const bool supportsProtected = getRenderEngine().supportsProtectedContent();
bool hasProtectedLayer = false;
if (allowProtected && supportsProtected) {
- hasProtectedLayer = schedule([=]() {
- bool protectedLayerFound = false;
- traverseLayers([&](Layer* layer) {
- protectedLayerFound = protectedLayerFound ||
- (layer->isVisible() && layer->isProtected());
- });
- return protectedLayerFound;
- }).get();
+ auto future = mScheduler->schedule([=]() {
+ bool protectedLayerFound = false;
+ traverseLayers([&](Layer* layer) {
+ protectedLayerFound =
+ protectedLayerFound || (layer->isVisible() && layer->isProtected());
+ });
+ return protectedLayerFound;
+ });
+ hasProtectedLayer = future.get();
}
const uint32_t usage = GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_RENDER |
@@ -6158,9 +6300,11 @@
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
- auto scheduleResultFuture = schedule([=,
- renderAreaFuture = std::move(renderAreaFuture)]() mutable
- -> std::shared_future<renderengine::RenderEngineResult> {
+ auto scheduleResultFuture = mScheduler->schedule([=,
+ renderAreaFuture =
+ std::move(renderAreaFuture)]() mutable
+ -> std::shared_future<
+ renderengine::RenderEngineResult> {
ScreenCaptureResults captureResults;
std::unique_ptr<RenderArea> renderArea = renderAreaFuture.get();
if (!renderArea) {
@@ -6287,6 +6431,8 @@
BlurSetting::Disabled
: compositionengine::LayerFE::ClientCompositionTargetSettings::
BlurSetting::Enabled,
+ DisplayDevice::sDefaultMaxLumiance,
+
};
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
@@ -6434,7 +6580,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());
@@ -6453,7 +6599,7 @@
return BAD_VALUE;
}
- auto future = schedule([=]() -> status_t {
+ auto future = mScheduler->schedule([=]() -> status_t {
const auto display = ON_MAIN_THREAD(getDisplayDeviceLocked(displayToken));
if (!display) {
ALOGE("Attempt to set desired display modes for invalid display token %p",
@@ -6531,6 +6677,9 @@
if (!layer->isRemovedFromCurrentState()) {
mScheduler->deregisterLayer(layer);
}
+ if (mTransactionTracingEnabled) {
+ mTransactionTracing.onLayerRemoved(layer->getSequence());
+ }
}
void SurfaceFlinger::onLayerUpdate() {
@@ -6590,7 +6739,7 @@
return BAD_VALUE;
}
- static_cast<void>(schedule([=] {
+ static_cast<void>(mScheduler->schedule([=] {
Mutex::Autolock lock(mStateLock);
if (authenticateSurfaceTextureLocked(surface)) {
sp<Layer> layer = (static_cast<MonitoredProducer*>(surface.get()))->getLayer();
@@ -6616,74 +6765,6 @@
return NO_ERROR;
}
-status_t SurfaceFlinger::acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) {
- if (!outToken) {
- return BAD_VALUE;
- }
-
- auto future = schedule([this] {
- status_t result = NO_ERROR;
- sp<IBinder> token;
-
- if (mFrameRateFlexibilityTokenCount == 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
-
- // This is a little racy, but not in a way that hurts anything. As we grab the
- // defaultMode from the display manager policy, we could be setting a new display
- // manager policy, leaving us using a stale defaultMode. The defaultMode doesn't
- // matter for the override policy though, since we set allowGroupSwitching to
- // true, so it's not a problem.
- scheduler::RefreshRateConfigs::Policy overridePolicy;
- overridePolicy.defaultMode =
- display->refreshRateConfigs().getDisplayManagerPolicy().defaultMode;
- overridePolicy.allowGroupSwitching = true;
- constexpr bool kOverridePolicy = true;
- result = setDesiredDisplayModeSpecsInternal(display, overridePolicy, kOverridePolicy);
- }
-
- if (result == NO_ERROR) {
- mFrameRateFlexibilityTokenCount++;
- // Handing out a reference to the SurfaceFlinger object, as we're doing in the line
- // below, is something to consider carefully. The lifetime of the
- // FrameRateFlexibilityToken isn't tied to SurfaceFlinger object lifetime, so if this
- // SurfaceFlinger object were to be destroyed while the token still exists, the token
- // destructor would be accessing a stale SurfaceFlinger reference, and crash. This is ok
- // in this case, for two reasons:
- // 1. Once SurfaceFlinger::run() is called by main_surfaceflinger.cpp, the only way
- // the program exits is via a crash. So we won't have a situation where the
- // SurfaceFlinger object is dead but the process is still up.
- // 2. The frame rate flexibility token is acquired/released only by CTS tests, so even
- // if condition 1 were changed, the problem would only show up when running CTS tests,
- // not on end user devices, so we could spot it and fix it without serious impact.
- token = new FrameRateFlexibilityToken(
- [this]() { onFrameRateFlexibilityTokenReleased(); });
- ALOGD("Frame rate flexibility token acquired. count=%d",
- mFrameRateFlexibilityTokenCount);
- }
-
- return std::make_pair(result, token);
- });
-
- status_t result;
- std::tie(result, *outToken) = future.get();
- return result;
-}
-
-void SurfaceFlinger::onFrameRateFlexibilityTokenReleased() {
- static_cast<void>(schedule([this] {
- LOG_ALWAYS_FATAL_IF(mFrameRateFlexibilityTokenCount == 0,
- "Failed tracking frame rate flexibility tokens");
- mFrameRateFlexibilityTokenCount--;
- ALOGD("Frame rate flexibility token released. count=%d", mFrameRateFlexibilityTokenCount);
- if (mFrameRateFlexibilityTokenCount == 0) {
- const auto display = ON_MAIN_THREAD(getDefaultDisplayDeviceLocked());
- constexpr bool kOverridePolicy = true;
- status_t result = setDesiredDisplayModeSpecsInternal(display, {}, kOverridePolicy);
- LOG_ALWAYS_FATAL_IF(result < 0, "Failed releasing frame rate flexibility token");
- }
- }));
-}
-
status_t SurfaceFlinger::setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) {
Mutex::Autolock lock(mStateLock);
@@ -6777,11 +6858,10 @@
}
void SurfaceFlinger::setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const sp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot) {
+ const wp<Layer> parent, bool addToRoot) {
Mutex::Autolock lock(mCreatedLayersLock);
mCreatedLayers[handle->localBinder()] =
- std::make_unique<LayerCreatedState>(layer, parent, parentLayer, producer, addToRoot);
+ std::make_unique<LayerCreatedState>(layer, parent, addToRoot);
}
auto SurfaceFlinger::getLayerCreatedState(const sp<IBinder>& handle) {
@@ -6819,19 +6899,16 @@
}
sp<Layer> parent;
- bool allowAddRoot = state->addToRoot;
+ bool addToRoot = state->addToRoot;
if (state->initialParent != nullptr) {
- parent = fromHandle(state->initialParent).promote();
+ parent = state->initialParent.promote();
if (parent == nullptr) {
- ALOGE("Invalid parent %p", state->initialParent.get());
- allowAddRoot = false;
+ ALOGE("Invalid parent %p", state->initialParent.unsafe_get());
+ addToRoot = false;
}
- } else if (state->initialParentLayer != nullptr) {
- parent = state->initialParentLayer.promote();
- allowAddRoot = false;
}
- if (parent == nullptr && allowAddRoot) {
+ if (parent == nullptr && addToRoot) {
layer->setIsAtRoot(true);
mCurrentState.layersSortedByZ.add(layer);
} else if (parent == nullptr) {
@@ -6845,19 +6922,6 @@
layer->updateTransformHint(mActiveDisplayTransformHint);
- if (state->initialProducer != nullptr) {
- mGraphicBufferProducerList.insert(state->initialProducer);
- LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() > mMaxGraphicBufferProducerListSize,
- "Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
- mNumLayers.load());
- if (mGraphicBufferProducerList.size() > mGraphicBufferProducerListSizeLogThreshold) {
- ALOGW("Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
- mGraphicBufferProducerList.size(), mMaxGraphicBufferProducerListSize,
- mNumLayers.load());
- }
- }
-
mInterceptor->saveSurfaceCreation(layer);
return layer;
}
@@ -6867,7 +6931,7 @@
return;
}
- mRegionSamplingThread->onCompositionComplete(mEventQueue->getScheduledFrameTime());
+ mRegionSamplingThread->onCompositionComplete(mScheduler->getScheduledFrameTime());
}
void SurfaceFlinger::onActiveDisplaySizeChanged(const sp<DisplayDevice>& activeDisplay) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 276c7f6..e1b52c5 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,23 +46,25 @@
#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/MessageQueue.h"
#include "Scheduler/RefreshRateConfigs.h"
#include "Scheduler/RefreshRateStats.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncModulator.h"
#include "SurfaceFlingerFactory.h"
-#include "SurfaceTracing.h"
#include "TracedOrdinal.h"
+#include "Tracing/LayerTracing.h"
+#include "Tracing/TransactionTracing.h"
#include "TransactionCallbackInvoker.h"
#include "TransactionState.h"
@@ -105,6 +106,7 @@
class FrameTracer;
class WindowInfosListenerInvoker;
+using gui::IRegionSamplingListener;
using gui::ScreenCaptureResults;
namespace frametimeline {
@@ -135,6 +137,8 @@
eTransactionMask = 0x1f,
};
+enum class LatchUnsignaledConfig { Always, Auto, Disabled };
+
using DisplayColorSetting = compositionengine::OutputColorSetting;
struct SurfaceFlingerBE {
@@ -163,7 +167,7 @@
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
private ICompositor,
- private ISchedulerCallback {
+ private scheduler::ISchedulerCallback {
public:
struct SkipInitializationTag {};
@@ -246,18 +250,13 @@
static ui::Dataspace wideColorGamutCompositionDataspace;
static ui::PixelFormat wideColorGamutCompositionPixelFormat;
- // Whether to use frame rate API when deciding about the refresh rate of the display. This
- // variable is caches in SF, so that we can check it with each layer creation, and a void the
- // overhead that is caused by reading from sysprop.
- static bool useFrameRateApi;
-
static constexpr SkipInitializationTag SkipInitialization;
// Whether or not SDR layers should be dimmed to the desired SDR white point instead of
// being treated as native display brightness
static bool enableSdrDimming;
- static bool enableLatchUnsignaled;
+ static LatchUnsignaledConfig enableLatchUnsignaledConfig;
// must be called before clients can connect
void init() ANDROID_API;
@@ -268,10 +267,6 @@
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
- // Schedule an asynchronous or synchronous task on the main thread.
- template <typename F, typename T = std::invoke_result_t<F>>
- [[nodiscard]] std::future<T> schedule(F&&);
-
// Schedule commit of transactions on the main thread ahead of the next VSYNC.
void scheduleCommit(FrameHint);
// As above, but also force composite regardless if transactions were committed.
@@ -326,6 +321,7 @@
// Disables expensive rendering for all displays
// This is scheduled on the main thread
void disableExpensiveRendering();
+ FloatRect getMaxDisplayBounds();
protected:
// We're reference counted, never destroy SurfaceFlinger directly
@@ -355,14 +351,13 @@
friend class MonitoredProducer;
friend class RefreshRateOverlay;
friend class RegionSamplingThread;
- friend class SurfaceTracing;
+ friend class LayerTracing;
// For unit tests
friend class TestableSurfaceFlinger;
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&)>;
@@ -601,7 +596,6 @@
float lightPosY, float lightPosZ, float lightRadius) override;
status_t setFrameRate(const sp<IGraphicBufferProducer>& surface, float frameRate,
int8_t compatibility, int8_t changeFrameRateStrategy) override;
- status_t acquireFrameRateFlexibilityToken(sp<IBinder>* outToken) override;
status_t setFrameTimelineInfo(const sp<IGraphicBufferProducer>& surface,
const FrameTimelineInfo& frameTimelineInfo) override;
@@ -649,8 +643,8 @@
// Toggles hardware VSYNC by calling into HWC.
void setVsyncEnabled(bool) override;
- // Initiates a refresh rate change to be applied on invalidate.
- void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ModeEvent) override;
+ // Initiates a refresh rate change to be applied on commit.
+ 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.
@@ -667,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);
@@ -687,9 +680,6 @@
const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy)
EXCLUDES(mStateLock);
- // Returns whether transactions were committed.
- bool flushAndCommitTransactions() EXCLUDES(mStateLock);
-
void commitTransactions() EXCLUDES(mStateLock);
void commitTransactionsLocked(uint32_t transactionFlags) REQUIRES(mStateLock);
void doCommitTransactions() REQUIRES(mStateLock);
@@ -700,7 +690,8 @@
void updateLayerGeometry();
void updateInputFlinger();
- void notifyWindowInfos();
+ void buildWindowInfos(std::vector<gui::WindowInfo>& outWindowInfos,
+ std::vector<gui::DisplayInfo>& outDisplayInfos);
void commitInputWindowCommands() REQUIRES(mStateLock);
void updateCursorAsync();
@@ -722,7 +713,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();
@@ -751,7 +742,15 @@
const FrameTimelineInfo& info, bool isAutoTimestamp, int64_t desiredPresentTime,
uid_t originUid, const Vector<ComposerState>& states,
const std::unordered_set<sp<IBinder>, ISurfaceComposer::SpHash<IBinder>>&
- bufferLayersReadyToPresent) const REQUIRES(mStateLock);
+ bufferLayersReadyToPresent,
+ bool allowLatchUnsignaled) const REQUIRES(mStateLock);
+ static LatchUnsignaledConfig getLatchUnsignaledConfig();
+ bool latchUnsignaledIsAllowed(std::vector<TransactionState>& transactions) REQUIRES(mStateLock);
+ bool allowedLatchUnsignaled() REQUIRES(mQueueLock, mStateLock);
+ bool checkTransactionCanLatchUnsignaled(const TransactionState& transaction)
+ 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);
@@ -759,31 +758,25 @@
/*
* Layer management
*/
- status_t createLayer(const String8& name, const sp<Client>& client, uint32_t w, uint32_t h,
- PixelFormat format, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp,
+ status_t createLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
const sp<IBinder>& parentHandle, int32_t* outLayerId,
const sp<Layer>& parentLayer = nullptr,
uint32_t* outTransformHint = nullptr);
- status_t createBufferQueueLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- PixelFormat& format, sp<IBinder>* outHandle,
- sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer);
+ status_t createBufferQueueLayer(LayerCreationArgs& args, PixelFormat& format,
+ sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp,
+ sp<Layer>* outLayer);
- status_t createBufferStateLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ status_t createBufferStateLayer(LayerCreationArgs& args, sp<IBinder>* outHandle,
+ sp<Layer>* outLayer);
- status_t createEffectLayer(const sp<Client>& client, std::string name, uint32_t w, uint32_t h,
- uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle,
+ status_t createEffectLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
sp<Layer>* outLayer);
- status_t createContainerLayer(const sp<Client>& client, std::string name, uint32_t w,
- uint32_t h, uint32_t flags, LayerMetadata metadata,
- sp<IBinder>* outHandle, sp<Layer>* outLayer);
+ status_t createContainerLayer(const LayerCreationArgs& args, sp<IBinder>* outHandle,
+ sp<Layer>* outLayer);
- status_t mirrorLayer(const sp<Client>& client, const sp<IBinder>& mirrorFromHandle,
+ status_t mirrorLayer(const LayerCreationArgs& args, const sp<IBinder>& mirrorFromHandle,
sp<IBinder>* outHandle, int32_t* outLayerId);
// called when all clients have released all their references to
@@ -794,9 +787,8 @@
// add a layer to SurfaceFlinger
status_t addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
- const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
- const sp<IBinder>& parentHandle, const sp<Layer>& parentLayer,
- bool addToRoot, uint32_t* outTransformHint);
+ const sp<Layer>& lbc, const wp<Layer>& parentLayer, bool addToRoot,
+ uint32_t* outTransformHint);
// Traverse through all the layers and compute and cache its bounds.
void computeLayerBounds();
@@ -893,8 +885,6 @@
// region of all screens presenting this layer stack.
void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty);
- sp<DisplayDevice> getDisplayWithInputByLayer(Layer* layer) const REQUIRES(mStateLock);
-
bool isDisplayActiveLocked(const sp<const DisplayDevice>& display) const REQUIRES(mStateLock) {
return display->getDisplayToken() == mActiveDisplayToken;
}
@@ -952,10 +942,6 @@
getHwComposer().setVsyncEnabled(id, enabled);
}
- // Sets the refresh rate by switching active configs, if they are available for
- // the desired refresh rate.
- void changeRefreshRateLocked(const RefreshRate&, Scheduler::ModeEvent) REQUIRES(mStateLock);
-
struct FenceWithFenceTime {
sp<Fence> fence = Fence::NO_FENCE;
std::shared_ptr<FenceTime> fenceTime = FenceTime::NO_FENCE;
@@ -1049,12 +1035,12 @@
void dumpWideColorInfo(std::string& result) const REQUIRES(mStateLock);
LayersProto dumpDrawingStateProto(uint32_t traceFlags) const;
void dumpOffscreenLayersProto(LayersProto& layersProto,
- uint32_t traceFlags = SurfaceTracing::TRACE_ALL) const;
+ uint32_t traceFlags = LayerTracing::TRACE_ALL) const;
void dumpDisplayProto(LayersTraceProto& layersTraceProto) const;
// Dumps state from HW Composer
void dumpHwc(std::string& result) const;
- LayersProto dumpProtoFromMainThread(uint32_t traceFlags = SurfaceTracing::TRACE_ALL)
+ LayersProto dumpProtoFromMainThread(uint32_t traceFlags = LayerTracing::TRACE_ALL)
EXCLUDES(mStateLock);
void dumpOffscreenLayers(std::string& result) EXCLUDES(mStateLock);
void dumpPlannerInfo(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock);
@@ -1067,8 +1053,6 @@
return doDump(fd, args, asProto);
}
- void onFrameRateFlexibilityTokenReleased();
-
static mat4 calculateColorMatrix(float saturation);
void updateColorMatrixLocked();
@@ -1101,7 +1085,7 @@
sp<StartPropertySetThread> mStartPropertySetThread;
surfaceflinger::Factory& mFactory;
-
+ pid_t mPid;
std::future<void> mRenderEnginePrimeCacheFuture;
// access must be protected by mStateLock
@@ -1110,6 +1094,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
@@ -1117,16 +1102,12 @@
float mGlobalSaturationFactor = 1.0f;
mat4 mClientColorMatrix;
- // Can't be unordered_set because wp<> isn't hashable
- std::set<wp<IBinder>> mGraphicBufferProducerList;
size_t mMaxGraphicBufferProducerListSize = ISurfaceComposer::MAX_LAYERS;
// If there are more GraphicBufferProducers tracked by SurfaceFlinger than
// this threshold, then begin logging.
size_t mGraphicBufferProducerListSizeLogThreshold =
static_cast<size_t>(0.95 * static_cast<double>(MAX_LAYERS));
- void removeGraphicBufferProducerAsync(const wp<IBinder>&);
-
// protected by mStateLock (but we could use another lock)
bool mLayersRemoved = false;
bool mLayersAdded = false;
@@ -1203,10 +1184,11 @@
bool mPropagateBackpressureClientComposition = false;
sp<SurfaceInterceptor> mInterceptor;
- SurfaceTracing mTracing{*this};
- std::mutex mTracingLock;
- bool mTracingEnabled = false;
- bool mTracePostComposition = false;
+ LayerTracing mLayerTracing{*this};
+ bool mLayerTracingEnabled = false;
+
+ TransactionTracing mTransactionTracing;
+ bool mTransactionTracingEnabled = false;
std::atomic<bool> mTracingEnabledChanged = false;
const std::shared_ptr<TimeStats> mTimeStats;
@@ -1223,14 +1205,9 @@
TransactionCallbackInvoker mTransactionCallbackInvoker;
- // these are thread safe
- std::unique_ptr<MessageQueue> mEventQueue;
+ // Thread-safe.
FrameTracker mAnimFrameTracker;
- // protected by mDestroyedLayerLock;
- mutable Mutex mDestroyedLayerLock;
- Vector<Layer const *> mDestroyedLayers;
-
// We maintain a pool of pre-generated texture names to hand out to avoid
// layer creation needing to run on the main thread (which it would
// otherwise need to do to access RenderEngine).
@@ -1242,7 +1219,7 @@
Condition mTransactionQueueCV;
std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IListenerHash>
mPendingTransactionQueues GUARDED_BY(mQueueLock);
- std::queue<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
+ std::deque<TransactionState> mTransactionQueue GUARDED_BY(mQueueLock);
/*
* Feature prototyping
*/
@@ -1289,7 +1266,7 @@
/*
* Scheduler
*/
- std::unique_ptr<Scheduler> mScheduler;
+ std::unique_ptr<scheduler::Scheduler> mScheduler;
scheduler::ConnectionHandle mAppConnectionHandle;
scheduler::ConnectionHandle mSfConnectionHandle;
@@ -1318,8 +1295,8 @@
const float mInternalDisplayDensity;
const float mEmulatedDisplayDensity;
- sp<os::IInputFlinger> mInputFlinger;
// Should only be accessed by the main thread.
+ sp<os::IInputFlinger> mInputFlinger;
InputWindowCommands mInputWindowCommands;
Hwc2::impl::PowerAdvisor mPowerAdvisor;
@@ -1335,31 +1312,18 @@
// be any issues with a raw pointer referencing an invalid object.
std::unordered_set<Layer*> mOffscreenLayers;
- int mFrameRateFlexibilityTokenCount = 0;
-
- sp<IBinder> mDebugFrameRateFlexibilityToken;
-
BufferCountTracker mBufferCountTracker;
std::unordered_map<DisplayId, sp<HdrLayerInfoReporter>> mHdrLayerInfoListeners
GUARDED_BY(mStateLock);
mutable Mutex mCreatedLayersLock;
struct LayerCreatedState {
- LayerCreatedState(const wp<Layer>& layer, const sp<IBinder>& parent,
- const wp<Layer> parentLayer, const wp<IBinder>& producer, bool addToRoot)
- : layer(layer),
- initialParent(parent),
- initialParentLayer(parentLayer),
- initialProducer(producer),
- addToRoot(addToRoot) {}
+ LayerCreatedState(const wp<Layer>& layer, const wp<Layer> parent, bool addToRoot)
+ : layer(layer), initialParent(parent), addToRoot(addToRoot) {}
wp<Layer> layer;
// Indicates the initial parent of the created layer, only used for creating layer in
// SurfaceFlinger. If nullptr, it may add the created layer into the current root layers.
- sp<IBinder> initialParent;
- wp<Layer> initialParentLayer;
- // Indicates the initial graphic buffer producer of the created layer, only used for
- // creating layer in SurfaceFlinger.
- wp<IBinder> initialProducer;
+ wp<Layer> initialParent;
// Indicates whether the layer getting created should be added at root if there's no parent
// and has permission ACCESS_SURFACE_FLINGER. If set to false and no parent, the layer will
// be added offscreen.
@@ -1370,8 +1334,7 @@
// thread.
std::unordered_map<BBinder*, std::unique_ptr<LayerCreatedState>> mCreatedLayers;
void setLayerCreatedState(const sp<IBinder>& handle, const wp<Layer>& layer,
- const sp<IBinder>& parent, const wp<Layer> parentLayer,
- const wp<IBinder>& producer, bool addToRoot);
+ const wp<Layer> parent, bool addToRoot);
auto getLayerCreatedState(const sp<IBinder>& handle);
sp<Layer> handleLayerCreatedLocked(const sp<IBinder>& handle) REQUIRES(mStateLock);
@@ -1389,6 +1352,11 @@
const sp<WindowInfosListenerInvoker> mWindowInfosListenerInvoker;
std::unique_ptr<FlagManager> mFlagManager;
+
+ // returns the framerate of the layer with the given sequence ID
+ float getLayerFramerate(nsecs_t now, int32_t id) const {
+ return mScheduler->getLayerFramerate(now, id);
+ }
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 9a2f910..b81b445 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -38,7 +38,6 @@
#include "SurfaceInterceptor.h"
#include "DisplayHardware/ComposerHal.h"
-#include "Scheduler/MessageQueue.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/VsyncConfiguration.h"
#include "Scheduler/VsyncController.h"
@@ -51,10 +50,6 @@
return std::make_unique<android::impl::HWComposer>(serviceName);
}
-std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue(ICompositor& compositor) {
- return std::make_unique<android::impl::MessageQueue>(compositor);
-}
-
std::unique_ptr<scheduler::VsyncConfiguration> DefaultFactory::createVsyncConfiguration(
Fps currentRefreshRate) {
if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
@@ -64,12 +59,6 @@
}
}
-std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback) {
- return std::make_unique<Scheduler>(std::move(refreshRateConfigs), callback);
-}
-
sp<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor() {
return new android::impl::SurfaceInterceptor();
}
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 2be09ee..501629d 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -27,11 +27,8 @@
virtual ~DefaultFactory();
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
- std::unique_ptr<MessageQueue> createMessageQueue(ICompositor&) override;
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) override;
- std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override;
sp<SurfaceInterceptor> createSurfaceInterceptor() override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index bca533b..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 {
@@ -77,11 +73,8 @@
class Factory {
public:
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
- virtual std::unique_ptr<MessageQueue> createMessageQueue(ICompositor&) = 0;
virtual std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps currentRefreshRate) = 0;
- virtual std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) = 0;
virtual sp<SurfaceInterceptor> createSurfaceInterceptor() = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index a8117f7..16f6e31 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -304,14 +304,6 @@
return defaultValue;
}
-bool use_frame_rate_api(bool defaultValue) {
- auto temp = SurfaceFlingerProperties::use_frame_rate_api();
- if (temp.has_value()) {
- return *temp;
- }
- return defaultValue;
-}
-
bool enable_sdr_dimming(bool defaultValue) {
return SurfaceFlingerProperties::enable_sdr_dimming().value_or(defaultValue);
}
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index ed18260..8d0e426 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -88,8 +88,6 @@
bool support_kernel_idle_timer(bool defaultValue);
-bool use_frame_rate_api(bool defaultValue);
-
int32_t display_update_imminent_timeout_ms(int32_t defaultValue);
android::ui::DisplayPrimaries getDisplayNativePrimaries();
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
deleted file mode 100644
index 5963737..0000000
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#undef LOG_TAG
-#define LOG_TAG "SurfaceTracing"
-#define ATRACE_TAG ATRACE_TAG_GRAPHICS
-
-#include "SurfaceTracing.h"
-#include <SurfaceFlinger.h>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-#include <log/log.h>
-#include <utils/SystemClock.h>
-#include <utils/Trace.h>
-
-namespace android {
-
-SurfaceTracing::SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
-
-bool SurfaceTracing::enable() {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- return false;
- }
-
- if (flagIsSet(TRACE_SYNC)) {
- runner = std::make_unique<SurfaceTracing::Runner>(mFlinger, mConfig);
- } else {
- runner = std::make_unique<SurfaceTracing::AsyncRunner>(mFlinger, mConfig,
- mFlinger.mTracingLock);
- }
- mEnabled = true;
- return true;
-}
-
-bool SurfaceTracing::disable() {
- std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return false;
- }
- mEnabled = false;
- runner->stop();
- return true;
-}
-
-bool SurfaceTracing::isEnabled() const {
- std::scoped_lock lock(mTraceLock);
- return mEnabled;
-}
-
-status_t SurfaceTracing::writeToFile() {
- std::scoped_lock lock(mTraceLock);
- if (!mEnabled) {
- return STATUS_OK;
- }
- return runner->writeToFile();
-}
-
-void SurfaceTracing::notify(const char* where) {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- runner->notify(where);
- }
-}
-
-void SurfaceTracing::notifyLocked(const char* where) {
- std::scoped_lock lock(mTraceLock);
- if (mEnabled) {
- runner->notifyLocked(where);
- }
-}
-
-void SurfaceTracing::dump(std::string& result) const {
- std::scoped_lock lock(mTraceLock);
- base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
- if (mEnabled) {
- runner->dump(result);
- }
-}
-
-void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
- // use the swap trick to make sure memory is released
- std::queue<LayersTraceProto>().swap(mStorage);
- mSizeInBytes = newSize;
- mUsedInBytes = 0U;
-}
-
-void SurfaceTracing::LayersTraceBuffer::emplace(LayersTraceProto&& proto) {
- size_t protoSize = static_cast<size_t>(proto.ByteSize());
- while (mUsedInBytes + protoSize > mSizeInBytes) {
- if (mStorage.empty()) {
- return;
- }
- mUsedInBytes -= static_cast<size_t>(mStorage.front().ByteSize());
- mStorage.pop();
- }
- mUsedInBytes += protoSize;
- mStorage.emplace();
- mStorage.back().Swap(&proto);
-}
-
-void SurfaceTracing::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) {
- fileProto->mutable_entry()->Reserve(static_cast<int>(mStorage.size()));
-
- while (!mStorage.empty()) {
- auto entry = fileProto->add_entry();
- entry->Swap(&mStorage.front());
- mStorage.pop();
- }
-}
-
-SurfaceTracing::Runner::Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config)
- : mFlinger(flinger), mConfig(config) {
- mBuffer.setSize(mConfig.bufferSize);
-}
-
-void SurfaceTracing::Runner::notify(const char* where) {
- LayersTraceProto entry = traceLayers(where);
- mBuffer.emplace(std::move(entry));
-}
-
-status_t SurfaceTracing::Runner::stop() {
- return writeToFile();
-}
-
-LayersTraceFileProto SurfaceTracing::createLayersTraceFileProto() {
- LayersTraceFileProto fileProto;
- fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
- LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
- return fileProto;
-}
-
-status_t SurfaceTracing::Runner::writeToFile() {
- ATRACE_CALL();
-
- LayersTraceFileProto fileProto = createLayersTraceFileProto();
- std::string output;
-
- mBuffer.flush(&fileProto);
- mBuffer.reset(mConfig.bufferSize);
-
- if (!fileProto.SerializeToString(&output)) {
- ALOGE("Could not save the proto file! Permission denied");
- return PERMISSION_DENIED;
- }
-
- // -rw-r--r--
- const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
- if (!android::base::WriteStringToFile(output, DEFAULT_FILE_NAME, mode, getuid(), getgid(),
- true)) {
- ALOGE("Could not save the proto file! There are missing fields");
- return PERMISSION_DENIED;
- }
-
- return NO_ERROR;
-}
-
-LayersTraceProto SurfaceTracing::Runner::traceLayers(const char* where) {
- ATRACE_CALL();
-
- LayersTraceProto entry;
- entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
- entry.set_where(where);
- LayersProto layers(mFlinger.dumpDrawingStateProto(mConfig.flags));
-
- if (flagIsSet(SurfaceTracing::TRACE_EXTRA)) {
- mFlinger.dumpOffscreenLayersProto(layers);
- }
- entry.mutable_layers()->Swap(&layers);
-
- if (flagIsSet(SurfaceTracing::TRACE_HWC)) {
- std::string hwcDump;
- mFlinger.dumpHwc(hwcDump);
- entry.set_hwc_blob(hwcDump);
- }
- if (!flagIsSet(SurfaceTracing::TRACE_COMPOSITION)) {
- entry.set_excludes_composition_state(true);
- }
- entry.set_missed_entries(mMissedTraceEntries);
- mFlinger.dumpDisplayProto(entry);
- return entry;
-}
-
-void SurfaceTracing::Runner::dump(std::string& result) const {
- base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
- mBuffer.frameCount(), float(mBuffer.used()) / float(1_MB),
- float(mBuffer.size()) / float(1_MB));
-}
-
-SurfaceTracing::AsyncRunner::AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config,
- std::mutex& sfLock)
- : SurfaceTracing::Runner(flinger, config), mSfLock(sfLock) {
- mEnabled = true;
- mThread = std::thread(&AsyncRunner::loop, this);
-}
-
-void SurfaceTracing::AsyncRunner::loop() {
- while (mEnabled) {
- LayersTraceProto entry;
- bool entryAdded = traceWhenNotified(&entry);
- if (entryAdded) {
- mBuffer.emplace(std::move(entry));
- }
- if (mWriteToFile) {
- Runner::writeToFile();
- mWriteToFile = false;
- }
- }
-}
-
-bool SurfaceTracing::AsyncRunner::traceWhenNotified(LayersTraceProto* outProto) {
- std::unique_lock<std::mutex> lock(mSfLock);
- mCanStartTrace.wait(lock);
- if (!mAddEntry) {
- return false;
- }
- *outProto = traceLayers(mWhere);
- mAddEntry = false;
- mMissedTraceEntries = 0;
- return true;
-}
-
-void SurfaceTracing::AsyncRunner::notify(const char* where) {
- std::scoped_lock lock(mSfLock);
- notifyLocked(where);
-}
-
-void SurfaceTracing::AsyncRunner::notifyLocked(const char* where) {
- mWhere = where;
- if (mAddEntry) {
- mMissedTraceEntries++;
- }
- mAddEntry = true;
- mCanStartTrace.notify_one();
-}
-
-status_t SurfaceTracing::AsyncRunner::writeToFile() {
- mWriteToFile = true;
- mCanStartTrace.notify_one();
- return STATUS_OK;
-}
-
-status_t SurfaceTracing::AsyncRunner::stop() {
- mEnabled = false;
- mCanStartTrace.notify_one();
- mThread.join();
- return Runner::writeToFile();
-}
-
-} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
deleted file mode 100644
index cea1a33..0000000
--- a/services/surfaceflinger/SurfaceTracing.h
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <android-base/thread_annotations.h>
-#include <layerproto/LayerProtoHeader.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-#include <thread>
-
-using namespace android::surfaceflinger;
-
-namespace android {
-
-class SurfaceFlinger;
-constexpr auto operator""_MB(unsigned long long const num) {
- return num * 1024 * 1024;
-}
-/*
- * SurfaceTracing records layer states during surface flinging. Manages tracing state and
- * configuration.
- */
-class SurfaceTracing {
-public:
- SurfaceTracing(SurfaceFlinger& flinger);
- bool enable();
- bool disable();
- status_t writeToFile();
- bool isEnabled() const;
- /*
- * Adds a trace entry, must be called from the drawing thread or while holding the
- * SurfaceFlinger tracing lock.
- */
- void notify(const char* where);
- /*
- * Adds a trace entry, called while holding the SurfaceFlinger tracing lock.
- */
- void notifyLocked(const char* where) /* REQUIRES(mSfLock) */;
-
- void setBufferSize(size_t bufferSizeInBytes) { mConfig.bufferSize = bufferSizeInBytes; }
- void dump(std::string& result) const;
-
- enum : uint32_t {
- TRACE_CRITICAL = 1 << 0,
- TRACE_INPUT = 1 << 1,
- TRACE_COMPOSITION = 1 << 2,
- TRACE_EXTRA = 1 << 3,
- TRACE_HWC = 1 << 4,
- // Add non-geometry composition changes to the trace.
- TRACE_BUFFERS = 1 << 5,
- // Add entries from the drawing thread post composition.
- TRACE_SYNC = 1 << 6,
- TRACE_ALL = TRACE_CRITICAL | TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
- };
- void setTraceFlags(uint32_t flags) { mConfig.flags = flags; }
- bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
- static LayersTraceFileProto createLayersTraceFileProto();
-
-private:
- class Runner;
- static constexpr auto DEFAULT_BUFFER_SIZE = 5_MB;
- static constexpr auto DEFAULT_FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
-
- SurfaceFlinger& mFlinger;
- mutable std::mutex mTraceLock;
- bool mEnabled GUARDED_BY(mTraceLock) = false;
- std::unique_ptr<Runner> runner GUARDED_BY(mTraceLock);
-
- struct Config {
- uint32_t flags = TRACE_CRITICAL | TRACE_INPUT | TRACE_SYNC;
- size_t bufferSize = DEFAULT_BUFFER_SIZE;
- } mConfig;
-
- /*
- * ring buffer.
- */
- class LayersTraceBuffer {
- public:
- size_t size() const { return mSizeInBytes; }
- size_t used() const { return mUsedInBytes; }
- size_t frameCount() const { return mStorage.size(); }
-
- void setSize(size_t newSize) { mSizeInBytes = newSize; }
- void reset(size_t newSize);
- void emplace(LayersTraceProto&& proto);
- void flush(LayersTraceFileProto* fileProto);
-
- private:
- size_t mUsedInBytes = 0U;
- size_t mSizeInBytes = DEFAULT_BUFFER_SIZE;
- std::queue<LayersTraceProto> mStorage;
- };
-
- /*
- * Implements a synchronous way of adding trace entries. This must be called
- * from the drawing thread.
- */
- class Runner {
- public:
- Runner(SurfaceFlinger& flinger, SurfaceTracing::Config& config);
- virtual ~Runner() = default;
- virtual status_t stop();
- virtual status_t writeToFile();
- virtual void notify(const char* where);
- /* Cannot be called with a synchronous runner. */
- virtual void notifyLocked(const char* /* where */) {}
- void dump(std::string& result) const;
-
- protected:
- bool flagIsSet(uint32_t flags) { return (mConfig.flags & flags) == flags; }
- SurfaceFlinger& mFlinger;
- SurfaceTracing::Config mConfig;
- SurfaceTracing::LayersTraceBuffer mBuffer;
- uint32_t mMissedTraceEntries = 0;
- LayersTraceProto traceLayers(const char* where);
- };
-
- /*
- * Implements asynchronous way to add trace entries called from a separate thread while holding
- * the SurfaceFlinger tracing lock. Trace entries may be missed if the tracing thread is not
- * scheduled in time.
- */
- class AsyncRunner : public Runner {
- public:
- AsyncRunner(SurfaceFlinger& flinger, SurfaceTracing::Config& config, std::mutex& sfLock);
- virtual ~AsyncRunner() = default;
- status_t stop() override;
- status_t writeToFile() override;
- void notify(const char* where) override;
- void notifyLocked(const char* where);
-
- private:
- std::mutex& mSfLock;
- std::condition_variable mCanStartTrace;
- std::thread mThread;
- const char* mWhere = "";
- bool mWriteToFile = false;
- bool mEnabled = false;
- bool mAddEntry = false;
- void loop();
- bool traceWhenNotified(LayersTraceProto* outProto);
- };
-};
-
-} // namespace android
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
new file mode 100644
index 0000000..d136e0b
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 "LayerTracing"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <SurfaceFlinger.h>
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <utils/SystemClock.h>
+#include <utils/Trace.h>
+
+#include "LayerTracing.h"
+#include "RingBuffer.h"
+
+namespace android {
+
+LayerTracing::LayerTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {
+ mBuffer = std::make_unique<RingBuffer<LayersTraceFileProto, LayersTraceProto>>();
+}
+
+LayerTracing::~LayerTracing() = default;
+
+bool LayerTracing::enable() {
+ std::scoped_lock lock(mTraceLock);
+ if (mEnabled) {
+ return false;
+ }
+ mBuffer->setSize(mBufferSizeInBytes);
+ mEnabled = true;
+ return true;
+}
+
+bool LayerTracing::disable() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return false;
+ }
+ mEnabled = false;
+ LayersTraceFileProto fileProto = createTraceFileProto();
+ mBuffer->writeToFile(fileProto, FILE_NAME);
+ mBuffer->reset();
+ return true;
+}
+
+bool LayerTracing::isEnabled() const {
+ std::scoped_lock lock(mTraceLock);
+ return mEnabled;
+}
+
+status_t LayerTracing::writeToFile() {
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return STATUS_OK;
+ }
+ LayersTraceFileProto fileProto = createTraceFileProto();
+ return mBuffer->writeToFile(fileProto, FILE_NAME);
+}
+
+void LayerTracing::setTraceFlags(uint32_t flags) {
+ std::scoped_lock lock(mTraceLock);
+ mFlags = flags;
+}
+
+void LayerTracing::setBufferSize(size_t bufferSizeInBytes) {
+ std::scoped_lock lock(mTraceLock);
+ mBufferSizeInBytes = bufferSizeInBytes;
+}
+
+bool LayerTracing::flagIsSet(uint32_t flags) const {
+ return (mFlags & flags) == flags;
+}
+
+LayersTraceFileProto LayerTracing::createTraceFileProto() const {
+ LayersTraceFileProto fileProto;
+ fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ return fileProto;
+}
+
+void LayerTracing::dump(std::string& result) const {
+ std::scoped_lock lock(mTraceLock);
+ base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ mBuffer->dump(result);
+}
+
+void LayerTracing::notify(const char* where) {
+ ATRACE_CALL();
+ std::scoped_lock lock(mTraceLock);
+ if (!mEnabled) {
+ return;
+ }
+
+ ATRACE_CALL();
+ LayersTraceProto entry;
+ entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
+ entry.set_where(where);
+ LayersProto layers(mFlinger.dumpDrawingStateProto(mFlags));
+
+ if (flagIsSet(LayerTracing::TRACE_EXTRA)) {
+ mFlinger.dumpOffscreenLayersProto(layers);
+ }
+ entry.mutable_layers()->Swap(&layers);
+
+ if (flagIsSet(LayerTracing::TRACE_HWC)) {
+ std::string hwcDump;
+ mFlinger.dumpHwc(hwcDump);
+ entry.set_hwc_blob(hwcDump);
+ }
+ if (!flagIsSet(LayerTracing::TRACE_COMPOSITION)) {
+ entry.set_excludes_composition_state(true);
+ }
+ mFlinger.dumpDisplayProto(entry);
+ mBuffer->emplace(std::move(entry));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/LayerTracing.h b/services/surfaceflinger/Tracing/LayerTracing.h
new file mode 100644
index 0000000..8ca3587
--- /dev/null
+++ b/services/surfaceflinger/Tracing/LayerTracing.h
@@ -0,0 +1,77 @@
+/*
+ * 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/LayerProtoHeader.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+
+#include <memory>
+#include <mutex>
+
+using namespace android::surfaceflinger;
+
+namespace android {
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer;
+
+class SurfaceFlinger;
+
+/*
+ * LayerTracing records layer states during surface flinging. Manages tracing state and
+ * configuration.
+ */
+class LayerTracing {
+public:
+ LayerTracing(SurfaceFlinger& flinger);
+ ~LayerTracing();
+ bool enable();
+ bool disable();
+ bool isEnabled() const;
+ status_t writeToFile();
+ LayersTraceFileProto createTraceFileProto() const;
+ void notify(const char* where);
+
+ enum : uint32_t {
+ TRACE_INPUT = 1 << 1,
+ TRACE_COMPOSITION = 1 << 2,
+ TRACE_EXTRA = 1 << 3,
+ TRACE_HWC = 1 << 4,
+ TRACE_BUFFERS = 1 << 5,
+ TRACE_ALL = TRACE_INPUT | TRACE_COMPOSITION | TRACE_EXTRA,
+ };
+ void setTraceFlags(uint32_t flags);
+ bool flagIsSet(uint32_t flags) const;
+ void setBufferSize(size_t bufferSizeInBytes);
+ void dump(std::string&) const;
+
+private:
+ static constexpr auto FILE_NAME = "/data/misc/wmtrace/layers_trace.winscope";
+
+ SurfaceFlinger& mFlinger;
+ uint32_t mFlags = TRACE_INPUT;
+ mutable std::mutex mTraceLock;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ std::unique_ptr<RingBuffer<LayersTraceFileProto, LayersTraceProto>> mBuffer
+ GUARDED_BY(mTraceLock);
+ size_t mBufferSizeInBytes GUARDED_BY(mTraceLock) = 20 * 1024 * 1024;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/RingBuffer.h b/services/surfaceflinger/Tracing/RingBuffer.h
new file mode 100644
index 0000000..3b2626d
--- /dev/null
+++ b/services/surfaceflinger/Tracing/RingBuffer.h
@@ -0,0 +1,119 @@
+/*
+ * 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/file.h>
+#include <android-base/stringprintf.h>
+
+#include <log/log.h>
+#include <utils/Errors.h>
+#include <utils/Timers.h>
+#include <utils/Trace.h>
+#include <chrono>
+#include <queue>
+
+namespace android {
+
+class SurfaceFlinger;
+
+template <typename FileProto, typename EntryProto>
+class RingBuffer {
+public:
+ size_t size() const { return mSizeInBytes; }
+ size_t used() const { return mUsedInBytes; }
+ size_t frameCount() const { return mStorage.size(); }
+ void setSize(size_t newSize) { mSizeInBytes = newSize; }
+ const std::string& front() const { return mStorage.front(); }
+ const std::string& back() const { return mStorage.back(); }
+
+ void reset() {
+ // use the swap trick to make sure memory is released
+ std::deque<std::string>().swap(mStorage);
+ mUsedInBytes = 0U;
+ }
+
+ void writeToProto(FileProto& fileProto) {
+ fileProto.mutable_entry()->Reserve(static_cast<int>(mStorage.size()) +
+ fileProto.entry().size());
+ for (const std::string& entry : mStorage) {
+ EntryProto* entryProto = fileProto.add_entry();
+ entryProto->ParseFromString(entry);
+ }
+ }
+
+ status_t writeToFile(FileProto& fileProto, std::string filename) {
+ ATRACE_CALL();
+ writeToProto(fileProto);
+ std::string output;
+ if (!fileProto.SerializeToString(&output)) {
+ ALOGE("Could not serialize proto.");
+ return UNKNOWN_ERROR;
+ }
+
+ // -rw-r--r--
+ const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ if (!android::base::WriteStringToFile(output, filename, mode, getuid(), getgid(), true)) {
+ ALOGE("Could not save the proto file.");
+ return PERMISSION_DENIED;
+ }
+ return NO_ERROR;
+ }
+
+ std::vector<std::string> emplace(std::string&& serializedProto) {
+ std::vector<std::string> replacedEntries;
+ size_t protoSize = static_cast<size_t>(serializedProto.size());
+ while (mUsedInBytes + protoSize > mSizeInBytes) {
+ if (mStorage.empty()) {
+ return {};
+ }
+ mUsedInBytes -= static_cast<size_t>(mStorage.front().size());
+ replacedEntries.emplace_back(mStorage.front());
+ mStorage.pop_front();
+ }
+ mUsedInBytes += protoSize;
+ mStorage.emplace_back(serializedProto);
+ return replacedEntries;
+ }
+
+ std::vector<std::string> emplace(EntryProto&& proto) {
+ std::string serializedProto;
+ proto.SerializeToString(&serializedProto);
+ return emplace(std::move(serializedProto));
+ }
+
+ void dump(std::string& result) const {
+ std::chrono::milliseconds duration(0);
+ if (frameCount() > 0) {
+ EntryProto entry;
+ entry.ParseFromString(mStorage.front());
+ duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::nanoseconds(systemTime() - entry.elapsed_realtime_nanos()));
+ }
+ 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);
+ }
+
+private:
+ size_t mUsedInBytes = 0U;
+ size_t mSizeInBytes = 0U;
+ std::deque<std::string> mStorage;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
index fb1d43b..378deb0 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.cpp
@@ -22,9 +22,9 @@
namespace android::surfaceflinger {
-proto::TransactionState TransactionProtoParser::toProto(
- const TransactionState& t, std::function<int32_t(const sp<IBinder>&)> getLayerId,
- std::function<int32_t(const sp<IBinder>&)> getDisplayId) {
+proto::TransactionState TransactionProtoParser::toProto(const TransactionState& t,
+ LayerHandleToIdFn getLayerId,
+ DisplayHandleToIdFn getDisplayId) {
proto::TransactionState proto;
proto.set_pid(t.originPid);
proto.set_uid(t.originUid);
@@ -42,10 +42,38 @@
return proto;
}
-proto::LayerState TransactionProtoParser::toProto(
- const layer_state_t& layer, std::function<int32_t(const sp<IBinder>&)> getLayerId) {
+proto::TransactionState TransactionProtoParser::toProto(
+ const std::map<int32_t /* layerId */, TracingLayerState>& states) {
+ proto::TransactionState proto;
+ for (auto& [layerId, state] : states) {
+ proto::LayerState layerProto = toProto(state, nullptr);
+ if (layerProto.has_buffer_data()) {
+ proto::LayerState_BufferData* bufferProto = layerProto.mutable_buffer_data();
+ bufferProto->set_buffer_id(state.bufferId);
+ bufferProto->set_width(state.bufferWidth);
+ bufferProto->set_height(state.bufferHeight);
+ }
+ layerProto.set_has_sideband_stream(state.hasSidebandStream);
+ layerProto.set_layer_id(state.layerId);
+ layerProto.set_parent_id(state.parentId);
+ layerProto.set_relative_parent_id(state.relativeParentId);
+ if (layerProto.has_window_info_handle()) {
+ layerProto.mutable_window_info_handle()->set_crop_layer_id(state.inputCropId);
+ }
+ proto.mutable_layer_changes()->Add(std::move(layerProto));
+ }
+ return proto;
+}
+
+proto::LayerState TransactionProtoParser::toProto(const layer_state_t& layer,
+ LayerHandleToIdFn getLayerId) {
proto::LayerState proto;
- proto.set_layer_id(layer.layerId);
+ if (getLayerId != nullptr) {
+ proto.set_layer_id(getLayerId(layer.surface));
+ } else {
+ proto.set_layer_id(layer.layerId);
+ }
+
proto.set_what(layer.what);
if (layer.what & layer_state_t::ePositionChanged) {
@@ -130,13 +158,13 @@
}
}
- if (layer.what & layer_state_t::eReparent) {
+ if ((layer.what & layer_state_t::eReparent) && getLayerId != nullptr) {
int32_t layerId = layer.parentSurfaceControlForChild
? getLayerId(layer.parentSurfaceControlForChild->getHandle())
: -1;
proto.set_parent_id(layerId);
}
- if (layer.what & layer_state_t::eRelativeLayerChanged) {
+ if ((layer.what & layer_state_t::eRelativeLayerChanged) && getLayerId != nullptr) {
int32_t layerId = layer.relativeLayerSurfaceControl
? getLayerId(layer.relativeLayerSurfaceControl->getHandle())
: -1;
@@ -164,8 +192,12 @@
transformProto->set_ty(inputInfo->transform.ty());
windowInfoProto->set_replace_touchable_region_with_crop(
inputInfo->replaceTouchableRegionWithCrop);
- windowInfoProto->set_crop_layer_id(
- getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+ if (getLayerId != nullptr) {
+ windowInfoProto->set_crop_layer_id(
+ getLayerId(inputInfo->touchableRegionCropHandle.promote()));
+ } else {
+ windowInfoProto->set_crop_layer_id(-1);
+ }
}
}
if (layer.what & layer_state_t::eBackgroundColorChanged) {
@@ -212,11 +244,13 @@
return proto;
}
-proto::DisplayState TransactionProtoParser::toProto(
- const DisplayState& display, std::function<int32_t(const sp<IBinder>&)> getDisplayId) {
+proto::DisplayState TransactionProtoParser::toProto(const DisplayState& display,
+ DisplayHandleToIdFn getDisplayId) {
proto::DisplayState proto;
proto.set_what(display.what);
- proto.set_id(getDisplayId(display.token));
+ if (getDisplayId != nullptr) {
+ proto.set_id(getDisplayId(display.token));
+ }
if (display.what & DisplayState::eLayerStackChanged) {
proto.set_layer_stack(display.layerStack.id);
@@ -238,9 +272,19 @@
return proto;
}
-TransactionState TransactionProtoParser::fromProto(
- const proto::TransactionState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle,
- std::function<sp<IBinder>(int32_t)> getDisplayHandle) {
+proto::LayerCreationArgs TransactionProtoParser::toProto(const TracingLayerCreationArgs& args) {
+ proto::LayerCreationArgs proto;
+ proto.set_layer_id(args.layerId);
+ proto.set_name(args.name);
+ proto.set_flags(args.flags);
+ proto.set_parent_id(args.parentId);
+ proto.set_mirror_from_id(args.mirrorFromId);
+ return proto;
+}
+
+TransactionState TransactionProtoParser::fromProto(const proto::TransactionState& proto,
+ LayerIdToHandleFn getLayerHandle,
+ DisplayIdToHandleFn getDisplayHandle) {
TransactionState t;
t.originPid = proto.pid();
t.originUid = proto.uid();
@@ -251,7 +295,7 @@
t.states.reserve(static_cast<size_t>(layerCount));
for (int i = 0; i < layerCount; i++) {
ComposerState s;
- s.state = std::move(fromProto(proto.layer_changes(i), getLayerHandle));
+ fromProto(proto.layer_changes(i), getLayerHandle, s.state);
t.states.add(s);
}
@@ -263,88 +307,118 @@
return t;
}
-layer_state_t TransactionProtoParser::fromProto(
- const proto::LayerState& proto, std::function<sp<IBinder>(int32_t)> getLayerHandle) {
- layer_state_t layer;
- layer.layerId = proto.layer_id();
- layer.what = proto.what();
+void TransactionProtoParser::fromProto(const proto::LayerCreationArgs& proto,
+ TracingLayerCreationArgs& outArgs) {
+ outArgs.layerId = proto.layer_id();
+ outArgs.name = proto.name();
+ outArgs.flags = proto.flags();
+ outArgs.parentId = proto.parent_id();
+ outArgs.mirrorFromId = proto.mirror_from_id();
+}
- if (layer.what & layer_state_t::ePositionChanged) {
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+ LayerIdToHandleFn getLayerHandle,
+ TracingLayerState& outState) {
+ fromProto(proto, getLayerHandle, static_cast<layer_state_t&>(outState));
+ if (proto.what() & layer_state_t::eReparent) {
+ outState.parentId = proto.parent_id();
+ outState.args.parentId = outState.parentId;
+ }
+ if (proto.what() & layer_state_t::eRelativeLayerChanged) {
+ outState.relativeParentId = proto.relative_parent_id();
+ }
+ if (proto.what() & layer_state_t::eInputInfoChanged) {
+ outState.inputCropId = proto.window_info_handle().crop_layer_id();
+ }
+ if (proto.what() & layer_state_t::eBufferChanged) {
+ const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
+ outState.bufferId = bufferProto.buffer_id();
+ outState.bufferWidth = bufferProto.width();
+ outState.bufferHeight = bufferProto.height();
+ }
+ if (proto.what() & layer_state_t::eSidebandStreamChanged) {
+ outState.hasSidebandStream = proto.has_sideband_stream();
+ }
+}
+
+void TransactionProtoParser::fromProto(const proto::LayerState& proto,
+ LayerIdToHandleFn getLayerHandle, layer_state_t& layer) {
+ layer.layerId = proto.layer_id();
+ layer.what |= proto.what();
+
+ if (getLayerHandle != nullptr) {
+ layer.surface = getLayerHandle(layer.layerId);
+ }
+
+ if (proto.what() & layer_state_t::ePositionChanged) {
layer.x = proto.x();
layer.y = proto.y();
}
- if (layer.what & layer_state_t::eLayerChanged) {
+ if (proto.what() & layer_state_t::eLayerChanged) {
layer.z = proto.z();
}
- if (layer.what & layer_state_t::eSizeChanged) {
+ if (proto.what() & layer_state_t::eSizeChanged) {
layer.w = proto.w();
layer.h = proto.h();
}
- if (layer.what & layer_state_t::eLayerStackChanged) {
+ if (proto.what() & layer_state_t::eLayerStackChanged) {
layer.layerStack.id = proto.layer_stack();
}
- if (layer.what & layer_state_t::eFlagsChanged) {
+ if (proto.what() & layer_state_t::eFlagsChanged) {
layer.flags = proto.flags();
layer.mask = proto.mask();
}
- if (layer.what & layer_state_t::eMatrixChanged) {
+ if (proto.what() & layer_state_t::eMatrixChanged) {
const proto::LayerState_Matrix22& matrixProto = proto.matrix();
layer.matrix.dsdx = matrixProto.dsdx();
layer.matrix.dsdy = matrixProto.dsdy();
layer.matrix.dtdx = matrixProto.dtdx();
layer.matrix.dtdy = matrixProto.dtdy();
}
- if (layer.what & layer_state_t::eCornerRadiusChanged) {
+ if (proto.what() & layer_state_t::eCornerRadiusChanged) {
layer.cornerRadius = proto.corner_radius();
}
- if (layer.what & layer_state_t::eBackgroundBlurRadiusChanged) {
+ if (proto.what() & layer_state_t::eBackgroundBlurRadiusChanged) {
layer.backgroundBlurRadius = proto.background_blur_radius();
}
- if (layer.what & layer_state_t::eAlphaChanged) {
+ if (proto.what() & layer_state_t::eAlphaChanged) {
layer.alpha = proto.alpha();
}
- if (layer.what & layer_state_t::eColorChanged) {
+ if (proto.what() & layer_state_t::eColorChanged) {
const proto::LayerState_Color3& colorProto = proto.color();
layer.color.r = colorProto.r();
layer.color.g = colorProto.g();
layer.color.b = colorProto.b();
}
- if (layer.what & layer_state_t::eTransparentRegionChanged) {
+ if (proto.what() & layer_state_t::eTransparentRegionChanged) {
LayerProtoHelper::readFromProto(proto.transparent_region(), layer.transparentRegion);
}
- if (layer.what & layer_state_t::eTransformChanged) {
+ if (proto.what() & layer_state_t::eTransformChanged) {
layer.transform = proto.transform();
}
- if (layer.what & layer_state_t::eTransformToDisplayInverseChanged) {
+ if (proto.what() & layer_state_t::eTransformToDisplayInverseChanged) {
layer.transformToDisplayInverse = proto.transform_to_display_inverse();
}
- if (layer.what & layer_state_t::eCropChanged) {
+ if (proto.what() & layer_state_t::eCropChanged) {
LayerProtoHelper::readFromProto(proto.crop(), layer.crop);
}
- if (layer.what & layer_state_t::eBufferChanged) {
+ if (proto.what() & layer_state_t::eBufferChanged) {
const proto::LayerState_BufferData& bufferProto = proto.buffer_data();
- layer.bufferData.buffer = new GraphicBuffer(bufferProto.width(), bufferProto.height(),
- HAL_PIXEL_FORMAT_RGBA_8888, 1, 0);
layer.bufferData.frameNumber = bufferProto.frame_number();
layer.bufferData.flags = Flags<BufferData::BufferDataChange>(bufferProto.flags());
layer.bufferData.cachedBuffer.id = bufferProto.cached_buffer_id();
}
- if (layer.what & layer_state_t::eSidebandStreamChanged) {
- native_handle_t* handle = native_handle_create(0, 0);
- layer.sidebandStream =
- proto.has_sideband_stream() ? NativeHandle::create(handle, true) : nullptr;
- }
- if (layer.what & layer_state_t::eApiChanged) {
+ if (proto.what() & layer_state_t::eApiChanged) {
layer.api = proto.api();
}
- if (layer.what & layer_state_t::eColorTransformChanged) {
+ if (proto.what() & layer_state_t::eColorTransformChanged) {
LayerProtoHelper::readFromProto(proto.color_transform(), layer.colorTransform);
}
- if (layer.what & layer_state_t::eBlurRegionsChanged) {
+ if (proto.what() & layer_state_t::eBlurRegionsChanged) {
layer.blurRegions.reserve(static_cast<size_t>(proto.blur_regions_size()));
for (int i = 0; i < proto.blur_regions_size(); i++) {
android::BlurRegion region;
@@ -353,20 +427,20 @@
}
}
- if (layer.what & layer_state_t::eReparent) {
+ if ((proto.what() & layer_state_t::eReparent) && (getLayerHandle != nullptr)) {
int32_t layerId = proto.parent_id();
layer.parentSurfaceControlForChild =
new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
nullptr, layerId);
}
- if (layer.what & layer_state_t::eRelativeLayerChanged) {
+ if ((proto.what() & layer_state_t::eRelativeLayerChanged) && (getLayerHandle != nullptr)) {
int32_t layerId = proto.relative_parent_id();
layer.relativeLayerSurfaceControl =
new SurfaceControl(SurfaceComposerClient::getDefault(), getLayerHandle(layerId),
nullptr, layerId);
}
- if ((layer.what & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
+ if ((proto.what() & layer_state_t::eInputInfoChanged) && proto.has_window_info_handle()) {
gui::WindowInfo inputInfo;
const proto::LayerState_WindowInfo& windowInfoProto = proto.window_info_handle();
@@ -385,10 +459,12 @@
inputInfo.replaceTouchableRegionWithCrop =
windowInfoProto.replace_touchable_region_with_crop();
int32_t layerId = windowInfoProto.crop_layer_id();
- inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
+ if (getLayerHandle != nullptr) {
+ inputInfo.touchableRegionCropHandle = getLayerHandle(layerId);
+ }
layer.windowInfoHandle = sp<gui::WindowInfoHandle>::make(inputInfo);
}
- if (layer.what & layer_state_t::eBackgroundColorChanged) {
+ if (proto.what() & layer_state_t::eBackgroundColorChanged) {
layer.bgColorAlpha = proto.bg_color_alpha();
layer.bgColorDataspace = static_cast<ui::Dataspace>(proto.bg_color_dataspace());
const proto::LayerState_Color3& colorProto = proto.color();
@@ -396,47 +472,48 @@
layer.color.g = colorProto.g();
layer.color.b = colorProto.b();
}
- if (layer.what & layer_state_t::eColorSpaceAgnosticChanged) {
+ if (proto.what() & layer_state_t::eColorSpaceAgnosticChanged) {
layer.colorSpaceAgnostic = proto.color_space_agnostic();
}
- if (layer.what & layer_state_t::eShadowRadiusChanged) {
+ if (proto.what() & layer_state_t::eShadowRadiusChanged) {
layer.shadowRadius = proto.shadow_radius();
}
- if (layer.what & layer_state_t::eFrameRateSelectionPriority) {
+ if (proto.what() & layer_state_t::eFrameRateSelectionPriority) {
layer.frameRateSelectionPriority = proto.frame_rate_selection_priority();
}
- if (layer.what & layer_state_t::eFrameRateChanged) {
+ if (proto.what() & layer_state_t::eFrameRateChanged) {
layer.frameRate = proto.frame_rate();
layer.frameRateCompatibility = static_cast<int8_t>(proto.frame_rate_compatibility());
layer.changeFrameRateStrategy = static_cast<int8_t>(proto.change_frame_rate_strategy());
}
- if (layer.what & layer_state_t::eFixedTransformHintChanged) {
+ if (proto.what() & layer_state_t::eFixedTransformHintChanged) {
layer.fixedTransformHint =
static_cast<ui::Transform::RotationFlags>(proto.fixed_transform_hint());
}
- if (layer.what & layer_state_t::eAutoRefreshChanged) {
+ if (proto.what() & layer_state_t::eAutoRefreshChanged) {
layer.autoRefresh = proto.auto_refresh();
}
- if (layer.what & layer_state_t::eTrustedOverlayChanged) {
+ if (proto.what() & layer_state_t::eTrustedOverlayChanged) {
layer.isTrustedOverlay = proto.is_trusted_overlay();
}
- if (layer.what & layer_state_t::eBufferCropChanged) {
+ if (proto.what() & layer_state_t::eBufferCropChanged) {
LayerProtoHelper::readFromProto(proto.buffer_crop(), layer.bufferCrop);
}
- if (layer.what & layer_state_t::eDestinationFrameChanged) {
+ if (proto.what() & layer_state_t::eDestinationFrameChanged) {
LayerProtoHelper::readFromProto(proto.destination_frame(), layer.destinationFrame);
}
- if (layer.what & layer_state_t::eDropInputModeChanged) {
+ if (proto.what() & layer_state_t::eDropInputModeChanged) {
layer.dropInputMode = static_cast<gui::DropInputMode>(proto.drop_input_mode());
}
- return layer;
}
-DisplayState TransactionProtoParser::fromProto(
- const proto::DisplayState& proto, std::function<sp<IBinder>(int32_t)> getDisplayHandle) {
+DisplayState TransactionProtoParser::fromProto(const proto::DisplayState& proto,
+ DisplayIdToHandleFn getDisplayHandle) {
DisplayState display;
display.what = proto.what();
- display.token = getDisplayHandle(proto.id());
+ if (getDisplayHandle != nullptr) {
+ display.token = getDisplayHandle(proto.id());
+ }
if (display.what & DisplayState::eLayerStackChanged) {
display.layerStack.id = proto.layer_stack();
diff --git a/services/surfaceflinger/Tracing/TransactionProtoParser.h b/services/surfaceflinger/Tracing/TransactionProtoParser.h
index a2b8889..b78d3d9 100644
--- a/services/surfaceflinger/Tracing/TransactionProtoParser.h
+++ b/services/surfaceflinger/Tracing/TransactionProtoParser.h
@@ -21,24 +21,53 @@
#include "TransactionState.h"
namespace android::surfaceflinger {
-class TransactionProtoParser {
-public:
- static proto::TransactionState toProto(
- const TransactionState&, std::function<int32_t(const sp<IBinder>&)> getLayerIdFn,
- std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn);
- static TransactionState fromProto(const proto::TransactionState&,
- std::function<sp<IBinder>(int32_t)> getLayerHandleFn,
- std::function<sp<IBinder>(int32_t)> getDisplayHandleFn);
-private:
- static proto::LayerState toProto(const layer_state_t&,
- std::function<int32_t(const sp<IBinder>&)> getLayerId);
- static proto::DisplayState toProto(const DisplayState&,
- std::function<int32_t(const sp<IBinder>&)> getDisplayId);
- static layer_state_t fromProto(const proto::LayerState&,
- std::function<sp<IBinder>(int32_t)> getLayerHandle);
- static DisplayState fromProto(const proto::DisplayState&,
- std::function<sp<IBinder>(int32_t)> getDisplayHandle);
+struct TracingLayerCreationArgs {
+ int32_t layerId;
+ std::string name;
+ uint32_t flags = 0;
+ int32_t parentId = -1;
+ int32_t mirrorFromId = -1;
};
-} // namespace android::surfaceflinger
\ No newline at end of file
+struct TracingLayerState : layer_state_t {
+ uint64_t bufferId;
+ uint32_t bufferHeight;
+ uint32_t bufferWidth;
+ bool hasSidebandStream;
+ int32_t parentId;
+ int32_t relativeParentId;
+ int32_t inputCropId;
+ TracingLayerCreationArgs args;
+};
+
+class TransactionProtoParser {
+public:
+ typedef std::function<sp<IBinder>(int32_t)> LayerIdToHandleFn;
+ typedef std::function<sp<IBinder>(int32_t)> DisplayIdToHandleFn;
+ typedef std::function<int32_t(const sp<IBinder>&)> LayerHandleToIdFn;
+ typedef std::function<int32_t(const sp<IBinder>&)> DisplayHandleToIdFn;
+
+ static proto::TransactionState toProto(const TransactionState&, LayerHandleToIdFn getLayerIdFn,
+ DisplayHandleToIdFn getDisplayIdFn);
+ static proto::TransactionState toProto(
+ const std::map<int32_t /* layerId */, TracingLayerState>&);
+
+ static proto::LayerCreationArgs toProto(const TracingLayerCreationArgs& args);
+
+ static TransactionState fromProto(const proto::TransactionState&,
+ LayerIdToHandleFn getLayerHandleFn,
+ DisplayIdToHandleFn getDisplayHandleFn);
+ static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandleFn,
+ TracingLayerState& outState);
+ static void fromProto(const proto::LayerCreationArgs&, TracingLayerCreationArgs& outArgs);
+
+private:
+ static proto::LayerState toProto(const layer_state_t&, LayerHandleToIdFn getLayerId);
+ static proto::DisplayState toProto(const DisplayState&, DisplayHandleToIdFn getDisplayId);
+ static void fromProto(const proto::LayerState&, LayerIdToHandleFn getLayerHandle,
+ layer_state_t& out);
+ static DisplayState fromProto(const proto::DisplayState&, DisplayIdToHandleFn getDisplayHandle);
+};
+
+} // namespace android::surfaceflinger
diff --git a/services/surfaceflinger/Tracing/TransactionTracing.cpp b/services/surfaceflinger/Tracing/TransactionTracing.cpp
new file mode 100644
index 0000000..b5966d5
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.cpp
@@ -0,0 +1,359 @@
+/*
+ * 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<std::string> removedEntries;
+ proto::TransactionTraceEntry entryProto;
+ for (const CommittedTransactions& entry : committedTransactions) {
+ 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 {
+ ALOGW("Could not find transaction id %" PRIu64, id);
+ }
+ }
+
+ std::string serializedProto;
+ entryProto.SerializeToString(&serializedProto);
+ entryProto.Clear();
+ std::vector<std::string> entries = mBuffer->emplace(std::move(serializedProto));
+ removedEntries.reserve(removedEntries.size() + entries.size());
+ removedEntries.insert(removedEntries.end(), std::make_move_iterator(entries.begin()),
+ std::make_move_iterator(entries.end()));
+ }
+
+ proto::TransactionTraceEntry removedEntryProto;
+ for (const std::string& removedEntry : removedEntries) {
+ removedEntryProto.ParseFromString(removedEntry);
+ updateStartingStateLocked(removedEntryProto);
+ removedEntryProto.Clear();
+ }
+ 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) {
+ proto::TransactionTraceEntry entry;
+ if (mBuffer->used() > 0) {
+ entry.ParseFromString(mBuffer->back());
+ }
+ return mBuffer->used() > 0 && entry.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, -1 /* mirrorFromId */};
+ if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+ ALOGW("Duplicate handles found. %p", layerHandle);
+ }
+ mLayerHandles[layerHandle] = layerId;
+ proto::LayerCreationArgs protoArgs = TransactionProtoParser::toProto(args);
+ proto::LayerCreationArgs protoArgsCopy = protoArgs;
+ mCreatedLayers.push_back(protoArgs);
+}
+
+void TransactionTracing::onMirrorLayerAdded(BBinder* layerHandle, int layerId,
+ const std::string& name, int mirrorFromId) {
+ std::scoped_lock lock(mTraceLock);
+ TracingLayerCreationArgs args{layerId, name, 0 /* flags */, -1 /* parentId */, mirrorFromId};
+ if (mLayerHandles.find(layerHandle) != mLayerHandles.end()) {
+ ALOGW("Duplicate handles found. %p", layerHandle);
+ }
+ mLayerHandles[layerHandle] = layerId;
+ mCreatedLayers.emplace_back(TransactionProtoParser::toProto(args));
+}
+
+void TransactionTracing::onLayerRemoved(int32_t layerId) {
+ mPendingRemovedLayers.emplace_back(layerId);
+ tryPushToTracingThread();
+}
+
+void TransactionTracing::onHandleRemoved(BBinder* layerHandle) {
+ std::scoped_lock lock(mTraceLock);
+ mLayerHandles.erase(layerHandle);
+}
+
+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());
+ if (it == mLayerHandles.end()) {
+ ALOGW("Could not find layer handle %p", layerHandle->localBinder());
+ return -1;
+ }
+ return 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();
+ TransactionProtoParser::fromProto(addedLayer, startingState.args);
+ }
+
+ // 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()) {
+ ALOGW("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()) {
+ 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);
+ if (mStartingStates.size() == 0) {
+ return;
+ }
+
+ entryProto->mutable_added_layers()->Reserve(static_cast<int32_t>(mStartingStates.size()));
+ for (auto& [layerId, state] : mStartingStates) {
+ entryProto->mutable_added_layers()->Add(TransactionProtoParser::toProto(state.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..26a3758
--- /dev/null
+++ b/services/surfaceflinger/Tracing/TransactionTracing.h
@@ -0,0 +1,128 @@
+/*
+ * 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 onMirrorLayerAdded(BBinder* layerHandle, int layerId, const std::string& name,
+ int mirrorFromId);
+ void onLayerRemoved(int layerId);
+ void onHandleRemoved(BBinder* layerHandle);
+ 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::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/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
index 935502a..802d22d 100644
--- a/services/surfaceflinger/TunnelModeEnabledReporter.h
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -22,6 +22,8 @@
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class Layer;
@@ -54,11 +56,6 @@
private:
mutable std::mutex mMutex;
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
GUARDED_BY(mMutex);
diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h
index ecd797a..4e08393 100644
--- a/services/surfaceflinger/WindowInfosListenerInvoker.h
+++ b/services/surfaceflinger/WindowInfosListenerInvoker.h
@@ -23,6 +23,8 @@
#include <utils/Mutex.h>
#include <unordered_map>
+#include "WpHash.h"
+
namespace android {
class SurfaceFlinger;
@@ -42,12 +44,6 @@
private:
void windowInfosReported();
- struct WpHash {
- size_t operator()(const wp<IBinder>& p) const {
- return std::hash<IBinder*>()(p.unsafe_get());
- }
- };
-
const sp<SurfaceFlinger> mSf;
std::mutex mListenersMutex;
std::unordered_map<wp<IBinder>, const sp<gui::IWindowInfosListener>, WpHash>
diff --git a/services/surfaceflinger/WpHash.h b/services/surfaceflinger/WpHash.h
new file mode 100644
index 0000000..86777b7
--- /dev/null
+++ b/services/surfaceflinger/WpHash.h
@@ -0,0 +1,25 @@
+/*
+ * 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
+
+namespace android {
+
+struct WpHash {
+ size_t operator()(const wp<IBinder>& p) const { return std::hash<IBinder*>()(p.unsafe_get()); }
+};
+
+} // namespace android
\ No newline at end of file
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 e7fb180..e31b502 100644
--- a/services/surfaceflinger/layerproto/transactions.proto
+++ b/services/surfaceflinger/layerproto/transactions.proto
@@ -39,21 +39,31 @@
}
message TransactionTraceEntry {
- int64 elapsed_time = 1;
+ int64 elapsed_realtime_nanos = 1;
int64 vsync_id = 2;
repeated TransactionState transactions = 3;
+ repeated LayerCreationArgs added_layers = 4;
+ repeated int32 removed_layers = 5;
+ repeated DisplayState added_displays = 6;
+ repeated int32 removed_displays = 7;
+}
+
+message LayerCreationArgs {
+ int32 layer_id = 1;
+ string name = 2;
+ uint32 flags = 3;
+ int32 parent_id = 4;
+ int32 mirror_from_id = 5;
}
message TransactionState {
- string tag = 2;
- int32 pid = 3;
- int32 uid = 4;
- int64 vsync_id = 5;
- int32 input_event_id = 6;
- int64 post_time = 7;
- repeated LayerState layer_changes = 9;
- repeated DisplayState new_displays = 10;
- repeated DisplayState display_changes = 11;
+ int32 pid = 1;
+ int32 uid = 2;
+ int64 vsync_id = 3;
+ int32 input_event_id = 4;
+ int64 post_time = 5;
+ repeated LayerState layer_changes = 6;
+ repeated DisplayState display_changes = 7;
}
// Keep insync with layer_state_t
@@ -130,8 +140,8 @@
eLayerSecure = 0x80;
eEnableBackpressure = 0x100;
};
- uint32 flags = 10;
- uint32 mask = 11;
+ uint32 flags = 9;
+ uint32 mask = 10;
message Matrix22 {
float dsdx = 1;
@@ -139,29 +149,29 @@
float dtdy = 3;
float dsdy = 4;
};
- Matrix22 matrix = 12;
- float corner_radius = 13;
- uint32 background_blur_radius = 14;
- int32 parent_id = 15;
- int32 relative_parent_id = 16;
+ Matrix22 matrix = 11;
+ float corner_radius = 12;
+ uint32 background_blur_radius = 13;
+ int32 parent_id = 14;
+ int32 relative_parent_id = 15;
- float alpha = 50;
+ float alpha = 16;
message Color3 {
float r = 1;
float g = 2;
float b = 3;
}
- Color3 color = 18;
- RegionProto transparent_region = 19;
- uint32 transform = 20;
- bool transform_to_display_inverse = 21;
- RectProto crop = 49;
+ Color3 color = 17;
+ RegionProto transparent_region = 18;
+ uint32 transform = 19;
+ bool transform_to_display_inverse = 20;
+ RectProto crop = 21;
message BufferData {
uint64 buffer_id = 1;
uint32 width = 2;
uint32 height = 3;
- uint64 frame_number = 5;
+ uint64 frame_number = 4;
enum BufferDataChange {
BufferDataChangeNone = 0;
@@ -169,14 +179,14 @@
frameNumberChanged = 0x02;
cachedBufferChanged = 0x04;
}
- uint32 flags = 6;
- uint64 cached_buffer_id = 7;
+ uint32 flags = 5;
+ uint64 cached_buffer_id = 6;
}
- BufferData buffer_data = 23;
- int32 api = 24;
- bool has_sideband_stream = 25;
- ColorTransformProto color_transform = 26;
- repeated BlurRegion blur_regions = 27;
+ BufferData buffer_data = 22;
+ int32 api = 23;
+ bool has_sideband_stream = 24;
+ ColorTransformProto color_transform = 25;
+ repeated BlurRegion blur_regions = 26;
message Transform {
float dsdx = 1;
@@ -189,38 +199,38 @@
message WindowInfo {
uint32 layout_params_flags = 1;
int32 layout_params_type = 2;
- RegionProto touchable_region = 4;
- int32 surface_inset = 5;
- bool focusable = 8;
- bool has_wallpaper = 9;
- float global_scale_factor = 10;
- int32 crop_layer_id = 13;
- bool replace_touchable_region_with_crop = 14;
- RectProto touchable_region_crop = 15;
- Transform transform = 16;
+ RegionProto touchable_region = 3;
+ int32 surface_inset = 4;
+ bool focusable = 5;
+ bool has_wallpaper = 6;
+ float global_scale_factor = 7;
+ int32 crop_layer_id = 8;
+ bool replace_touchable_region_with_crop = 9;
+ RectProto touchable_region_crop = 10;
+ Transform transform = 11;
}
- WindowInfo window_info_handle = 28;
- float bg_color_alpha = 31;
- int32 bg_color_dataspace = 32;
- bool color_space_agnostic = 33;
- float shadow_radius = 34;
- int32 frame_rate_selection_priority = 35;
- float frame_rate = 36;
- int32 frame_rate_compatibility = 37;
- int32 change_frame_rate_strategy = 38;
- uint32 fixed_transform_hint = 39;
- uint64 frame_number = 40;
- bool auto_refresh = 41;
- bool is_trusted_overlay = 42;
- RectProto buffer_crop = 44;
- RectProto destination_frame = 45;
+ WindowInfo window_info_handle = 27;
+ float bg_color_alpha = 28;
+ int32 bg_color_dataspace = 29;
+ bool color_space_agnostic = 30;
+ float shadow_radius = 31;
+ int32 frame_rate_selection_priority = 32;
+ float frame_rate = 33;
+ int32 frame_rate_compatibility = 34;
+ int32 change_frame_rate_strategy = 35;
+ uint32 fixed_transform_hint = 36;
+ uint64 frame_number = 37;
+ bool auto_refresh = 38;
+ bool is_trusted_overlay = 39;
+ RectProto buffer_crop = 40;
+ RectProto destination_frame = 41;
enum DropInputMode {
NONE = 0;
ALL = 1;
OBSCURED = 2;
};
- DropInputMode drop_input_mode = 48;
+ DropInputMode drop_input_mode = 42;
}
message DisplayState {
diff --git a/services/surfaceflinger/main_surfaceflinger.cpp b/services/surfaceflinger/main_surfaceflinger.cpp
index 673239d..caeff4a 100644
--- a/services/surfaceflinger/main_surfaceflinger.cpp
+++ b/services/surfaceflinger/main_surfaceflinger.cpp
@@ -63,18 +63,17 @@
return OK;
}
-static status_t startDisplayService() {
+static void startDisplayService() {
using android::frameworks::displayservice::V1_0::implementation::DisplayService;
using android::frameworks::displayservice::V1_0::IDisplayService;
sp<IDisplayService> displayservice = new DisplayService();
status_t err = displayservice->registerAsService();
+ // b/141930622
if (err != OK) {
- ALOGE("Could not register IDisplayService service.");
+ ALOGE("Did not register (deprecated) IDisplayService service.");
}
-
- return err;
}
int main(int, char**) {
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index 575e70d..39d7bd9 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -3,7 +3,7 @@
user system
group graphics drmrpc readproc
capabilities SYS_NICE
- onrestart restart zygote
+ onrestart restart --only-if-running zygote
task_profiles HighPerformance
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
socket pdx/system/vr/display/manager stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 78f8a2f..7702ea2 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -414,16 +414,6 @@
prop_name: "ro.surface_flinger.supports_background_blur"
}
-# Indicates whether Scheduler should use frame rate API when adjusting the
-# display refresh rate.
-prop {
- api_name: "use_frame_rate_api"
- type: Boolean
- scope: Public
- access: Readonly
- prop_name: "ro.surface_flinger.use_frame_rate_api"
-}
-
# Sets the timeout used to rate limit DISPLAY_UPDATE_IMMINENT Power HAL notifications.
# SurfaceFlinger wakeups will trigger this boost whenever they are separated by more than this
# duration (specified in milliseconds). A value of 0 disables the rate limit, and will result in
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
index 9c567d6..bf1e7e2 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-current.txt
@@ -152,10 +152,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
index ba60a7d..640b9fb 100644
--- a/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
+++ b/services/surfaceflinger/sysprop/api/SurfaceFlingerProperties-latest.txt
@@ -136,10 +136,6 @@
prop_name: "ro.surface_flinger.use_context_priority"
}
prop {
- api_name: "use_frame_rate_api"
- prop_name: "ro.surface_flinger.use_frame_rate_api"
- }
- prop {
api_name: "use_smart_90_for_video"
prop_name: "ro.surface_flinger.use_smart_90_for_video"
deprecated: true
diff --git a/services/surfaceflinger/tests/InvalidHandles_test.cpp b/services/surfaceflinger/tests/InvalidHandles_test.cpp
index 9cf7c09..d192a2d 100644
--- a/services/surfaceflinger/tests/InvalidHandles_test.cpp
+++ b/services/surfaceflinger/tests/InvalidHandles_test.cpp
@@ -52,17 +52,6 @@
}
};
-TEST_F(InvalidHandleTest, createSurfaceInvalidParentHandle) {
- // The createSurface is scheduled now, we could still get a created surface from createSurface.
- // Should verify if it actually added into current state by checking the screenshot.
- auto notSc = mScc->createSurface(String8("lolcats"), 19, 47, PIXEL_FORMAT_RGBA_8888, 0,
- mNotSc->getHandle());
- LayerCaptureArgs args;
- args.layerHandle = notSc->getHandle();
- ScreenCaptureResults captureResults;
- ASSERT_EQ(NAME_NOT_FOUND, ScreenCapture::captureLayers(args, captureResults));
-}
-
TEST_F(InvalidHandleTest, captureLayersInvalidHandle) {
LayerCaptureArgs args;
args.layerHandle = mNotSc->getHandle();
diff --git a/services/surfaceflinger/tests/LayerCallback_test.cpp b/services/surfaceflinger/tests/LayerCallback_test.cpp
index 91a5b52..5c16fee 100644
--- a/services/surfaceflinger/tests/LayerCallback_test.cpp
+++ b/services/surfaceflinger/tests/LayerCallback_test.cpp
@@ -26,6 +26,7 @@
namespace android {
using android::hardware::graphics::common::V1_1::BufferUsage;
+using SCHash = SurfaceComposerClient::SCHash;
::testing::Environment* const binderEnv =
::testing::AddGlobalTestEnvironment(new BinderEnvironment());
@@ -102,6 +103,24 @@
}
}
+ static void waitForCommitCallback(
+ CallbackHelper& helper,
+ const std::unordered_set<sp<SurfaceControl>, SCHash>& committedSc) {
+ CallbackData callbackData;
+ ASSERT_NO_FATAL_FAILURE(helper.getCallbackData(&callbackData));
+
+ const auto& surfaceControlStats = callbackData.surfaceControlStats;
+
+ ASSERT_EQ(surfaceControlStats.size(), committedSc.size()) << "wrong number of surfaces";
+
+ for (const auto& stats : surfaceControlStats) {
+ ASSERT_NE(stats.surfaceControl, nullptr) << "returned null surface control";
+
+ const auto& expectedSc = committedSc.find(stats.surfaceControl);
+ ASSERT_NE(expectedSc, committedSc.end()) << "unexpected surface control";
+ }
+ }
+
DisplayEventReceiver mDisplayEventReceiver;
int mEpollFd;
@@ -1085,4 +1104,29 @@
EXPECT_NO_FATAL_FAILURE(waitForCallback(callback, expected, true));
}
+TEST_F(LayerCallbackTest, CommitCallbackOffscreenLayer) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createBufferStateLayer());
+ sp<SurfaceControl> offscreenLayer =
+ createSurface(mClient, "Offscreen Layer", 0, 0, PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState, layer.get());
+
+ Transaction transaction;
+ CallbackHelper callback;
+ int err = fillTransaction(transaction, &callback, layer, true);
+ err |= fillTransaction(transaction, &callback, offscreenLayer, true);
+ if (err) {
+ GTEST_SUCCEED() << "test not supported";
+ return;
+ }
+
+ transaction.reparent(offscreenLayer, nullptr)
+ .addTransactionCommittedCallback(callback.function, callback.getContext());
+ transaction.apply();
+
+ std::unordered_set<sp<SurfaceControl>, SCHash> committedSc;
+ committedSc.insert(layer);
+ committedSc.insert(offscreenLayer);
+ EXPECT_NO_FATAL_FAILURE(waitForCommitCallback(callback, committedSc));
+}
} // namespace android
diff --git a/services/surfaceflinger/tests/MirrorLayer_test.cpp b/services/surfaceflinger/tests/MirrorLayer_test.cpp
index 3ec6da9..a921aa8 100644
--- a/services/surfaceflinger/tests/MirrorLayer_test.cpp
+++ b/services/surfaceflinger/tests/MirrorLayer_test.cpp
@@ -273,6 +273,61 @@
}
}
+// Test that a mirror layer can be screenshot when offscreen
+TEST_F(MirrorLayerTest, OffscreenMirrorScreenshot) {
+ const auto display = SurfaceComposerClient::getInternalDisplayToken();
+ ui::DisplayMode mode;
+ SurfaceComposerClient::getActiveDisplayMode(display, &mode);
+ const ui::Size& size = mode.resolution;
+
+ sp<SurfaceControl> grandchild =
+ createLayer("Grandchild layer", 50, 50, ISurfaceComposerClient::eFXSurfaceBufferState,
+ mChildLayer.get());
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(grandchild, Color::BLUE, 50, 50));
+ Rect childBounds = Rect(50, 50, 450, 450);
+
+ asTransaction([&](Transaction& t) {
+ t.setCrop(grandchild, Rect(0, 0, 50, 50)).show(grandchild);
+ t.setFlags(grandchild, layer_state_t::eLayerOpaque, layer_state_t::eLayerOpaque);
+ });
+
+ sp<SurfaceControl> mirrorLayer = nullptr;
+ {
+ // Run as system to get the ACCESS_SURFACE_FLINGER permission when mirroring
+ UIDFaker f(AID_SYSTEM);
+ // Mirror mChildLayer
+ mirrorLayer = mClient->mirrorSurface(mChildLayer.get());
+ ASSERT_NE(mirrorLayer, nullptr);
+ }
+
+ // Show the mirror layer, but don't reparent to a layer on screen.
+ Transaction().show(mirrorLayer).apply();
+
+ {
+ SCOPED_TRACE("Offscreen Mirror");
+ auto shot = screenshot();
+ shot->expectColor(Rect(0, 0, size.getWidth(), 50), Color::RED);
+ shot->expectColor(Rect(0, 0, 50, size.getHeight()), Color::RED);
+ shot->expectColor(Rect(450, 0, size.getWidth(), size.getHeight()), Color::RED);
+ shot->expectColor(Rect(0, 450, size.getWidth(), size.getHeight()), Color::RED);
+ shot->expectColor(Rect(100, 100, 450, 450), Color::GREEN);
+ shot->expectColor(Rect(50, 50, 100, 100), Color::BLUE);
+ }
+
+ {
+ SCOPED_TRACE("Capture Mirror");
+ // Capture just the mirror layer and child.
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = mirrorLayer->getHandle();
+ captureArgs.sourceCrop = childBounds;
+ std::unique_ptr<ScreenCapture> shot;
+ ScreenCapture::captureLayers(&shot, captureArgs);
+ shot->expectSize(childBounds.width(), childBounds.height());
+ shot->expectColor(Rect(0, 0, 50, 50), Color::BLUE);
+ shot->expectColor(Rect(50, 50, 400, 400), Color::GREEN);
+ }
+}
+
} // namespace android
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/ScreenCapture_test.cpp b/services/surfaceflinger/tests/ScreenCapture_test.cpp
index 95301b3..f9b3185 100644
--- a/services/surfaceflinger/tests/ScreenCapture_test.cpp
+++ b/services/surfaceflinger/tests/ScreenCapture_test.cpp
@@ -37,6 +37,8 @@
ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getActiveDisplayMode(display, &mode));
const ui::Size& resolution = mode.resolution;
+ mDisplaySize = resolution;
+
// Background surface
mBGSurfaceControl = createLayer(String8("BG Test Surface"), resolution.getWidth(),
resolution.getHeight(), 0);
@@ -72,6 +74,7 @@
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
std::unique_ptr<ScreenCapture> mCapture;
+ ui::Size mDisplaySize;
};
TEST_F(ScreenCaptureTest, SetFlagsSecureEUidSystem) {
@@ -515,18 +518,8 @@
}
TEST_F(ScreenCaptureTest, CaptureInvalidLayer) {
- sp<SurfaceControl> redLayer = createLayer(String8("Red surface"), 60, 60,
- ISurfaceComposerClient::eFXSurfaceBufferState);
-
- ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(redLayer, Color::RED, 60, 60));
-
- auto redLayerHandle = redLayer->getHandle();
- Transaction().reparent(redLayer, nullptr).apply();
- redLayer.clear();
- SurfaceComposerClient::Transaction().apply(true);
-
LayerCaptureArgs args;
- args.layerHandle = redLayerHandle;
+ args.layerHandle = new BBinder();
ScreenCaptureResults captureResults;
// Layer was deleted so captureLayers should fail with NAME_NOT_FOUND
@@ -840,6 +833,33 @@
Color{expectedColor, expectedColor, expectedColor, 255}, tolerance);
}
+TEST_F(ScreenCaptureTest, CaptureOffscreen) {
+ sp<SurfaceControl> layer;
+ ASSERT_NO_FATAL_FAILURE(layer = createLayer("test layer", 32, 32,
+ ISurfaceComposerClient::eFXSurfaceBufferState,
+ mBGSurfaceControl.get()));
+ ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32));
+
+ Transaction().show(layer).hide(mFGSurfaceControl).reparent(layer, nullptr).apply();
+
+ DisplayCaptureArgs displayCaptureArgs;
+ displayCaptureArgs.displayToken = mDisplay;
+
+ {
+ // Validate that the red layer is not on screen
+ ScreenCapture::captureDisplay(&mCapture, displayCaptureArgs);
+ mCapture->expectColor(Rect(0, 0, mDisplaySize.width, mDisplaySize.height),
+ {63, 63, 195, 255});
+ }
+
+ LayerCaptureArgs captureArgs;
+ captureArgs.layerHandle = layer->getHandle();
+
+ ScreenCapture::captureLayers(&mCapture, captureArgs);
+ mCapture->expectSize(32, 32);
+ mCapture->expectColor(Rect(0, 0, 32, 32), Color::RED);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 2082c42..28e8b8c 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -754,9 +754,7 @@
}
bool SurfaceInterceptorTest::surfaceCreationFound(const Increment& increment, bool foundSurface) {
- bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment) &&
- increment.surface_creation().w() == SIZE_UPDATE &&
- increment.surface_creation().h() == SIZE_UPDATE);
+ bool isMatch(increment.surface_creation().name() == getUniqueName(LAYER_NAME, increment));
if (isMatch && !foundSurface) {
foundSurface = true;
} else if (isMatch && foundSurface) {
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 f152ced..5568418 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",
@@ -107,7 +109,6 @@
"mock/MockEventThread.cpp",
"mock/MockFrameTimeline.cpp",
"mock/MockFrameTracer.cpp",
- "mock/MockMessageQueue.cpp",
"mock/MockNativeWindowSurface.cpp",
"mock/MockSurfaceInterceptor.cpp",
"mock/MockTimeStats.cpp",
@@ -118,6 +119,7 @@
static_libs: [
"android.hardware.common-V2-ndk",
"android.hardware.common.fmq-V1-ndk",
+ "android.hardware.graphics.common-V3-ndk",
"android.hardware.graphics.composer@2.1",
"android.hardware.graphics.composer@2.2",
"android.hardware.graphics.composer@2.3",
@@ -136,12 +138,14 @@
"libgui_mocks",
"liblayers_proto",
"libperfetto_client_experimental",
- "librenderengine_mocks",
"librenderengine",
+ "librenderengine_mocks",
+ "libscheduler",
"libserviceutils",
"libtimestats",
"libtimestats_atoms_proto",
"libtimestats_proto",
+ "libtonemap",
"libtrace_proto",
"perfetto_trace_protos",
],
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 40ef6e7..89a517f 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -45,7 +45,6 @@
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockTimeStats.h"
#include "mock/MockVsyncController.h"
#include "mock/system/window/MockNativeWindow.h"
@@ -88,6 +87,11 @@
constexpr int DEFAULT_SIDEBAND_STREAM = 51;
+MATCHER(IsIdentityMatrix, "") {
+ constexpr auto kIdentity = mat4();
+ return (mat4(arg) == kIdentity);
+}
+
class CompositionTest : public testing::Test {
public:
CompositionTest() {
@@ -95,7 +99,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
EXPECT_CALL(*mNativeWindow, query(NATIVE_WINDOW_WIDTH, _))
@@ -140,14 +143,11 @@
.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,
kHasMultipleConfigs);
-
- // Layer history should be created if there are multiple configs.
- ASSERT_TRUE(mFlinger.scheduler()->hasLayerHistory());
}
void setupForceGeometryDirty() {
@@ -186,7 +186,6 @@
Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
mock::TimeStats* mTimeStats = new mock::TimeStats();
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
Hwc2::mock::PowerAdvisor mPowerAdvisor;
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
@@ -331,9 +330,7 @@
template <typename Case>
static void setupCommonCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer,
- setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
- .Times(1);
+ EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, IsIdentityMatrix())).Times(1);
EXPECT_CALL(*test->mComposer, getDisplayRequests(HWC_DISPLAY, _, _, _)).Times(1);
EXPECT_CALL(*test->mComposer, acceptDisplayChanges(HWC_DISPLAY)).Times(1);
EXPECT_CALL(*test->mComposer, presentDisplay(HWC_DISPLAY, _)).Times(1);
@@ -373,24 +370,26 @@
}
static void setupHwcCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+ 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);
}
static void setupHwcClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _))
+ .Times(1);
}
static void setupHwcForcedClientCompositionCallExpectations(CompositionTest* test) {
- EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _)).Times(1);
+ EXPECT_CALL(*test->mComposer, validateDisplay(HWC_DISPLAY, _, _, _)).Times(1);
}
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));
@@ -452,9 +451,7 @@
template <typename Case>
static void setupCommonCompositionCallExpectations(CompositionTest* test) {
// TODO: This seems like an unnecessary call if display is powered off.
- EXPECT_CALL(*test->mComposer,
- setColorTransform(HWC_DISPLAY, _, Hwc2::ColorTransform::IDENTITY))
- .Times(1);
+ EXPECT_CALL(*test->mComposer, setColorTransform(HWC_DISPLAY, IsIdentityMatrix())).Times(1);
// TODO: This seems like an unnecessary call if display is powered off.
Case::CompositionType::setupHwcSetCallExpectations(test);
@@ -535,15 +532,16 @@
static void setupLatchedBuffer(CompositionTest* test, sp<BufferQueueLayer> layer) {
// TODO: Eliminate the complexity of actually creating a buffer
+ layer->setSizeForTest(LayerProperties::WIDTH, LayerProperties::HEIGHT);
status_t err =
layer->setDefaultBufferProperties(LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::FORMAT);
ASSERT_EQ(NO_ERROR, err);
Mock::VerifyAndClear(test->mRenderEngine);
- EXPECT_CALL(*test->mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
enqueueBuffer(test, layer);
- Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
bool ignoredRecomputeVisibleRegions;
layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, 0);
@@ -838,16 +836,16 @@
struct BaseLayerVariant {
template <typename L, typename F>
static sp<L> createLayerWithFactory(CompositionTest* test, F factory) {
- EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(0);
+ EXPECT_CALL(*test->mFlinger.scheduler(), postMessage(_)).Times(0);
sp<L> layer = factory();
// Layer should be registered with scheduler.
- EXPECT_EQ(1, test->mFlinger.scheduler()->layerHistorySize());
+ EXPECT_EQ(1u, test->mFlinger.scheduler()->layerHistorySize());
Mock::VerifyAndClear(test->mComposer);
Mock::VerifyAndClear(test->mRenderEngine);
- Mock::VerifyAndClearExpectations(test->mMessageQueue);
+ Mock::VerifyAndClearExpectations(test->mFlinger.scheduler());
initLayerDrawingStateAndComputeBounds(test, layer);
@@ -888,7 +886,7 @@
// Layer should be unregistered with scheduler.
test->mFlinger.commit();
- EXPECT_EQ(0, test->mFlinger.scheduler()->layerHistorySize());
+ EXPECT_EQ(0u, test->mFlinger.scheduler()->layerHistorySize());
}
};
@@ -901,7 +899,6 @@
FlingerLayerType layer = Base::template createLayerWithFactory<EffectLayer>(test, [test]() {
return new EffectLayer(
LayerCreationArgs(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata()));
});
@@ -940,7 +937,6 @@
FlingerLayerType layer =
Base::template createLayerWithFactory<BufferQueueLayer>(test, [test]() {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
args.textureName = test->mFlinger.mutableTexturePool().back();
return new BufferQueueLayer(args);
@@ -952,7 +948,6 @@
}
static void cleanupInjectedLayers(CompositionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, postMessage(_)).Times(1);
Base::cleanupInjectedLayers(test);
}
@@ -990,7 +985,6 @@
static FlingerLayerType createLayer(CompositionTest* test) {
LayerCreationArgs args(test->mFlinger.flinger(), sp<Client>(), "test-container-layer",
- LayerProperties::WIDTH, LayerProperties::HEIGHT,
LayerProperties::LAYER_FLAGS, LayerMetadata());
FlingerLayerType layer = new ContainerLayer(args);
Base::template initLayerDrawingStateAndComputeBounds(test, layer);
@@ -1038,9 +1032,10 @@
}
};
-template <IComposerClient::Composition CompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition CompositionType>
struct KeepCompositionTypeVariant {
- static constexpr hal::Composition TYPE = CompositionType;
+ static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+ CompositionType;
static void setupHwcSetCallExpectations(CompositionTest* test) {
if (!test->mDisplayOff) {
@@ -1055,10 +1050,11 @@
}
};
-template <IComposerClient::Composition InitialCompositionType,
- IComposerClient::Composition FinalCompositionType>
+template <aidl::android::hardware::graphics::composer3::Composition InitialCompositionType,
+ aidl::android::hardware::graphics::composer3::Composition FinalCompositionType>
struct ChangeCompositionTypeVariant {
- static constexpr hal::Composition TYPE = FinalCompositionType;
+ static constexpr aidl::android::hardware::graphics::composer3::Composition TYPE =
+ FinalCompositionType;
static void setupHwcSetCallExpectations(CompositionTest* test) {
if (!test->mDisplayOff) {
@@ -1072,8 +1068,9 @@
EXPECT_CALL(*test->mComposer, getChangedCompositionTypes(HWC_DISPLAY, _, _))
.WillOnce(DoAll(SetArgPointee<1>(std::vector<Hwc2::Layer>{
static_cast<Hwc2::Layer>(HWC_LAYER)}),
- SetArgPointee<2>(std::vector<IComposerClient::Composition>{
- FinalCompositionType}),
+ SetArgPointee<2>(
+ std::vector<aidl::android::hardware::graphics::composer3::
+ Composition>{FinalCompositionType}),
Return(Error::NONE)));
}
};
@@ -1267,25 +1264,28 @@
*/
TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedNormalBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedNormalBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenNormalBufferLayer) {
@@ -1299,25 +1299,28 @@
*/
TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedEffectLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedEffectLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::SOLID_COLOR,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, EffectLayerVariant<EffectLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SOLID_COLOR,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenEffectLayer) {
@@ -1331,25 +1334,28 @@
*/
TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSidebandBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::SIDEBAND>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedSidebandBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::SIDEBAND,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SidebandLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::SIDEBAND,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSidebandBufferLayer) {
@@ -1363,25 +1369,28 @@
*/
TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSecureBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedSecureBufferLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSecureBufferLayerOnSecureDisplay) {
@@ -1395,17 +1404,19 @@
*/
TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedSecureBufferLayerOnInsecureDisplayWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ InsecureDisplaySetupVariant, BufferLayerVariant<SecureLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenSecureBufferLayerOnInsecureDisplay) {
@@ -1420,22 +1431,24 @@
TEST_F(CompositionTest,
HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest,
HWCComposedBufferLayerWithSecureParentLayerOnInsecureDisplayWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<InsecureDisplaySetupVariant,
- ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
- ContainerLayerVariant<SecureLayerProperties>>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ InsecureDisplaySetupVariant,
+ ChildLayerVariant<BufferLayerVariant<ParentSecureLayerProperties>,
+ ContainerLayerVariant<SecureLayerProperties>>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenBufferLayerWithSecureParentLayerOnInsecureDisplay) {
@@ -1451,25 +1464,28 @@
*/
TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, HWCComposedCursorLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CURSOR>,
- HwcCompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR>,
+ HwcCompositionResultVariant>>();
}
TEST_F(CompositionTest, REComposedCursorLayer) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::CURSOR,
- IComposerClient::Composition::CLIENT>,
- RECompositionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<CursorLayerProperties>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CURSOR,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ RECompositionResultVariant>>();
}
TEST_F(CompositionTest, captureScreenCursorLayer) {
@@ -1486,7 +1502,8 @@
mDisplayOff = true;
displayRefreshCompositionDirtyGeometry<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
HwcCompositionResultVariant>>();
}
@@ -1494,7 +1511,8 @@
mDisplayOff = true;
displayRefreshCompositionDirtyFrame<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::DEVICE>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE>,
HwcCompositionResultVariant>>();
}
@@ -1502,8 +1520,9 @@
mDisplayOff = true;
displayRefreshCompositionDirtyFrame<CompositionCase<
PoweredOffDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- ChangeCompositionTypeVariant<IComposerClient::Composition::DEVICE,
- IComposerClient::Composition::CLIENT>,
+ ChangeCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::DEVICE,
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
RECompositionResultVariant>>();
}
@@ -1518,17 +1537,19 @@
*/
TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyGeometry) {
- displayRefreshCompositionDirtyGeometry<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionViaDebugOptionResultVariant>>();
+ displayRefreshCompositionDirtyGeometry<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
}
TEST_F(CompositionTest, DebugOptionForcingClientCompositionOfBufferLayerWithDirtyFrame) {
- displayRefreshCompositionDirtyFrame<
- CompositionCase<DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
- KeepCompositionTypeVariant<IComposerClient::Composition::CLIENT>,
- ForcedClientCompositionViaDebugOptionResultVariant>>();
+ displayRefreshCompositionDirtyFrame<CompositionCase<
+ DefaultDisplaySetupVariant, BufferLayerVariant<DefaultLayerProperties>,
+ KeepCompositionTypeVariant<
+ aidl::android::hardware::graphics::composer3::Composition::CLIENT>,
+ ForcedClientCompositionViaDebugOptionResultVariant>>();
}
} // namespace
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/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index 6cb3052..b1f704a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -50,7 +50,6 @@
});
injectMockScheduler();
- mFlinger.mutableEventQueue().reset(mMessageQueue);
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
mFlinger.mutableInterceptor() = mSurfaceInterceptor;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
index 7746e73..0a3437a 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTestHelpers.h
@@ -47,7 +47,6 @@
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockPowerAdvisor.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockNativeWindowSurface.h"
#include "mock/MockSchedulerCallback.h"
#include "mock/MockSurfaceInterceptor.h"
@@ -118,12 +117,11 @@
// to keep a reference to them for use in setting up call expectations.
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
Hwc2::mock::Composer* mComposer = nullptr;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
sp<mock::SurfaceInterceptor> mSurfaceInterceptor = new mock::SurfaceInterceptor;
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/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index cb690aa..67a0d7e 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -258,6 +258,15 @@
<< expectedTimestamp;
const auto& event = std::get<0>(args.value());
for (int i = 0; i < DisplayEventReceiver::kFrameTimelinesLength; i++) {
+ auto prediction =
+ mTokenManager->getPredictionsForToken(event.vsync.frameTimelines[i].vsyncId);
+ EXPECT_TRUE(prediction.has_value());
+ EXPECT_EQ(prediction.value().endTime, event.vsync.frameTimelines[i].deadlineTimestamp)
+ << "Deadline timestamp does not match cached value";
+ EXPECT_EQ(prediction.value().presentTime,
+ event.vsync.frameTimelines[i].expectedVSyncTimestamp)
+ << "Expected vsync timestamp does not match cached value";
+
if (i > 0) {
EXPECT_GT(event.vsync.frameTimelines[i].deadlineTimestamp,
event.vsync.frameTimelines[i - 1].deadlineTimestamp)
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 010c675..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;
@@ -114,8 +118,7 @@
sp<BufferStateLayer> FpsReporterTest::createBufferStateLayer(LayerMetadata metadata = {}) {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, metadata);
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
return new BufferStateLayer(args);
}
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 3fa1a2c..981ca1d 100644
--- a/services/surfaceflinger/tests/unittests/GameModeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -53,7 +53,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
@@ -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/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
index 655baf8..765dec3 100644
--- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp
@@ -36,6 +36,7 @@
#include "DisplayHardware/DisplayMode.h"
#include "DisplayHardware/HWComposer.h"
#include "DisplayHardware/Hal.h"
+#include "DisplayIdentificationTest.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/DisplayHardware/MockHWC2.h"
@@ -57,6 +58,29 @@
using ::testing::SetArgPointee;
using ::testing::StrictMock;
+TEST(HWComposerTest, isHeadless) {
+ Hwc2::mock::Composer* mHal = new StrictMock<Hwc2::mock::Composer>();
+ impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
+ ASSERT_TRUE(hwc.isHeadless());
+
+ const hal::HWDisplayId hwcId = 1;
+
+ EXPECT_CALL(*mHal, getDisplayIdentificationData(_, _, _))
+ .WillOnce(DoAll(SetArgPointee<2>(getExternalEdid()),
+ Return(hardware::graphics::composer::V2_1::Error::NONE)));
+
+ EXPECT_CALL(*mHal, setVsyncEnabled(_, _));
+ EXPECT_CALL(*mHal, setClientTargetSlotCount(_));
+
+ auto info = hwc.onHotplug(hwcId, hal::Connection::CONNECTED);
+ ASSERT_TRUE(info);
+ auto displayId = info->id;
+ ASSERT_FALSE(hwc.isHeadless());
+
+ hwc.disconnectDisplay(displayId);
+ ASSERT_TRUE(hwc.isHeadless());
+}
+
struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> {
MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection));
MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId));
@@ -86,7 +110,8 @@
}),
Return(hardware::graphics::composer::V2_4::Error::NONE)));
EXPECT_CALL(*mHal, registerCallback(_));
- EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+ EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+ .WillOnce(Return(false));
impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
hwc.setCallback(&mCallback);
@@ -104,7 +129,8 @@
EXPECT_CALL(*mHal, getLayerGenericMetadataKeys(_))
.WillOnce(Return(hardware::graphics::composer::V2_4::Error::UNSUPPORTED));
EXPECT_CALL(*mHal, registerCallback(_));
- EXPECT_CALL(*mHal, isVsyncPeriodSwitchSupported()).WillOnce(Return(false));
+ EXPECT_CALL(*mHal, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+ .WillOnce(Return(false));
impl::HWComposer hwc{std::unique_ptr<Hwc2::Composer>(mHal)};
hwc.setCallback(&mCallback);
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index e8795fe..cdb2240 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:
@@ -59,48 +59,55 @@
LayerHistoryTest() { mFlinger.resetScheduler(mScheduler); }
- void SetUp() override { ASSERT_TRUE(mScheduler->hasLayerHistory()); }
-
- LayerHistory& history() { return *mScheduler->mutableLayerHistory(); }
- const LayerHistory& history() const { return *mScheduler->mutableLayerHistory(); }
+ LayerHistory& history() { return mScheduler->mutableLayerHistory(); }
+ const LayerHistory& history() const { return mScheduler->mutableLayerHistory(); }
LayerHistory::Summary summarizeLayerHistory(nsecs_t now) {
- return history().summarize(*mScheduler->refreshRateConfigs(), now);
+ // LayerHistory::summarize makes no guarantee of the order of the elements in the summary
+ // however, for testing only, a stable order is required, therefore we sort the list here.
+ // Any tests requiring ordered results must create layers with names.
+ auto summary = history().summarize(*mScheduler->refreshRateConfigs(), now);
+ std::sort(summary.begin(), summary.end(),
+ [](const RefreshRateConfigs::LayerRequirement& a,
+ const RefreshRateConfigs::LayerRequirement& b) -> bool {
+ return a.name < b.name;
+ });
+ return summary;
}
size_t layerCount() const { return mScheduler->layerHistorySize(); }
- size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS { return history().mActiveLayersEnd; }
+ size_t activeLayerCount() const NO_THREAD_SAFETY_ANALYSIS {
+ return history().mActiveLayerInfos.size();
+ }
auto frequentLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isFrequent(now); });
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isFrequent(now);
+ });
}
auto animatingLayerCount(nsecs_t now) const NO_THREAD_SAFETY_ANALYSIS {
- const auto& infos = history().mLayerInfos;
- return std::count_if(infos.begin(),
- infos.begin() + static_cast<long>(history().mActiveLayersEnd),
- [now](const auto& pair) { return pair.second->isAnimating(now); });
+ const auto& infos = history().mActiveLayerInfos;
+ return std::count_if(infos.begin(), infos.end(), [now](const auto& pair) {
+ return pair.second.second->isAnimating(now);
+ });
}
void setDefaultLayerVote(Layer* layer,
LayerHistory::LayerVoteType vote) NO_THREAD_SAFETY_ANALYSIS {
- for (auto& [layerUnsafe, info] : history().mLayerInfos) {
- if (layerUnsafe == layer) {
- info->setDefaultLayerVote(vote);
- return;
- }
+ auto [found, layerPair] = history().findLayer(layer->getSequence());
+ if (found != LayerHistory::layerStatus::NotFound) {
+ layerPair->second->setDefaultLayerVote(vote);
}
}
- 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++) {
@@ -146,6 +153,8 @@
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+ // history().registerLayer(layer, LayerHistory::LayerVoteType::Max);
+
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
@@ -370,9 +379,9 @@
}
TEST_F(LayerHistoryTest, multipleLayers) {
- auto layer1 = createLayer();
- auto layer2 = createLayer();
- auto layer3 = createLayer();
+ auto layer1 = createLayer("A");
+ auto layer2 = createLayer("B");
+ auto layer3 = createLayer("C");
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer1, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
@@ -656,6 +665,29 @@
EXPECT_EQ(1, animatingLayerCount(time));
}
+TEST_F(LayerHistoryTest, getFramerate) {
+ auto layer = createLayer();
+
+ EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
+ EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate()));
+
+ nsecs_t time = systemTime();
+
+ EXPECT_EQ(1, layerCount());
+ EXPECT_EQ(0, activeLayerCount());
+ EXPECT_EQ(0, frequentLayerCount(time));
+ EXPECT_EQ(0, animatingLayerCount(time));
+
+ // layer is active but infrequent.
+ for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
+ history().record(layer.get(), time, time, LayerHistory::LayerUpdateType::Buffer);
+ time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ }
+
+ float expectedFramerate = 1e9f / MAX_FREQUENT_LAYER_PERIOD_NS.count();
+ EXPECT_FLOAT_EQ(expectedFramerate, history().getLayerFramerate(time, layer->getSequence()));
+}
+
TEST_F(LayerHistoryTest, heuristicLayer60Hz) {
const auto layer = createLayer();
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
@@ -770,8 +802,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/MessageQueueTest.cpp b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
index 17d1dd6..bd4dc59 100644
--- a/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/MessageQueueTest.cpp
@@ -41,7 +41,7 @@
struct MockHandler : MessageQueue::Handler {
using MessageQueue::Handler::Handler;
- MOCK_METHOD(void, dispatchCommit, (int64_t, nsecs_t), (override));
+ MOCK_METHOD(void, dispatchFrame, (int64_t, nsecs_t), (override));
};
explicit TestableMessageQueue(sp<MockHandler> handler)
@@ -96,7 +96,7 @@
EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
@@ -109,13 +109,13 @@
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(4567));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
EXPECT_EQ(4567, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
@@ -128,7 +128,7 @@
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(1234));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
ASSERT_TRUE(mEventQueue.getScheduledFrameTime());
EXPECT_EQ(1234, mEventQueue.getScheduledFrameTime()->time_since_epoch().count());
@@ -141,7 +141,7 @@
generateTokenForPredictions(
frametimeline::TimelineItem(startTime, endTime, presentTime)))
.WillOnce(Return(vsyncId));
- EXPECT_CALL(*mEventQueue.mHandler, dispatchCommit(vsyncId, presentTime)).Times(1);
+ EXPECT_CALL(*mEventQueue.mHandler, dispatchFrame(vsyncId, presentTime)).Times(1);
EXPECT_NO_FATAL_FAILURE(mEventQueue.vsyncCallback(presentTime, startTime, endTime));
EXPECT_FALSE(mEventQueue.getScheduledFrameTime());
@@ -152,7 +152,7 @@
.earliestVsync = presentTime};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timingAfterCallback)).WillOnce(Return(0));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
TEST_F(MessageQueueTest, commitWithDurationChange) {
@@ -164,7 +164,7 @@
.earliestVsync = 0};
EXPECT_CALL(mVSyncDispatch, schedule(mCallbackToken, timing)).WillOnce(Return(0));
- EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleCommit());
+ EXPECT_NO_FATAL_FAILURE(mEventQueue.scheduleFrame());
}
} // namespace
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/RefreshRateSelectionTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
index e388a6f..1e6e336 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectionTest.cpp
@@ -91,15 +91,14 @@
sp<BufferStateLayer> RefreshRateSelectionTest::createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-queue-layer", LAYER_FLAGS,
+ LayerMetadata());
return new BufferStateLayer(args);
}
sp<EffectLayer> RefreshRateSelectionTest::createEffectLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
- LayerMetadata());
+ LayerCreationArgs args(mFlinger.flinger(), client, "color-layer", LAYER_FLAGS, LayerMetadata());
return new EffectLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index 599b235..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,29 +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;
-
- // The scheduler should initially disable VSYNC.
- struct ExpectDisableVsync {
- ExpectDisableVsync(mock::SchedulerCallback& callback) {
- EXPECT_CALL(callback, setVsyncEnabled(false)).Times(1);
- }
- } mExpectDisableVsync{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));
@@ -106,7 +102,7 @@
} // namespace
TEST_F(SchedulerTest, invalidConnectionHandle) {
- Scheduler::ConnectionHandle handle;
+ ConnectionHandle handle;
const sp<IDisplayEventConnection> connection = mScheduler->createDisplayEventConnection(handle);
@@ -163,12 +159,12 @@
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(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
constexpr bool kPowerStateNormal = true;
mScheduler->setDisplayPowerState(kPowerStateNormal);
@@ -181,25 +177,23 @@
}
TEST_F(SchedulerTest, updateDisplayModes) {
- ASSERT_EQ(static_cast<size_t>(0), mScheduler->layerHistorySize());
- sp<mock::MockLayer> layer = sp<mock::MockLayer>::make(mFlinger.flinger());
- ASSERT_EQ(static_cast<size_t>(1), mScheduler->layerHistorySize());
+ ASSERT_EQ(0u, mScheduler->layerHistorySize());
+ 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(static_cast<size_t>(0), mScheduler->getNumActiveLayers());
+ ASSERT_EQ(0u, mScheduler->getNumActiveLayers());
mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer);
- ASSERT_EQ(static_cast<size_t>(1), mScheduler->getNumActiveLayers());
+ 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) {
@@ -211,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);
}
@@ -232,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);
@@ -249,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 3b40965..fe5f9e0 100644
--- a/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SetFrameRateTest.cpp
@@ -33,7 +33,6 @@
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockVsyncController.h"
namespace android {
@@ -49,6 +48,8 @@
using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+using scheduler::LayerHistory;
+
using FrameRate = Layer::FrameRate;
using FrameRateCompatibility = Layer::FrameRateCompatibility;
@@ -70,8 +71,8 @@
std::string name() override { return "BufferStateLayer"; }
sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, LayerMetadata());
+ LayerCreationArgs args(flinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS,
+ LayerMetadata());
return new BufferStateLayer(args);
}
};
@@ -81,7 +82,7 @@
std::string name() override { return "EffectLayer"; }
sp<Layer> createLayer(TestableSurfaceFlinger& flinger) override {
sp<Client> client;
- LayerCreationArgs args(flinger.flinger(), client, "color-layer", WIDTH, HEIGHT, LAYER_FLAGS,
+ LayerCreationArgs args(flinger.flinger(), client, "color-layer", LAYER_FLAGS,
LayerMetadata());
return new EffectLayer(args);
}
@@ -112,7 +113,6 @@
void commitTransaction();
TestableSurfaceFlinger mFlinger;
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
std::vector<sp<Layer>> mLayers;
};
@@ -122,12 +122,9 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableUseFrameRateApi() = true;
-
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
}
void SetFrameRateTest::addChild(sp<Layer> layer, sp<Layer> child) {
@@ -174,7 +171,7 @@
namespace {
TEST_P(SetFrameRateTest, SetAndGet) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -185,7 +182,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParent) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -210,7 +207,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentAllVote) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -249,7 +246,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChild) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -274,7 +271,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAllVote) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -313,7 +310,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildAddAfterVote) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -343,7 +340,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetChildRemoveAfterVote) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -374,7 +371,7 @@
}
TEST_P(SetFrameRateTest, SetAndGetParentNotInTree) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
@@ -456,24 +453,20 @@
parent->setFrameRate(FRAME_RATE_VOTE1);
commitTransaction();
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ auto& history = mFlinger.mutableScheduler().mutableLayerHistory();
+ history.record(parent.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
+ history.record(child.get(), 0, 0, LayerHistory::LayerUpdateType::Buffer);
- const auto layerHistorySummary =
- mFlinger.mutableScheduler()
- .mutableLayerHistory()
- ->summarize(*mFlinger.mutableScheduler().refreshRateConfigs(), 0);
- ASSERT_EQ(2u, layerHistorySummary.size());
- EXPECT_EQ(FRAME_RATE_VOTE1.rate, layerHistorySummary[0].desiredRefreshRate);
- EXPECT_EQ(FRAME_RATE_VOTE1.rate, layerHistorySummary[1].desiredRefreshRate);
+ const auto configs = mFlinger.mutableScheduler().refreshRateConfigs();
+ const auto summary = history.summarize(*configs, 0);
+
+ ASSERT_EQ(2u, summary.size());
+ EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[0].desiredRefreshRate);
+ EXPECT_EQ(FRAME_RATE_VOTE1.rate, summary[1].desiredRefreshRate);
}
TEST_P(SetFrameRateTest, addChildForParentWithTreeVote) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
const auto& layerFactory = GetParam();
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
index 8c30341..f7d34ac 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_CreateDisplayTest.cpp
@@ -52,7 +52,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
TEST_F(CreateDisplayTest, createDisplaySetsCurrentStateForSecureDisplay) {
@@ -87,7 +87,7 @@
// Cleanup conditions
// Creating the display commits a display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
index 7087fb6..c7e61c9 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DestroyDisplayTest.cpp
@@ -41,7 +41,7 @@
EXPECT_CALL(*mSurfaceInterceptor, saveDisplayDeletion(_)).Times(1);
// Destroying the display commits a display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
new file mode 100644
index 0000000..56a0506
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_DisplayModeSwitching.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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, isSupported(Hwc2::Composer::OptionalFeature::RefreshRateSwitching))
+ .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_HotplugTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
index 29ff0cd..c9a2b00 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_HotplugTest.cpp
@@ -39,7 +39,7 @@
// Call Expectations
// We expect a scheduled commit for the display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
@@ -86,7 +86,7 @@
// Call Expectations
// We expect a scheduled commit for the display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// --------------------------------------------------------------------
// Invocation
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/SurfaceFlinger_OnInitializeDisplaysTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
index e1b44cf..37cf05e 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_OnInitializeDisplaysTest.cpp
@@ -47,7 +47,7 @@
Case::Display::setupHwcGetActiveConfigCallExpectations(this);
// We expect a scheduled commit for the display transaction.
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
index 6edebd4..b57feff 100644
--- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_SetPowerModeInternalTest.cpp
@@ -273,7 +273,7 @@
}
static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) {
- EXPECT_CALL(*test->mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*test->mFlinger.scheduler(), scheduleFrame()).Times(1);
}
static void setupSurfaceInterceptorCallExpectations(DisplayTransactionTest* test,
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 1d21bd4..364d8f1 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -28,23 +28,30 @@
#include "mock/MockVSyncTracker.h"
#include "mock/MockVsyncController.h"
-namespace android {
+namespace android::scheduler {
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ICompositor {
public:
- TestableScheduler(const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback)
+ TestableScheduler(std::shared_ptr<RefreshRateConfigs> configs, ISchedulerCallback& callback)
: TestableScheduler(std::make_unique<mock::VsyncController>(),
- std::make_unique<mock::VSyncTracker>(), refreshRateConfigs,
+ std::make_unique<mock::VSyncTracker>(), std::move(configs),
callback) {}
- TestableScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController,
- std::unique_ptr<scheduler::VSyncTracker> vsyncTracker,
- const std::shared_ptr<scheduler::RefreshRateConfigs>& refreshRateConfigs,
- ISchedulerCallback& callback)
- : Scheduler({std::move(vsyncController), std::move(vsyncTracker), nullptr},
- refreshRateConfigs, callback, createLayerHistory(),
- {.useContentDetection = true}) {}
+ 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) {
+ // Execute task to prevent broken promise exception on destruction.
+ handler->handleMessage(Message());
+ });
+ }
+
+ MOCK_METHOD(void, scheduleFrame, (), (override));
+ MOCK_METHOD(void, postMessage, (sp<MessageHandler>&&), (override));
// Used to inject mock event thread.
ConnectionHandle createConnection(std::unique_ptr<EventThread> eventThread) {
@@ -58,20 +65,16 @@
auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; }
auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; }
- bool hasLayerHistory() const { return static_cast<bool>(mLayerHistory); }
-
- auto* mutableLayerHistory() { return mLayerHistory.get(); }
+ auto& mutableLayerHistory() { return mLayerHistory; }
size_t layerHistorySize() NO_THREAD_SAFETY_ANALYSIS {
- if (!mLayerHistory) return 0;
- return mutableLayerHistory()->mLayerInfos.size();
+ return mLayerHistory.mActiveLayerInfos.size() + mLayerHistory.mInactiveLayerInfos.size();
}
auto refreshRateConfigs() { return holdRefreshRateConfigs(); }
size_t getNumActiveLayers() NO_THREAD_SAFETY_ANALYSIS {
- if (!mLayerHistory) return 0;
- return mutableLayerHistory()->mActiveLayersEnd;
+ return mLayerHistory.mActiveLayerInfos.size();
}
void replaceTouchTimer(int64_t millis) {
@@ -86,32 +89,29 @@
}
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; }
+ void composite(nsecs_t) override {}
+ 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 c23fcc7..0067997 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"
@@ -73,20 +74,11 @@
return nullptr;
}
- std::unique_ptr<MessageQueue> createMessageQueue(ICompositor& compositor) override {
- return std::make_unique<android::impl::MessageQueue>(compositor);
- }
-
std::unique_ptr<scheduler::VsyncConfiguration> createVsyncConfiguration(
Fps /*currentRefreshRate*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
- std::unique_ptr<Scheduler> createScheduler(
- const std::shared_ptr<scheduler::RefreshRateConfigs>&, ISchedulerCallback&) override {
- return nullptr;
- }
-
sp<SurfaceInterceptor> createSurfaceInterceptor() override {
return new android::impl::SurfaceInterceptor();
}
@@ -178,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.
@@ -206,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))
@@ -233,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) {
@@ -276,7 +270,8 @@
layer->editCompositionState()->sidebandStream = sidebandStream;
}
- void setLayerCompositionType(const sp<Layer>& layer, hal::Composition type) {
+ void setLayerCompositionType(const sp<Layer>& layer,
+ aidl::android::hardware::graphics::composer3::Composition type) {
auto outputLayer = findOutputLayerForDisplay(layer, mFlinger->getDefaultDisplayDevice());
LOG_ALWAYS_FATAL_IF(!outputLayer);
auto& state = outputLayer->editState();
@@ -314,6 +309,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(
@@ -375,6 +375,7 @@
auto& getTransactionQueue() { return mFlinger->mTransactionQueue; }
auto& getPendingTransactionQueue() { return mFlinger->mPendingTransactionQueues; }
+ auto& getTransactionCommittedSignals() { return mFlinger->mTransactionCommittedSignals; }
auto setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
@@ -388,7 +389,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);
@@ -401,6 +402,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.
*/
@@ -430,7 +446,6 @@
auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; }
auto& mutableDisplays() { return mFlinger->mDisplays; }
auto& mutableDrawingState() { return mFlinger->mDrawingState; }
- auto& mutableEventQueue() { return mFlinger->mEventQueue; }
auto& mutableGeometryDirty() { return mFlinger->mGeometryDirty; }
auto& mutableInterceptor() { return mFlinger->mInterceptor; }
auto& mutableMainThreadId() { return mFlinger->mMainThreadId; }
@@ -445,7 +460,6 @@
auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; }
auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; }
auto& mutablePrimaryHwcDisplayId() { return getHwComposer().mPrimaryHwcDisplayId; }
- auto& mutableUseFrameRateApi() { return mFlinger->useFrameRateApi; }
auto& mutableActiveDisplayToken() { return mFlinger->mActiveDisplayToken; }
auto fromHandle(const sp<IBinder>& handle) {
@@ -460,7 +474,6 @@
mutableDisplays().clear();
mutableCurrentState().displays.clear();
mutableDrawingState().displays.clear();
- mutableEventQueue().reset();
mutableInterceptor().clear();
mFlinger->mScheduler.reset();
mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>());
@@ -491,7 +504,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;
@@ -645,10 +658,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)
@@ -684,7 +697,7 @@
auto& mutableDisplayDevice() { return mFlinger.mutableDisplays()[mDisplayToken]; }
auto& setActiveMode(DisplayModeId mode) {
- mActiveModeId = mode;
+ mCreationArgs.activeModeId = mode;
return *this;
}
@@ -739,14 +752,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);
@@ -764,19 +792,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 05551b4..16d4b59 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -24,12 +24,11 @@
#include <gtest/gtest.h>
#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
+#include <ui/MockFence.h>
#include <utils/String8.h>
-#include "TestableScheduler.h"
#include "TestableSurfaceFlinger.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
#include "mock/MockVsyncController.h"
namespace android {
@@ -46,7 +45,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
}
@@ -74,20 +72,27 @@
EXPECT_CALL(*mVSyncTracker, currentPeriod())
.WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+ EXPECT_CALL(*mFenceUnsignaled, getStatus())
+ .WillRepeatedly(Return(Fence::Status::Unsignaled));
+ EXPECT_CALL(*mFenceUnsignaled2, getStatus())
+ .WillRepeatedly(Return(Fence::Status::Unsignaled));
+ EXPECT_CALL(*mFenceSignaled, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+ EXPECT_CALL(*mFenceSignaled2, getStatus()).WillRepeatedly(Return(Fence::Status::Signaled));
+
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.setupScheduler(std::unique_ptr<mock::VsyncController>(mVsyncController),
std::unique_ptr<mock::VSyncTracker>(mVSyncTracker),
std::move(eventThread), std::move(sfEventThread));
}
- TestableScheduler* mScheduler;
TestableSurfaceFlinger mFlinger;
- std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>();
-
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
mock::VsyncController* mVsyncController = new mock::VsyncController();
mock::VSyncTracker* mVSyncTracker = new mock::VSyncTracker();
+ mock::MockFence* mFenceUnsignaled = new mock::MockFence();
+ mock::MockFence* mFenceSignaled = new mock::MockFence();
+ mock::MockFence* mFenceUnsignaled2 = new mock::MockFence();
+ mock::MockFence* mFenceSignaled2 = new mock::MockFence();
struct TransactionInfo {
Vector<ComposerState> states;
@@ -124,9 +129,18 @@
transaction.frameTimelineInfo = frameTimelineInfo;
}
+ void setupSingleWithComposer(TransactionInfo& transaction, uint32_t flags,
+ bool syncInputWindows, int64_t desiredPresentTime,
+ bool isAutoTimestamp, const FrameTimelineInfo& frameTimelineInfo,
+ const Vector<ComposerState>* states) {
+ setupSingle(transaction, flags, syncInputWindows, desiredPresentTime, isAutoTimestamp,
+ frameTimelineInfo);
+ transaction.states = *states;
+ }
+
void NotPlacedOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transaction;
setupSingle(transaction, flags, syncInputWindows,
/*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
@@ -156,7 +170,7 @@
void PlaceOnTransactionQueue(uint32_t flags, bool syncInputWindows) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
// first check will see desired present time has not passed,
// but afterwards it will look like the desired present time has passed
@@ -187,9 +201,9 @@
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
nsecs_t time = systemTime();
if (!syncInputWindows) {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(2);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(2);
} else {
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
}
// transaction that should go on the pending thread
TransactionInfo transactionA;
@@ -245,6 +259,188 @@
EXPECT_EQ(0u, transactionQueue.size());
}
+ void Flush_removesUnsignaledFromTheQueue(Vector<ComposerState> state1,
+ Vector<ComposerState> state2,
+ bool updateApplyToken = true) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+
+ TransactionInfo transactionA;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+
+ TransactionInfo transactionB;
+ if (updateApplyToken) {
+ transactionB.applyToken = sp<IBinder>();
+ }
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_removesFromTheQueue(const Vector<ComposerState>& state) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ TransactionInfo transaction;
+ setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state);
+
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(1u, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_keepsInTheQueue(const Vector<ComposerState>& state) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ TransactionInfo transaction;
+ setupSingleWithComposer(transaction, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ systemTime(), /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state);
+
+ mFlinger.setTransactionState(transaction.frameTimelineInfo, transaction.states,
+ transaction.displays, transaction.flags,
+ transaction.applyToken, transaction.inputWindowCommands,
+ transaction.desiredPresentTime, transaction.isAutoTimestamp,
+ transaction.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transaction.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(1u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ void Flush_KeepsUnsignaledInTheQueue(const Vector<ComposerState>& state1,
+ const Vector<ComposerState>& state2,
+ bool updateApplyToken = true,
+ uint32_t pendingTransactionQueueSize = 1u) {
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ auto time = systemTime();
+ TransactionInfo transactionA;
+ TransactionInfo transactionB;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+ if (updateApplyToken) {
+ transactionB.applyToken = sp<IBinder>();
+ }
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(pendingTransactionQueueSize, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ }
+
+ void Flush_removesSignaledFromTheQueue(const Vector<ComposerState>& state1,
+ const Vector<ComposerState>& state2) {
+ ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+
+ auto time = systemTime();
+ TransactionInfo transactionA;
+ TransactionInfo transactionB;
+ setupSingleWithComposer(transactionA, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state1);
+ setupSingleWithComposer(transactionB, ISurfaceComposer::eSynchronous,
+ /*syncInputWindows*/ false,
+ /*desiredPresentTime*/ time, /*isAutoTimestamp*/ true,
+ FrameTimelineInfo{}, &state2);
+ mFlinger.setTransactionState(transactionA.frameTimelineInfo, transactionA.states,
+ transactionA.displays, transactionA.flags,
+ transactionA.applyToken, transactionA.inputWindowCommands,
+ transactionA.desiredPresentTime, transactionA.isAutoTimestamp,
+ transactionA.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionA.id);
+ mFlinger.setTransactionState(transactionB.frameTimelineInfo, transactionB.states,
+ transactionB.displays, transactionB.flags,
+ transactionB.applyToken, transactionB.inputWindowCommands,
+ transactionB.desiredPresentTime, transactionB.isAutoTimestamp,
+ transactionB.uncacheBuffer, mHasListenerCallbacks, mCallbacks,
+ transactionB.id);
+
+ mFlinger.flushTransactionQueues();
+ EXPECT_EQ(0u, mFlinger.getPendingTransactionQueue().size());
+ EXPECT_EQ(0u, mFlinger.getTransactionQueue().size());
+ EXPECT_EQ(2ul, mFlinger.getTransactionCommittedSignals().size());
+ }
+
+ static Vector<ComposerState> createComposerStateVector(const ComposerState& state1,
+ const ComposerState& state2) {
+ Vector<ComposerState> states;
+ states.push_back(state1);
+ states.push_back(state2);
+ return states;
+ }
+
+ static Vector<ComposerState> createComposerStateVector(const ComposerState& state) {
+ Vector<ComposerState> states;
+ states.push_back(state);
+ return states;
+ }
+
+ static ComposerState createComposerState(int layerId, sp<Fence> fence,
+ uint32_t stateFlags = layer_state_t::eBufferChanged) {
+ ComposerState composer_state;
+ composer_state.state.bufferData.acquireFence = std::move(fence);
+ composer_state.state.layerId = layerId;
+ composer_state.state.bufferData.flags = BufferData::BufferDataChange::fenceChanged;
+ composer_state.state.flags = stateFlags;
+ return composer_state;
+ }
+
bool mHasListenerCallbacks = false;
std::vector<ListenerCallbacks> mCallbacks;
int mTransactionNumber = 0;
@@ -252,7 +448,7 @@
TEST_F(TransactionApplicationTest, Flush_RemovesFromQueue) {
ASSERT_EQ(0u, mFlinger.getTransactionQueue().size());
- EXPECT_CALL(*mMessageQueue, scheduleCommit()).Times(1);
+ EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
TransactionInfo transactionA; // transaction to go on pending queue
setupSingle(transactionA, /*flags*/ 0, /*syncInputWindows*/ false,
@@ -327,4 +523,216 @@
auto ret = mFlinger.fromHandle(badHandle);
EXPECT_EQ(nullptr, ret.promote().get());
}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSingleUnSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsUnSignaledInTheQueue_NonBufferCropChange_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled, layer_state_t::eCropChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsUnSignaledInTheQueue_NonBufferChangeClubed_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled,
+ layer_state_t::eCropChanged | layer_state_t::eBufferChanged)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsInTheQueueSameApplyTokenMultiState_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_MultipleStateTransaction_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemoveSignaledWithUnsignaledIntact_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepsTransactionInTheQueueSameApplyToken_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsTransactionInTheQueue_LatchUnsignaled_Auto) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Auto;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ true,
+ /*pendingTransactionQueueSize*/ 2u);
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueSameLayerId_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepsInTheQueueDifferentLayerId_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_keepsInTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_KeepInTheQueueDifferentApplyToken_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheQueueSameApplyToken_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(1ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_KeepInTheUnsignaledTheQueue_LatchUnsignaled_Disabled) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Disabled;
+ Flush_KeepsUnsignaledInTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceUnsignaled)),
+ /*updateApplyToken*/ false);
+ EXPECT_EQ(0ul, mFlinger.getTransactionCommittedSignals().size());
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesFromTheQueueSameLayerId_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 1, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesFromTheQueueDifferentLayerId_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesFromTheQueue(
+ createComposerStateVector(createComposerState(/*layerId*/ 1, mFenceUnsignaled),
+ createComposerState(/*layerId*/ 2, mFenceSignaled)));
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesSignaledFromTheQueue_LatchUnSignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesSignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled2)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesFromTheQueueDifferentApplyToken_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1, mFenceSignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2,
+ mFenceUnsignaled)));
+}
+
+TEST_F(TransactionApplicationTest,
+ Flush_RemovesUnsignaledFromTheQueueSameApplyToken_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1,
+ mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2, mFenceSignaled)),
+ /*updateApplyToken*/ false);
+}
+
+TEST_F(TransactionApplicationTest, Flush_RemovesUnsignaledFromTheQueue_LatchUnsignaled_Always) {
+ SurfaceFlinger::enableLatchUnsignaledConfig = LatchUnsignaledConfig::Always;
+ Flush_removesUnsignaledFromTheQueue(createComposerStateVector(
+ createComposerState(/*layerId*/ 1,
+ mFenceUnsignaled)),
+ createComposerStateVector(
+ createComposerState(/*layerId*/ 2,
+ mFenceUnsignaled)));
+}
+
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
index bd6a780..deeb785 100644
--- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp
@@ -57,7 +57,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
index cebd451..6e00748 100644
--- a/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionProtoParserTest.cpp
@@ -69,16 +69,16 @@
t1.displays.add(display);
}
- std::function<int32_t(const sp<IBinder>&)> getLayerIdFn = [&](const sp<IBinder>& handle) {
+ TransactionProtoParser::LayerHandleToIdFn getLayerIdFn = [&](const sp<IBinder>& handle) {
return (handle == layerHandle) ? 42 : -1;
};
- std::function<int32_t(const sp<IBinder>&)> getDisplayIdFn = [&](const sp<IBinder>& handle) {
+ TransactionProtoParser::DisplayHandleToIdFn getDisplayIdFn = [&](const sp<IBinder>& handle) {
return (handle == displayHandle) ? 43 : -1;
};
- std::function<sp<IBinder>(int32_t)> getLayerHandleFn = [&](int32_t id) {
+ TransactionProtoParser::LayerIdToHandleFn getLayerHandleFn = [&](int32_t id) {
return (id == 42) ? layerHandle : nullptr;
};
- std::function<sp<IBinder>(int32_t)> getDisplayHandleFn = [&](int32_t id) {
+ TransactionProtoParser::DisplayIdToHandleFn getDisplayHandleFn = [&](int32_t id) {
return (id == 43) ? displayHandle : nullptr;
};
diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
index bf69704..704340d 100644
--- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp
@@ -57,7 +57,7 @@
sp<BufferStateLayer> createBufferStateLayer() {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 0,
LayerMetadata());
return new BufferStateLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
new file mode 100644
index 0000000..43b09fd
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TransactionTracingTest.cpp
@@ -0,0 +1,363 @@
+/*
+ * 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);
+ proto::TransactionTraceEntry entry;
+ entry.ParseFromString(mTracing->mBuffer->front());
+ return entry;
+ }
+
+ 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(), mChildLayerId, "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;
+ childState.state.what = layer_state_t::eLayerChanged;
+ childState.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;
+ int mChildLayerId = 2;
+ 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_EQ(proto.entry(0).transactions(0).layer_changes().size(), 2);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).layer_id(), mParentLayerId);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(0).z(), 42);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).layer_id(), mChildLayerId);
+ EXPECT_EQ(proto.entry(0).transactions(0).layer_changes(1).z(), 43);
+}
+
+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);
+}
+
+class TransactionTracingMirrorLayerTest : 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(), mLayerId, "Test Layer",
+ 123 /* flags */, -1 /* parentId */);
+ const sp<IBinder> fakeMirrorLayerHandle = new BBinder();
+ mTracing->onMirrorLayerAdded(fakeMirrorLayerHandle->localBinder(), mMirrorLayerId, "Mirror",
+ mLayerId);
+
+ // 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 mirrorState;
+ mirrorState.state.surface = fakeMirrorLayerHandle;
+ mirrorState.state.what = layer_state_t::eLayerChanged;
+ mirrorState.state.z = 43;
+ transaction.states.add(mirrorState);
+ mTracing->addQueuedTransaction(transaction);
+
+ std::vector<TransactionState> transactions;
+ transactions.emplace_back(transaction);
+ mTracing->addCommittedTransactions(transactions, ++mVsyncId);
+ flush(mVsyncId);
+ }
+ }
+
+ void TearDown() override {
+ mTracing->disable();
+ verifyDisabledTracingState();
+ TransactionTracingTest::TearDown();
+ }
+
+ int mLayerId = 5;
+ int mMirrorLayerId = 55;
+ 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(TransactionTracingMirrorLayerTest, canAddMirrorLayers) {
+ proto::TransactionTraceFile proto = writeToProto();
+ // We don't have any starting states since no layer was removed from.
+ EXPECT_EQ(proto.entry().size(), 2);
+ EXPECT_EQ(proto.entry(0).transactions().size(), 0);
+ EXPECT_EQ(proto.entry(0).added_layers().size(), 0);
+
+ // Verify the mirror layer was added
+ EXPECT_EQ(proto.entry(1).transactions().size(), 1);
+ EXPECT_EQ(proto.entry(1).added_layers().size(), 2);
+ EXPECT_EQ(proto.entry(1).added_layers(1).layer_id(), mMirrorLayerId);
+ EXPECT_EQ(proto.entry(1).transactions(0).layer_changes().size(), 2);
+ EXPECT_EQ(proto.entry(1).transactions(0).layer_changes(1).z(), 43);
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
index e4f7469..15fea9c 100644
--- a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -27,7 +27,6 @@
#include "TunnelModeEnabledReporter.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockEventThread.h"
-#include "mock/MockMessageQueue.h"
namespace android {
@@ -69,12 +68,12 @@
TestableSurfaceFlinger mFlinger;
Hwc2::mock::Composer* mComposer = nullptr;
- sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
- new TestableTunnelModeEnabledListener();
- sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
- new TunnelModeEnabledReporter();
- mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
+ sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
+ sp<TestableTunnelModeEnabledListener>::make();
+
+ sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
+ sp<TunnelModeEnabledReporter>::make();
};
TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
@@ -82,7 +81,6 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- mFlinger.mutableEventQueue().reset(mMessageQueue);
setupScheduler();
mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
mFlinger.flinger()->mTunnelModeEnabledReporter = mTunnelModeEnabledReporter;
@@ -100,8 +98,7 @@
sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
LayerMetadata metadata = {}) {
sp<Client> client;
- LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
- LAYER_FLAGS, metadata);
+ LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", LAYER_FLAGS, metadata);
return new BufferStateLayer(args);
}
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 1ba3c0f..a1aacc0 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -48,6 +48,7 @@
Composer();
~Composer() override;
+ MOCK_METHOD(bool, isSupported, (OptionalFeature), (const, override));
MOCK_METHOD0(getCapabilities, std::vector<IComposer::Capability>());
MOCK_METHOD0(dumpDebugInfo, std::string());
MOCK_METHOD1(registerCallback, void(const sp<IComposerCallback>&));
@@ -61,7 +62,8 @@
MOCK_METHOD2(destroyLayer, Error(Display, Layer));
MOCK_METHOD2(getActiveConfig, Error(Display, Config*));
MOCK_METHOD3(getChangedCompositionTypes,
- Error(Display, std::vector<Layer>*, std::vector<IComposerClient::Composition>*));
+ Error(Display, std::vector<Layer>*,
+ std::vector<aidl::android::hardware::graphics::composer3::Composition>*));
MOCK_METHOD2(getColorModes, Error(Display, std::vector<ColorMode>*));
MOCK_METHOD4(getDisplayAttribute,
Error(Display, Config config, IComposerClient::Attribute, int32_t*));
@@ -82,20 +84,22 @@
Error(Display, uint32_t, const sp<GraphicBuffer>&, int, Dataspace,
const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setColorMode, Error(Display, ColorMode, RenderIntent));
- MOCK_METHOD3(setColorTransform, Error(Display, const float*, ColorTransform));
+ MOCK_METHOD2(setColorTransform, Error(Display, const float*));
MOCK_METHOD3(setOutputBuffer, Error(Display, const native_handle_t*, int));
MOCK_METHOD2(setPowerMode, Error(Display, IComposerClient::PowerMode));
MOCK_METHOD2(setVsyncEnabled, Error(Display, IComposerClient::Vsync));
MOCK_METHOD1(setClientTargetSlotCount, Error(Display));
- MOCK_METHOD3(validateDisplay, Error(Display, uint32_t*, uint32_t*));
- MOCK_METHOD5(presentOrValidateDisplay, Error(Display, uint32_t*, uint32_t*, int*, uint32_t*));
+ MOCK_METHOD4(validateDisplay, Error(Display, nsecs_t, uint32_t*, uint32_t*));
+ MOCK_METHOD6(presentOrValidateDisplay,
+ Error(Display, nsecs_t, uint32_t*, uint32_t*, int*, uint32_t*));
MOCK_METHOD4(setCursorPosition, Error(Display, Layer, int32_t, int32_t));
MOCK_METHOD5(setLayerBuffer, Error(Display, Layer, uint32_t, const sp<GraphicBuffer>&, int));
MOCK_METHOD3(setLayerSurfaceDamage,
Error(Display, Layer, const std::vector<IComposerClient::Rect>&));
MOCK_METHOD3(setLayerBlendMode, Error(Display, Layer, IComposerClient::BlendMode));
MOCK_METHOD3(setLayerColor, Error(Display, Layer, const IComposerClient::Color&));
- MOCK_METHOD3(setLayerCompositionType, Error(Display, Layer, IComposerClient::Composition));
+ MOCK_METHOD3(setLayerCompositionType,
+ Error(Display, Layer, aidl::android::hardware::graphics::composer3::Composition));
MOCK_METHOD3(setLayerDataspace, Error(Display, Layer, Dataspace));
MOCK_METHOD3(setLayerPerFrameMetadata,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadata>&));
@@ -117,7 +121,6 @@
MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
- MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
MOCK_METHOD2(getDisplayConnectionType,
V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
@@ -136,7 +139,9 @@
const std::vector<uint8_t>&));
MOCK_METHOD1(getLayerGenericMetadataKeys,
V2_4::Error(std::vector<IComposerClient::LayerGenericMetadataKey>*));
- MOCK_METHOD2(getClientTargetProperty, Error(Display, IComposerClient::ClientTargetProperty*));
+ MOCK_METHOD3(getClientTargetProperty,
+ Error(Display, IComposerClient::ClientTargetProperty*, float*));
+ MOCK_METHOD3(setLayerWhitePointNits, Error(Display, Layer, float));
};
} // namespace Hwc2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
index fe1544e..b43bc0e 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockHWC2.h
@@ -38,7 +38,9 @@
MOCK_METHOD((base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>), createLayer, (),
(override));
MOCK_METHOD(hal::Error, getChangedCompositionTypes,
- ((std::unordered_map<Layer *, hal::Composition> *)), (override));
+ ((std::unordered_map<Layer *,
+ aidl::android::hardware::graphics::composer3::Composition> *)),
+ (override));
MOCK_METHOD(hal::Error, getColorModes, (std::vector<hal::ColorMode> *), (const, override));
MOCK_METHOD(int32_t, getSupportedPerFrameMetadata, (), (const, override));
MOCK_METHOD(hal::Error, getRenderIntents, (hal::ColorMode, std::vector<hal::RenderIntent> *),
@@ -66,16 +68,16 @@
const android::sp<android::Fence> &, hal::Dataspace),
(override));
MOCK_METHOD(hal::Error, setColorMode, (hal::ColorMode, hal::RenderIntent), (override));
- MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &, hal::ColorTransform),
- (override));
+ MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
MOCK_METHOD(hal::Error, setOutputBuffer,
(const android::sp<android::GraphicBuffer> &, const android::sp<android::Fence> &),
(override));
MOCK_METHOD(hal::Error, setPowerMode, (hal::PowerMode), (override));
MOCK_METHOD(hal::Error, setVsyncEnabled, (hal::Vsync), (override));
- MOCK_METHOD(hal::Error, validate, (uint32_t *, uint32_t *), (override));
+ MOCK_METHOD(hal::Error, validate, (nsecs_t, uint32_t *, uint32_t *), (override));
MOCK_METHOD(hal::Error, presentOrValidate,
- (uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *), (override));
+ (nsecs_t, uint32_t *, uint32_t *, android::sp<android::Fence> *, uint32_t *),
+ (override));
MOCK_METHOD(std::future<hal::Error>, setDisplayBrightness, (float), (override));
MOCK_METHOD(hal::Error, setActiveConfigWithConstraints,
(hal::HWConfigId, const hal::VsyncPeriodChangeConstraints &,
@@ -85,7 +87,8 @@
MOCK_METHOD(hal::Error, getSupportedContentTypes, (std::vector<hal::ContentType> *),
(const, override));
MOCK_METHOD(hal::Error, setContentType, (hal::ContentType), (override));
- MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *), (override));
+ MOCK_METHOD(hal::Error, getClientTargetProperty, (hal::ClientTargetProperty *, float *),
+ (override));
};
class Layer : public HWC2::Layer {
@@ -102,7 +105,8 @@
MOCK_METHOD(hal::Error, setSurfaceDamage, (const android::Region &), (override));
MOCK_METHOD(hal::Error, setBlendMode, (hal::BlendMode), (override));
MOCK_METHOD(hal::Error, setColor, (hal::Color), (override));
- MOCK_METHOD(hal::Error, setCompositionType, (hal::Composition), (override));
+ MOCK_METHOD(hal::Error, setCompositionType,
+ (aidl::android::hardware::graphics::composer3::Composition), (override));
MOCK_METHOD(hal::Error, setDataspace, (android::ui::Dataspace), (override));
MOCK_METHOD(hal::Error, setPerFrameMetadata, (const int32_t, const android::HdrMetadata &),
(override));
@@ -116,6 +120,7 @@
MOCK_METHOD(hal::Error, setColorTransform, (const android::mat4 &), (override));
MOCK_METHOD(hal::Error, setLayerGenericMetadata,
(const std::string &, bool, const std::vector<uint8_t> &), (override));
+ MOCK_METHOD(hal::Error, setWhitePointNits, (float whitePointNits), (override));
};
} // namespace android::HWC2::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
index ba2e4db..8b48e1c 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h
@@ -25,7 +25,7 @@
class MockLayer : public Layer {
public:
MockLayer(SurfaceFlinger* flinger, std::string name)
- : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 800, 600, 0, {})) {}
+ : Layer(LayerCreationArgs(flinger, nullptr, std::move(name), 0, {})) {}
explicit MockLayer(SurfaceFlinger* flinger) : MockLayer(flinger, "TestLayer") {}
MOCK_CONST_METHOD0(getType, const char*());
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
deleted file mode 100644
index 5fb06fd..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "mock/MockMessageQueue.h"
-
-namespace android::mock {
-
-MessageQueue::MessageQueue() {
- ON_CALL(*this, postMessage).WillByDefault([](sp<MessageHandler>&& handler) {
- // Execute task to prevent broken promise exception on destruction.
- handler->handleMessage(Message());
- });
-}
-
-MessageQueue::~MessageQueue() = default;
-
-} // namespace android::mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h b/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
deleted file mode 100644
index d684337..0000000
--- a/services/surfaceflinger/tests/unittests/mock/MockMessageQueue.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <gmock/gmock.h>
-
-#include "FrameTimeline.h"
-#include "Scheduler/EventThread.h"
-#include "Scheduler/MessageQueue.h"
-
-namespace android::mock {
-
-class MessageQueue : public android::MessageQueue {
-public:
- MessageQueue();
- ~MessageQueue() override;
-
- MOCK_METHOD1(setInjector, void(sp<EventThreadConnection>));
- MOCK_METHOD0(waitMessage, void());
- MOCK_METHOD1(postMessage, void(sp<MessageHandler>&&));
- MOCK_METHOD3(initVsync,
- void(scheduler::VSyncDispatch&, frametimeline::TokenManager&,
- std::chrono::nanoseconds));
- MOCK_METHOD1(setDuration, void(std::chrono::nanoseconds workDuration));
-
- MOCK_METHOD(void, scheduleCommit, (), (override));
- MOCK_METHOD(void, scheduleComposite, (), (override));
-
- MOCK_METHOD(std::optional<Clock::time_point>, getScheduledFrameTime, (), (const, override));
-};
-
-} // namespace android::mock
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/services/surfaceflinger/tests/utils/ScreenshotUtils.h b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
index ddaa5a1..cae7684 100644
--- a/services/surfaceflinger/tests/utils/ScreenshotUtils.h
+++ b/services/surfaceflinger/tests/utils/ScreenshotUtils.h
@@ -175,6 +175,11 @@
void expectChildColor(uint32_t x, uint32_t y) { checkPixel(x, y, 200, 200, 200); }
+ void expectSize(uint32_t width, uint32_t height) {
+ EXPECT_EQ(width, mOutBuffer->getWidth());
+ EXPECT_EQ(height, mOutBuffer->getHeight());
+ }
+
explicit ScreenCapture(const sp<GraphicBuffer>& outBuffer) : mOutBuffer(outBuffer) {
if (mOutBuffer) {
mOutBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, reinterpret_cast<void**>(&mPixels));
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/debug_report.h b/vulkan/libvulkan/debug_report.h
index e5b1587..416c0bc 100644
--- a/vulkan/libvulkan/debug_report.h
+++ b/vulkan/libvulkan/debug_report.h
@@ -78,8 +78,7 @@
VkDebugReportCallbackEXT driver_handle;
};
- // TODO(b/143295577): use std::shared_mutex when available in libc++
- mutable std::shared_timed_mutex rwmutex_;
+ mutable std::shared_mutex rwmutex_;
Node head_;
};
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index e89a49b..4a6b4f1 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -23,6 +23,8 @@
#include <sync/sync.h>
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
+#include <ui/DebugUtils.h>
+#include <ui/PixelFormat.h>
#include <utils/StrongPointer.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -462,21 +464,24 @@
*count = num_copied;
}
-android_pixel_format GetNativePixelFormat(VkFormat format) {
- android_pixel_format native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+android::PixelFormat GetNativePixelFormat(VkFormat format) {
+ android::PixelFormat native_format = android::PIXEL_FORMAT_RGBA_8888;
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SRGB:
- native_format = HAL_PIXEL_FORMAT_RGBA_8888;
+ native_format = android::PIXEL_FORMAT_RGBA_8888;
break;
case VK_FORMAT_R5G6B5_UNORM_PACK16:
- native_format = HAL_PIXEL_FORMAT_RGB_565;
+ native_format = android::PIXEL_FORMAT_RGB_565;
break;
case VK_FORMAT_R16G16B16A16_SFLOAT:
- native_format = HAL_PIXEL_FORMAT_RGBA_FP16;
+ native_format = android::PIXEL_FORMAT_RGBA_FP16;
break;
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
- native_format = HAL_PIXEL_FORMAT_RGBA_1010102;
+ native_format = android::PIXEL_FORMAT_RGBA_1010102;
+ break;
+ case VK_FORMAT_R8_UNORM:
+ native_format = android::PIXEL_FORMAT_R_8;
break;
default:
ALOGV("unsupported swapchain format %d", format);
@@ -537,30 +542,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 +656,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 +701,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 +717,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{
@@ -778,6 +763,13 @@
}
}
+ desc.format = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R8_UNORM,
+ VK_COLOR_SPACE_PASS_THROUGH_EXT});
+ }
+
VkResult result = VK_SUCCESS;
if (formats) {
uint32_t transfer_count = all_formats.size();
@@ -873,13 +865,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 +887,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);
@@ -1049,7 +1046,7 @@
if (!allocator)
allocator = &GetData(device).allocator;
- android_pixel_format native_pixel_format =
+ android::PixelFormat native_pixel_format =
GetNativePixelFormat(create_info->imageFormat);
android_dataspace native_dataspace =
GetNativeDataspace(create_info->imageColorSpace);
@@ -1146,8 +1143,8 @@
err = native_window_set_buffers_format(window, native_pixel_format);
if (err != android::OK) {
- ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
- native_pixel_format, strerror(-err), err);
+ ALOGE("native_window_set_buffers_format(%s) failed: %s (%d)",
+ decodePixelFormat(native_pixel_format).c_str(), strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = native_window_set_buffers_data_space(window, native_dataspace);
@@ -1211,21 +1208,28 @@
}
}
- 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 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;
- uint32_t num_images =
- std::max(min_buffer_count, create_info->minImageCount);
-
- // 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
- // in place for that to work yet. Note we only lie to the lower layer-- we
- // don't want to give the app back a swapchain with extra images (which they
- // can't actually use!).
- err = native_window_set_buffer_count(window, std::max(2u, num_images));
+ // Lower layer insists that we have at least min_undequeued_buffers + 1
+ // buffers. This is wasteful and we'd like to relax it in the shared case,
+ // but not all the pieces are in place for that to work yet. Note we only
+ // lie to the lower layer--we don't want to give the app back a swapchain
+ // with extra images (which they can't actually use!).
+ uint32_t min_buffer_count = min_undequeued_buffers + 1;
+ err = native_window_set_buffer_count(
+ window, std::max(min_buffer_count, num_images));
if (err != android::OK) {
ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
strerror(-err), err);